精简P2: 剥离 xray-core API 客户端: StatsService 改 protowire 线缆级实现(QueryStats/GetUsersStats 字段号按核心 proto 复刻, agent 流量上报与本地流量任务不受影响); Handler/Routing 变更类 RPC 无法脱离核心类型化配置构建, 统一降级 ErrHotApplyUnavailable(调用方既有兜底: 热更新->重启核心, balancer 视图报未运行, 节点桥探测放弃, IP 限连临时断开仅记日志); ValidateOutboundConfig 改纯 JSON 实现, 精确复刻核心 v26.7.11 未加密 vless/trojan 公网禁令(含私有 IP/域名匹配表与原文报错, 且同核心一样只查扁平 address 形式); 删除 buildUserAccount 及 SS/WG/e2e 旧测试, 流量增量测试改线缆级假服务器并新增 stock protobuf 服务器互操作测试
This commit is contained in:
@@ -5,41 +5,78 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
statsService "github.com/xtls/xray-core/app/stats/command"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/encoding/protowire"
|
||||
)
|
||||
|
||||
// fakeStatsServer serves a scripted sequence of QueryStats responses, one per
|
||||
// call, so the delta bookkeeping in GetTraffic can be driven exactly.
|
||||
// call, so the delta bookkeeping in GetTraffic can be driven exactly. It
|
||||
// speaks the stats service's wire format directly: this build has no typed
|
||||
// protobuf client, so the fake encodes responses with protowire and the
|
||||
// server runs with the client's raw codec.
|
||||
type fakeStatsServer struct {
|
||||
statsService.UnimplementedStatsServiceServer
|
||||
rounds [][]*statsService.Stat
|
||||
rounds [][]statEntry
|
||||
calls int
|
||||
}
|
||||
|
||||
func (f *fakeStatsServer) QueryStats(context.Context, *statsService.QueryStatsRequest) (*statsService.QueryStatsResponse, error) {
|
||||
func (f *fakeStatsServer) queryStats() rawBytes {
|
||||
round := f.calls
|
||||
f.calls++
|
||||
if round >= len(f.rounds) {
|
||||
round = len(f.rounds) - 1
|
||||
}
|
||||
return &statsService.QueryStatsResponse{Stat: f.rounds[round]}, nil
|
||||
return encodeQueryStatsResponse(f.rounds[round])
|
||||
}
|
||||
|
||||
func stat(name string, value int64) *statsService.Stat {
|
||||
return &statsService.Stat{Name: name, Value: value}
|
||||
// encodeQueryStatsResponse builds a QueryStatsResponse message
|
||||
// (stat = 1, repeated Stat{name = 1, value = 2}).
|
||||
func encodeQueryStatsResponse(stats []statEntry) rawBytes {
|
||||
var out []byte
|
||||
for _, st := range stats {
|
||||
var statMsg []byte
|
||||
statMsg = protowire.AppendTag(statMsg, 1, protowire.BytesType)
|
||||
statMsg = protowire.AppendString(statMsg, st.Name)
|
||||
statMsg = protowire.AppendTag(statMsg, 2, protowire.VarintType)
|
||||
statMsg = protowire.AppendVarint(statMsg, uint64(st.Value))
|
||||
out = protowire.AppendTag(out, 1, protowire.BytesType)
|
||||
out = protowire.AppendBytes(out, statMsg)
|
||||
}
|
||||
return rawBytes(out)
|
||||
|
||||
}
|
||||
|
||||
func stat(name string, value int64) statEntry {
|
||||
return statEntry{Name: name, Value: value}
|
||||
}
|
||||
|
||||
type statsServiceIface interface{}
|
||||
|
||||
// startFakeStats runs the scripted stats service and returns an XrayAPI wired
|
||||
// to it.
|
||||
func startFakeStats(t *testing.T, rounds [][]*statsService.Stat) *XrayAPI {
|
||||
func startFakeStats(t *testing.T, rounds [][]statEntry) *XrayAPI {
|
||||
t.Helper()
|
||||
lis, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
srv := grpc.NewServer()
|
||||
statsService.RegisterStatsServiceServer(srv, &fakeStatsServer{rounds: rounds})
|
||||
fake := &fakeStatsServer{rounds: rounds}
|
||||
srv := grpc.NewServer(grpc.ForceServerCodec(rawCodec{}))
|
||||
srv.RegisterService(&grpc.ServiceDesc{
|
||||
ServiceName: "xray.app.stats.command.StatsService",
|
||||
HandlerType: (*statsServiceIface)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "QueryStats",
|
||||
Handler: func(_ any, _ context.Context, dec func(any) error, _ grpc.UnaryServerInterceptor) (any, error) {
|
||||
var req rawBytes
|
||||
if err := dec(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fake.queryStats(), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}, fake)
|
||||
go func() { _ = srv.Serve(lis) }()
|
||||
t.Cleanup(srv.Stop)
|
||||
|
||||
@@ -64,7 +101,7 @@ func clientTrafficByEmail(t *testing.T, traffics []*ClientTraffic) map[string]*C
|
||||
// the panel may attach to counters that already hold traffic it cannot
|
||||
// attribute, so the first poll of a client only records baselines.
|
||||
func TestGetTrafficFirstPollIsBaselineOnly(t *testing.T) {
|
||||
api := startFakeStats(t, [][]*statsService.Stat{
|
||||
api := startFakeStats(t, [][]statEntry{
|
||||
{stat("user>>>alice>>>traffic>>>uplink", 5000)},
|
||||
})
|
||||
|
||||
@@ -84,7 +121,7 @@ func TestGetTrafficFirstPollIsBaselineOnly(t *testing.T) {
|
||||
// traffic being dropped: xray creates a counter on first use, so a name that
|
||||
// appears after the baseline poll starts at zero and every byte in it is new.
|
||||
func TestGetTrafficCountsNewStatFromZero(t *testing.T) {
|
||||
api := startFakeStats(t, [][]*statsService.Stat{
|
||||
api := startFakeStats(t, [][]statEntry{
|
||||
{stat("user>>>alice>>>traffic>>>uplink", 100)},
|
||||
{
|
||||
stat("user>>>alice>>>traffic>>>uplink", 180),
|
||||
@@ -122,7 +159,7 @@ func TestGetTrafficCountsNewStatFromZero(t *testing.T) {
|
||||
// start over at zero, so a value below the recorded baseline is all new
|
||||
// traffic rather than something to drop.
|
||||
func TestGetTrafficCountsAfterCounterReset(t *testing.T) {
|
||||
api := startFakeStats(t, [][]*statsService.Stat{
|
||||
api := startFakeStats(t, [][]statEntry{
|
||||
{stat("user>>>alice>>>traffic>>>uplink", 100)},
|
||||
{stat("user>>>alice>>>traffic>>>uplink", 900)},
|
||||
{stat("user>>>alice>>>traffic>>>uplink", 250)},
|
||||
@@ -152,7 +189,7 @@ func TestGetTrafficCountsAfterCounterReset(t *testing.T) {
|
||||
// inbound is the panel's own gRPC channel and is never a user-facing inbound,
|
||||
// and baselines for vanished stats are dropped once they outgrow the live set.
|
||||
func TestGetTrafficSkipsAPIInboundAndPrunes(t *testing.T) {
|
||||
api := startFakeStats(t, [][]*statsService.Stat{
|
||||
api := startFakeStats(t, [][]statEntry{
|
||||
{
|
||||
stat("inbound>>>api>>>traffic>>>uplink", 10),
|
||||
stat("inbound>>>in-443>>>traffic>>>uplink", 10),
|
||||
|
||||
Reference in New Issue
Block a user