mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-07 06:18:13 +03:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
002433cc46 | ||
|
|
34ebaed38f | ||
|
|
5a2afdefdd | ||
|
|
076954fbe8 | ||
|
|
6e3322d219 | ||
|
|
1d8eb81d70 | ||
|
|
e78d8ef184 | ||
|
|
6ce924ad56 | ||
|
|
035d438979 |
@@ -92,7 +92,7 @@ jobs:
|
|||||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ jobs:
|
|||||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v7
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v7
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ func (b *Balancer) SelectOutbounds() ([]string, error) {
|
|||||||
|
|
||||||
// GetPrincipleTarget implements routing.BalancerPrincipleTarget
|
// GetPrincipleTarget implements routing.BalancerPrincipleTarget
|
||||||
func (r *Router) GetPrincipleTarget(tag string) ([]string, error) {
|
func (r *Router) GetPrincipleTarget(tag string) ([]string, error) {
|
||||||
if b, ok := r.balancers[tag]; ok {
|
if b, ok := (*r.balancers.Load())[tag]; ok {
|
||||||
if s, ok := b.strategy.(BalancingPrincipleTarget); ok {
|
if s, ok := b.strategy.(BalancingPrincipleTarget); ok {
|
||||||
candidates, err := b.SelectOutbounds()
|
candidates, err := b.SelectOutbounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -151,7 +151,7 @@ func (r *Router) GetPrincipleTarget(tag string) ([]string, error) {
|
|||||||
|
|
||||||
// SetOverrideTarget implements routing.BalancerOverrider
|
// SetOverrideTarget implements routing.BalancerOverrider
|
||||||
func (r *Router) SetOverrideTarget(tag, target string) error {
|
func (r *Router) SetOverrideTarget(tag, target string) error {
|
||||||
if b, ok := r.balancers[tag]; ok {
|
if b, ok := (*r.balancers.Load())[tag]; ok {
|
||||||
b.override.Put(target)
|
b.override.Put(target)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ func (r *Router) SetOverrideTarget(tag, target string) error {
|
|||||||
|
|
||||||
// GetOverrideTarget implements routing.BalancerOverrider
|
// GetOverrideTarget implements routing.BalancerOverrider
|
||||||
func (r *Router) GetOverrideTarget(tag string) (string, error) {
|
func (r *Router) GetOverrideTarget(tag string) (string, error) {
|
||||||
if b, ok := r.balancers[tag]; ok {
|
if b, ok := (*r.balancers.Load())[tag]; ok {
|
||||||
return b.override.Get(), nil
|
return b.override.Get(), nil
|
||||||
}
|
}
|
||||||
return "", errors.New("cannot find tag")
|
return "", errors.New("cannot find tag")
|
||||||
|
|||||||
@@ -2,25 +2,8 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
sync "sync"
|
sync "sync"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Router) OverrideBalancer(balancer string, target string) error {
|
|
||||||
var b *Balancer
|
|
||||||
for tag, bl := range r.balancers {
|
|
||||||
if tag == balancer {
|
|
||||||
b = bl
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b == nil {
|
|
||||||
return errors.New("balancer '", balancer, "' not found")
|
|
||||||
}
|
|
||||||
b.override.Put(target)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type overrideSettings struct {
|
type overrideSettings struct {
|
||||||
target string
|
target string
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-114
@@ -2,7 +2,9 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"maps"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -17,8 +19,8 @@ import (
|
|||||||
// Router is an implementation of routing.Router.
|
// Router is an implementation of routing.Router.
|
||||||
type Router struct {
|
type Router struct {
|
||||||
domainStrategy Config_DomainStrategy
|
domainStrategy Config_DomainStrategy
|
||||||
rules []*Rule
|
rules atomic.Pointer[[]*Rule]
|
||||||
balancers map[string]*Balancer
|
balancers atomic.Pointer[map[string]*Balancer]
|
||||||
dns dns.Client
|
dns dns.Client
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -43,52 +45,9 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
|
|||||||
r.ohm = ohm
|
r.ohm = ohm
|
||||||
r.dispatcher = dispatcher
|
r.dispatcher = dispatcher
|
||||||
|
|
||||||
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
|
r.rules.Store(new([]*Rule))
|
||||||
for _, rule := range config.BalancingRule {
|
r.balancers.Store(&map[string]*Balancer{})
|
||||||
balancer, err := rule.Build(ohm, dispatcher)
|
return r.ReloadRules(config, false)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
balancer.InjectContext(ctx)
|
|
||||||
r.balancers[rule.Tag] = balancer
|
|
||||||
}
|
|
||||||
|
|
||||||
r.rules = make([]*Rule, 0, len(config.Rule))
|
|
||||||
for _, rule := range config.Rule {
|
|
||||||
cond, err := rule.BuildCondition()
|
|
||||||
if err != nil {
|
|
||||||
r.closeWebhooks()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rr := &Rule{
|
|
||||||
Condition: cond,
|
|
||||||
Tag: rule.GetTag(),
|
|
||||||
RuleTag: rule.GetRuleTag(),
|
|
||||||
}
|
|
||||||
if wh := rule.GetWebhook(); wh != nil {
|
|
||||||
notifier, err := NewWebhookNotifier(wh)
|
|
||||||
if err != nil {
|
|
||||||
r.closeWebhooks()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rr.Webhook = notifier
|
|
||||||
}
|
|
||||||
btag := rule.GetBalancingTag()
|
|
||||||
if len(btag) > 0 {
|
|
||||||
brule, found := r.balancers[btag]
|
|
||||||
if !found {
|
|
||||||
if rr.Webhook != nil {
|
|
||||||
rr.Webhook.Close()
|
|
||||||
}
|
|
||||||
r.closeWebhooks()
|
|
||||||
return errors.New("balancer ", btag, " not found")
|
|
||||||
}
|
|
||||||
rr.Balancer = brule
|
|
||||||
}
|
|
||||||
r.rules = append(r.rules, rr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PickRoute implements routing.Router.
|
// PickRoute implements routing.Router.
|
||||||
@@ -124,18 +83,22 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
|
|||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
if !shouldAppend {
|
oldRules := *r.rules.Load()
|
||||||
for _, rule := range r.rules {
|
oldBalancers := *r.balancers.Load()
|
||||||
if rule.Webhook != nil {
|
|
||||||
rule.Webhook.Close()
|
var newRules []*Rule
|
||||||
}
|
newBalancers := make(map[string]*Balancer)
|
||||||
|
existTags := make(map[string]bool, len(oldRules)+len(config.Rule))
|
||||||
|
if shouldAppend {
|
||||||
|
newRules = append(newRules, oldRules...)
|
||||||
|
maps.Copy(newBalancers, oldBalancers)
|
||||||
|
for _, rule := range oldRules {
|
||||||
|
existTags[rule.RuleTag] = true
|
||||||
}
|
}
|
||||||
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
|
|
||||||
r.rules = make([]*Rule, 0, len(config.Rule))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range config.BalancingRule {
|
for _, rule := range config.BalancingRule {
|
||||||
_, found := r.balancers[rule.Tag]
|
if _, found := newBalancers[rule.Tag]; found {
|
||||||
if found {
|
|
||||||
return errors.New("duplicate balancer tag")
|
return errors.New("duplicate balancer tag")
|
||||||
}
|
}
|
||||||
balancer, err := rule.Build(r.ohm, r.dispatcher)
|
balancer, err := rule.Build(r.ohm, r.dispatcher)
|
||||||
@@ -143,27 +106,12 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
balancer.InjectContext(r.ctx)
|
balancer.InjectContext(r.ctx)
|
||||||
r.balancers[rule.Tag] = balancer
|
newBalancers[rule.Tag] = balancer
|
||||||
}
|
|
||||||
|
|
||||||
startIdx := len(r.rules)
|
|
||||||
closeNewWebhooks := func() {
|
|
||||||
for i := startIdx; i < len(r.rules); i++ {
|
|
||||||
if r.rules[i].Webhook != nil {
|
|
||||||
r.rules[i].Webhook.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r.rules = r.rules[:startIdx]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range config.Rule {
|
for _, rule := range config.Rule {
|
||||||
if r.RuleExists(rule.GetRuleTag()) {
|
|
||||||
closeNewWebhooks()
|
|
||||||
return errors.New("duplicate ruleTag ", rule.GetRuleTag())
|
|
||||||
}
|
|
||||||
cond, err := rule.BuildCondition()
|
cond, err := rule.BuildCondition()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
closeNewWebhooks()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rr := &Rule{
|
rr := &Rule{
|
||||||
@@ -171,69 +119,64 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
|
|||||||
Tag: rule.GetTag(),
|
Tag: rule.GetTag(),
|
||||||
RuleTag: rule.GetRuleTag(),
|
RuleTag: rule.GetRuleTag(),
|
||||||
}
|
}
|
||||||
|
if rr.RuleTag != "" && existTags[rr.RuleTag] {
|
||||||
|
return errors.New("duplicate ruleTag ", rr.RuleTag)
|
||||||
|
}
|
||||||
|
existTags[rr.RuleTag] = true
|
||||||
if wh := rule.GetWebhook(); wh != nil {
|
if wh := rule.GetWebhook(); wh != nil {
|
||||||
notifier, err := NewWebhookNotifier(wh)
|
notifier, err := NewWebhookNotifier(wh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
closeNewWebhooks()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rr.Webhook = notifier
|
rr.Webhook = notifier
|
||||||
}
|
}
|
||||||
btag := rule.GetBalancingTag()
|
if btag := rule.GetBalancingTag(); len(btag) > 0 {
|
||||||
if len(btag) > 0 {
|
brule, found := newBalancers[btag]
|
||||||
brule, found := r.balancers[btag]
|
|
||||||
if !found {
|
if !found {
|
||||||
if rr.Webhook != nil {
|
|
||||||
rr.Webhook.Close()
|
|
||||||
}
|
|
||||||
closeNewWebhooks()
|
|
||||||
return errors.New("balancer ", btag, " not found")
|
return errors.New("balancer ", btag, " not found")
|
||||||
}
|
}
|
||||||
rr.Balancer = brule
|
rr.Balancer = brule
|
||||||
}
|
}
|
||||||
r.rules = append(r.rules, rr)
|
newRules = append(newRules, rr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.balancers.Store(&newBalancers)
|
||||||
|
r.rules.Store(&newRules)
|
||||||
|
if !shouldAppend {
|
||||||
|
closeWebhooks(oldRules)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) RuleExists(tag string) bool {
|
|
||||||
if tag != "" {
|
|
||||||
for _, rule := range r.rules {
|
|
||||||
if rule.RuleTag == tag {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveRule implements routing.Router.
|
// RemoveRule implements routing.Router.
|
||||||
func (r *Router) RemoveRule(tag string) error {
|
func (r *Router) RemoveRule(tag string) error {
|
||||||
|
if tag == "" {
|
||||||
|
return errors.New("empty tag name!")
|
||||||
|
}
|
||||||
|
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
newRules := []*Rule{}
|
oldRules := *r.rules.Load()
|
||||||
if tag != "" {
|
newRules := make([]*Rule, 0, len(oldRules))
|
||||||
for _, rule := range r.rules {
|
var removed []*Rule
|
||||||
if rule.RuleTag != tag {
|
for _, rule := range oldRules {
|
||||||
newRules = append(newRules, rule)
|
if rule.RuleTag != tag {
|
||||||
} else if rule.Webhook != nil {
|
newRules = append(newRules, rule)
|
||||||
rule.Webhook.Close()
|
} else {
|
||||||
}
|
removed = append(removed, rule)
|
||||||
}
|
}
|
||||||
r.rules = newRules
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return errors.New("empty tag name!")
|
r.rules.Store(&newRules)
|
||||||
|
closeWebhooks(removed)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListRule implements routing.Router
|
// ListRule implements routing.Router
|
||||||
func (r *Router) ListRule() []routing.Route {
|
func (r *Router) ListRule() []routing.Route {
|
||||||
r.mu.Lock()
|
rules := *r.rules.Load()
|
||||||
defer r.mu.Unlock()
|
ruleList := make([]routing.Route, 0, len(rules))
|
||||||
ruleList := make([]routing.Route, 0)
|
for _, rule := range rules {
|
||||||
for _, rule := range r.rules {
|
|
||||||
ruleList = append(ruleList, &Route{
|
ruleList = append(ruleList, &Route{
|
||||||
outboundTag: rule.Tag,
|
outboundTag: rule.Tag,
|
||||||
ruleTag: rule.RuleTag,
|
ruleTag: rule.RuleTag,
|
||||||
@@ -252,7 +195,9 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context,
|
|||||||
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range r.rules {
|
rules := *r.rules.Load()
|
||||||
|
|
||||||
|
for _, rule := range rules {
|
||||||
if rule.Apply(ctx) {
|
if rule.Apply(ctx) {
|
||||||
return rule, ctx, nil
|
return rule, ctx, nil
|
||||||
}
|
}
|
||||||
@@ -265,7 +210,7 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context,
|
|||||||
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
||||||
|
|
||||||
// Try applying rules again if we have IPs.
|
// Try applying rules again if we have IPs.
|
||||||
for _, rule := range r.rules {
|
for _, rule := range rules {
|
||||||
if rule.Apply(ctx) {
|
if rule.Apply(ctx) {
|
||||||
return rule, ctx, nil
|
return rule, ctx, nil
|
||||||
}
|
}
|
||||||
@@ -279,9 +224,9 @@ func (r *Router) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeWebhooks closes all webhook notifiers in the current rule set.
|
// closeWebhooks closes all webhook notifiers in the given rule set.
|
||||||
func (r *Router) closeWebhooks() {
|
func closeWebhooks(rules []*Rule) {
|
||||||
for _, rule := range r.rules {
|
for _, rule := range rules {
|
||||||
if rule.Webhook != nil {
|
if rule.Webhook != nil {
|
||||||
rule.Webhook.Close()
|
rule.Webhook.Close()
|
||||||
}
|
}
|
||||||
@@ -292,7 +237,7 @@ func (r *Router) closeWebhooks() {
|
|||||||
func (r *Router) Close() error {
|
func (r *Router) Close() error {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
r.closeWebhooks()
|
closeWebhooks(*r.rules.Load())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-23
@@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -40,6 +41,7 @@ type WebhookNotifier struct {
|
|||||||
deduplication uint32
|
deduplication uint32
|
||||||
client *http.Client
|
client *http.Client
|
||||||
seen sync.Map
|
seen sync.Map
|
||||||
|
lastSweep atomic.Int64
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
@@ -77,11 +79,6 @@ func NewWebhookNotifier(cfg *WebhookConfig) (*WebhookNotifier, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.deduplication > 0 {
|
|
||||||
h.wg.Add(1)
|
|
||||||
go h.cleanupLoop()
|
|
||||||
}
|
|
||||||
|
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +198,7 @@ func (h *WebhookNotifier) isDuplicate(email string) bool {
|
|||||||
}
|
}
|
||||||
ttl := time.Duration(h.deduplication) * time.Second
|
ttl := time.Duration(h.deduplication) * time.Second
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
h.maybeSweep(now, ttl)
|
||||||
if v, loaded := h.seen.LoadOrStore(email, now); loaded {
|
if v, loaded := h.seen.LoadOrStore(email, now); loaded {
|
||||||
if now.Sub(v.(time.Time)) < ttl {
|
if now.Sub(v.(time.Time)) < ttl {
|
||||||
return true
|
return true
|
||||||
@@ -210,27 +208,23 @@ func (h *WebhookNotifier) isDuplicate(email string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *WebhookNotifier) cleanupLoop() {
|
func (h *WebhookNotifier) maybeSweep(now time.Time, ttl time.Duration) {
|
||||||
defer h.wg.Done()
|
last := h.lastSweep.Load()
|
||||||
ttl := time.Duration(h.deduplication) * time.Second
|
if now.UnixNano()-last < int64(ttl) {
|
||||||
ticker := time.NewTicker(ttl)
|
return
|
||||||
defer ticker.Stop()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-h.done:
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
now := time.Now()
|
|
||||||
h.seen.Range(func(key, value any) bool {
|
|
||||||
if now.Sub(value.(time.Time)) >= ttl {
|
|
||||||
h.seen.Delete(key)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if !h.lastSweep.CompareAndSwap(last, now.UnixNano()) {
|
||||||
|
return // another goroutine did the sweep
|
||||||
|
}
|
||||||
|
h.seen.Range(func(key, value any) bool {
|
||||||
|
if now.Sub(value.(time.Time)) >= ttl {
|
||||||
|
h.seen.Delete(key)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only need to call if the Notifier is really used, otherwise GC can clean it
|
||||||
func (h *WebhookNotifier) Close() error {
|
func (h *WebhookNotifier) Close() error {
|
||||||
h.closeOnce.Do(func() {
|
h.closeOnce.Do(func() {
|
||||||
close(h.done)
|
close(h.done)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ require (
|
|||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||||
golang.zx2c4.com/wireguard/windows v1.0.1
|
golang.zx2c4.com/wireguard/windows v1.0.1
|
||||||
google.golang.org/grpc v1.82.0
|
google.golang.org/grpc v1.82.1
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||||
h12.io/socks v1.0.3
|
h12.io/socks v1.0.3
|
||||||
|
|||||||
@@ -149,8 +149,8 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
|||||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
|
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
|
||||||
google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -113,8 +113,10 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
|||||||
config.MinClientVer[i] = byte(u)
|
config.MinClientVer[i] = byte(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
errors.LogWarning(context.Background(), `REALITY: Changing "minClientVer" will increase the likelihood of your server's IP being blocked by the GFW`)
|
||||||
} else {
|
} else {
|
||||||
config.MinClientVer = []byte{26, 3, 27} // change it at your own risk: https://github.com/XTLS/Xray-core/pull/6181#issuecomment-4567373533
|
config.MinClientVer = []byte{26, 3, 27} // change it at your own risk: https://github.com/XTLS/Xray-core/commit/af7eb68028732a8ee3c0e5d6ab2b8a657bb2e770
|
||||||
|
errors.LogWarning(context.Background(), `REALITY: The default minimal client version is Xray-core v26.3.27, other clients may be refused to connect`)
|
||||||
}
|
}
|
||||||
if c.MaxClientVer != "" {
|
if c.MaxClientVer != "" {
|
||||||
config.MaxClientVer = make([]byte, 3)
|
config.MaxClientVer = make([]byte, 3)
|
||||||
@@ -159,8 +161,10 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, sn := range config.ServerNames {
|
for _, sn := range config.ServerNames {
|
||||||
if strings.Contains(sn, "apple") || strings.Contains(sn, "icloud") {
|
sn = strings.ToLower(sn)
|
||||||
errors.LogWarning(context.Background(), `REALITY: Choosing apple, icloud, etc. as the target may get your IP blocked by the GFW`)
|
if strings.HasSuffix(sn, ".ru") || strings.HasSuffix(sn, ".ir") || strings.HasSuffix(sn, ".cn") ||
|
||||||
|
strings.Contains(sn, "apple") || strings.Contains(sn, "icloud") || strings.Contains(sn, "microsoft") {
|
||||||
|
errors.LogWarning(context.Background(), `REALITY: Choosing "`, sn, `" as the target will increase the likelihood of your server's IP being blocked by the GFW`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+70
-1
@@ -1,12 +1,19 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/proxy/tun"
|
"github.com/xtls/xray-core/proxy/tun"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TunConfig struct {
|
type TunConfig struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
MTU uint32 `json:"mtu"`
|
MTU uint32 `json:"mtu"`
|
||||||
Gateway []string `json:"gateway"`
|
Gateway []string `json:"gateway"`
|
||||||
DNS []string `json:"dns"`
|
DNS []string `json:"dns"`
|
||||||
@@ -18,6 +25,7 @@ type TunConfig struct {
|
|||||||
func (v *TunConfig) Build() (proto.Message, error) {
|
func (v *TunConfig) Build() (proto.Message, error) {
|
||||||
config := &tun.Config{
|
config := &tun.Config{
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
|
Desc: v.Desc,
|
||||||
MTU: v.MTU,
|
MTU: v.MTU,
|
||||||
Gateway: v.Gateway,
|
Gateway: v.Gateway,
|
||||||
DNS: v.DNS,
|
DNS: v.DNS,
|
||||||
@@ -32,10 +40,71 @@ func (v *TunConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.Name == "" {
|
if config.Name == "" {
|
||||||
config.Name = "xray0"
|
name, err := GetAvailableTunName()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Name = name
|
||||||
|
}
|
||||||
|
if config.Desc == "" {
|
||||||
|
config.Desc = "Wintun"
|
||||||
}
|
}
|
||||||
if config.MTU == 0 {
|
if config.MTU == 0 {
|
||||||
config.MTU = 1500
|
config.MTU = 1500
|
||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tunNamePrefix = "utun"
|
||||||
|
minTunIndex = 10
|
||||||
|
maxTunIndex = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAvailableTunName() (string, error) {
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("fail to get system interface information: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
usedNames := make(map[string]struct{}, len(interfaces))
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
usedNames[iface.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex, err := randomInt(minTunIndex, maxTunIndex)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("fail to generate valid tun name: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rangeSize := maxTunIndex - minTunIndex + 1
|
||||||
|
|
||||||
|
for offset := 0; offset < rangeSize; offset++ {
|
||||||
|
index := minTunIndex + (startIndex-minTunIndex+offset)%rangeSize
|
||||||
|
name := tunNamePrefix + strconv.Itoa(index)
|
||||||
|
|
||||||
|
if _, exists := usedNames[name]; !exists {
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"no available TUN interface name in range %s%d-%s%d",
|
||||||
|
tunNamePrefix,
|
||||||
|
minTunIndex,
|
||||||
|
tunNamePrefix,
|
||||||
|
maxTunIndex,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomInt(min, max int) (int, error) {
|
||||||
|
value, err := rand.Int(
|
||||||
|
rand.Reader,
|
||||||
|
big.NewInt(int64(max-min+1)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return min + int(value.Int64()), nil
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -176,7 +176,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
|
|||||||
receiverSettings.StreamSettings = ss
|
receiverSettings.StreamSettings = ss
|
||||||
if strings.Contains(ss.SecurityType, "reality") && (receiverSettings.PortList == nil ||
|
if strings.Contains(ss.SecurityType, "reality") && (receiverSettings.PortList == nil ||
|
||||||
len(receiverSettings.PortList.Ports()) != 1 || receiverSettings.PortList.Ports()[0] != 443) {
|
len(receiverSettings.PortList.Ports()) != 1 || receiverSettings.PortList.Ports()[0] != 443) {
|
||||||
errors.LogWarning(context.Background(), `REALITY: Listening on non-443 ports may get your IP blocked by the GFW`)
|
errors.LogWarning(context.Background(), `REALITY: Listening on non-443 ports will increase the likelihood of your server's IP being blocked by the GFW`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.SniffingConfig != nil {
|
if c.SniffingConfig != nil {
|
||||||
|
|||||||
+6
-2
@@ -31,13 +31,17 @@ Here is simple Xray config snippet to enable the inbound:
|
|||||||
"port": 0,
|
"port": 0,
|
||||||
"protocol": "tun",
|
"protocol": "tun",
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "xray0",
|
"name": "utun10",
|
||||||
"MTU": 1492
|
"desc": "Wintun",
|
||||||
|
"mtu": 1500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`desc` sets the Windows Wintun adapter tunnel type and defaults to `Wintun`.
|
||||||
|
It is ignored on other platforms.
|
||||||
|
|
||||||
## SUPPORTED FEATURES
|
## SUPPORTED FEATURES
|
||||||
|
|
||||||
- IPv4 and IPv6
|
- IPv4 and IPv6
|
||||||
|
|||||||
+14
-4
@@ -7,11 +7,12 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
sync "sync"
|
sync "sync"
|
||||||
unsafe "unsafe"
|
unsafe "unsafe"
|
||||||
|
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -30,6 +31,7 @@ type Config struct {
|
|||||||
UserLevel uint32 `protobuf:"varint,5,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
UserLevel uint32 `protobuf:"varint,5,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
||||||
AutoSystemRoutingTable []string `protobuf:"bytes,6,rep,name=auto_system_routing_table,json=autoSystemRoutingTable,proto3" json:"auto_system_routing_table,omitempty"`
|
AutoSystemRoutingTable []string `protobuf:"bytes,6,rep,name=auto_system_routing_table,json=autoSystemRoutingTable,proto3" json:"auto_system_routing_table,omitempty"`
|
||||||
AutoOutboundsInterface string `protobuf:"bytes,7,opt,name=auto_outbounds_interface,json=autoOutboundsInterface,proto3" json:"auto_outbounds_interface,omitempty"`
|
AutoOutboundsInterface string `protobuf:"bytes,7,opt,name=auto_outbounds_interface,json=autoOutboundsInterface,proto3" json:"auto_outbounds_interface,omitempty"`
|
||||||
|
Desc string `protobuf:"bytes,8,opt,name=desc,proto3" json:"desc,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -113,11 +115,18 @@ func (x *Config) GetAutoOutboundsInterface() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetDesc() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Desc
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_proxy_tun_config_proto protoreflect.FileDescriptor
|
var File_proxy_tun_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_proxy_tun_config_proto_rawDesc = "" +
|
const file_proxy_tun_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x16proxy/tun/config.proto\x12\x0exray.proxy.tun\"\xee\x01\n" +
|
"\x16proxy/tun/config.proto\x12\x0exray.proxy.tun\"\x82\x02\n" +
|
||||||
"\x06Config\x12\x12\n" +
|
"\x06Config\x12\x12\n" +
|
||||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" +
|
"\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" +
|
||||||
"\x03MTU\x18\x02 \x01(\rR\x03MTU\x12\x18\n" +
|
"\x03MTU\x18\x02 \x01(\rR\x03MTU\x12\x18\n" +
|
||||||
@@ -126,7 +135,8 @@ const file_proxy_tun_config_proto_rawDesc = "" +
|
|||||||
"\n" +
|
"\n" +
|
||||||
"user_level\x18\x05 \x01(\rR\tuserLevel\x129\n" +
|
"user_level\x18\x05 \x01(\rR\tuserLevel\x129\n" +
|
||||||
"\x19auto_system_routing_table\x18\x06 \x03(\tR\x16autoSystemRoutingTable\x128\n" +
|
"\x19auto_system_routing_table\x18\x06 \x03(\tR\x16autoSystemRoutingTable\x128\n" +
|
||||||
"\x18auto_outbounds_interface\x18\a \x01(\tR\x16autoOutboundsInterfaceBL\n" +
|
"\x18auto_outbounds_interface\x18\a \x01(\tR\x16autoOutboundsInterface\x12\x12\n" +
|
||||||
|
"\x04desc\x18\b \x01(\tR\x04descBL\n" +
|
||||||
"\x12com.xray.proxy.tunP\x01Z#github.com/xtls/xray-core/proxy/tun\xaa\x02\x0eXray.Proxy.Tunb\x06proto3"
|
"\x12com.xray.proxy.tunP\x01Z#github.com/xtls/xray-core/proxy/tun\xaa\x02\x0eXray.Proxy.Tunb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ message Config {
|
|||||||
uint32 user_level = 5;
|
uint32 user_level = 5;
|
||||||
repeated string auto_system_routing_table = 6;
|
repeated string auto_system_routing_table = 6;
|
||||||
string auto_outbounds_interface = 7;
|
string auto_outbounds_interface = 7;
|
||||||
|
string desc = 8;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ var _ GVisorDevice = (*WindowsTun)(nil)
|
|||||||
// interface with the same name exist, it tried to be reused.
|
// interface with the same name exist, it tried to be reused.
|
||||||
func NewTun(options *Config) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
// instantiate wintun adapter
|
// instantiate wintun adapter
|
||||||
adapter, err := open(options.Name)
|
adapter, err := open(options.Name, options.Desc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -73,12 +73,12 @@ func NewTun(options *Config) (Tun, error) {
|
|||||||
return tun, nil
|
return tun, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func open(name string) (*wintun.Adapter, error) {
|
func open(name, desc string) (*wintun.Adapter, error) {
|
||||||
// generate a deterministic GUID from the adapter name
|
// generate a deterministic GUID from the adapter name
|
||||||
id := md5.Sum([]byte(name))
|
id := md5.Sum([]byte(name))
|
||||||
guid := (*windows.GUID)(unsafe.Pointer(&id[0]))
|
guid := (*windows.GUID)(unsafe.Pointer(&id[0]))
|
||||||
// try to create adapter anew
|
// try to create adapter anew
|
||||||
adapter, err := wintun.CreateAdapter(name, "Xray", guid)
|
adapter, err := wintun.CreateAdapter(name, desc, guid)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return adapter, nil
|
return adapter, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,14 +38,12 @@ func NewHunkReadWriter(hc HunkConn, cancel context.CancelFunc) *HunkReaderWriter
|
|||||||
|
|
||||||
func NewHunkConn(hc HunkConn, cancel context.CancelFunc, trustedXForwardedFor []string) net.Conn {
|
func NewHunkConn(hc HunkConn, cancel context.CancelFunc, trustedXForwardedFor []string) net.Conn {
|
||||||
rAddr := remoteAddrFromContext(hc.Context(), trustedXForwardedFor)
|
rAddr := remoteAddrFromContext(hc.Context(), trustedXForwardedFor)
|
||||||
lAddr := localAddrFromContext(hc.Context())
|
|
||||||
wrc := NewHunkReadWriter(hc, cancel)
|
wrc := NewHunkReadWriter(hc, cancel)
|
||||||
return cnc.NewConnection(
|
return cnc.NewConnection(
|
||||||
cnc.ConnectionInput(wrc),
|
cnc.ConnectionInput(wrc),
|
||||||
cnc.ConnectionOutput(wrc),
|
cnc.ConnectionOutput(wrc),
|
||||||
cnc.ConnectionOnClose(wrc),
|
cnc.ConnectionOnClose(wrc),
|
||||||
cnc.ConnectionRemoteAddr(rAddr),
|
cnc.ConnectionRemoteAddr(rAddr),
|
||||||
cnc.ConnectionLocalAddr(lAddr),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,14 +33,12 @@ func NewMultiHunkReadWriter(hc MultiHunkConn, cancel context.CancelFunc) *MultiH
|
|||||||
|
|
||||||
func NewMultiHunkConn(hc MultiHunkConn, cancel context.CancelFunc, trustedXForwardedFor []string) net.Conn {
|
func NewMultiHunkConn(hc MultiHunkConn, cancel context.CancelFunc, trustedXForwardedFor []string) net.Conn {
|
||||||
rAddr := remoteAddrFromContext(hc.Context(), trustedXForwardedFor)
|
rAddr := remoteAddrFromContext(hc.Context(), trustedXForwardedFor)
|
||||||
lAddr := localAddrFromContext(hc.Context())
|
|
||||||
wrc := NewMultiHunkReadWriter(hc, cancel)
|
wrc := NewMultiHunkReadWriter(hc, cancel)
|
||||||
return cnc.NewConnection(
|
return cnc.NewConnection(
|
||||||
cnc.ConnectionInputMulti(wrc),
|
cnc.ConnectionInputMulti(wrc),
|
||||||
cnc.ConnectionOutputMulti(wrc),
|
cnc.ConnectionOutputMulti(wrc),
|
||||||
cnc.ConnectionOnClose(wrc),
|
cnc.ConnectionOnClose(wrc),
|
||||||
cnc.ConnectionRemoteAddr(rAddr),
|
cnc.ConnectionRemoteAddr(rAddr),
|
||||||
cnc.ConnectionLocalAddr(lAddr),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,17 +56,3 @@ func parseTrustedXForwardedFor(md metadata.MD, trusted []string, remoteAddr net.
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func localAddrFromContext(ctx context.Context) net.Addr {
|
|
||||||
var localAddr net.Addr
|
|
||||||
if pr, ok := peer.FromContext(ctx); ok {
|
|
||||||
localAddr = pr.LocalAddr
|
|
||||||
}
|
|
||||||
if localAddr == nil {
|
|
||||||
localAddr = &net.TCPAddr{
|
|
||||||
IP: []byte{0, 0, 0, 0},
|
|
||||||
Port: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return localAddr
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -373,15 +373,11 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
Reader: request.Body,
|
Reader: request.Body,
|
||||||
ResponseWriter: writer,
|
ResponseWriter: writer,
|
||||||
}
|
}
|
||||||
localAddr := h.localAddr
|
|
||||||
if la, ok := request.Context().Value(http.LocalAddrContextKey).(net.Addr); ok && la != nil {
|
|
||||||
localAddr = la
|
|
||||||
}
|
|
||||||
conn := splitConn{
|
conn := splitConn{
|
||||||
writer: httpSC,
|
writer: httpSC,
|
||||||
reader: httpSC,
|
reader: httpSC,
|
||||||
remoteAddr: remoteAddr,
|
remoteAddr: remoteAddr,
|
||||||
localAddr: localAddr,
|
localAddr: h.localAddr,
|
||||||
}
|
}
|
||||||
if sessionId != "" { // if not stream-one
|
if sessionId != "" { // if not stream-one
|
||||||
conn.reader = currentSession.uploadQueue
|
conn.reader = currentSession.uploadQueue
|
||||||
|
|||||||
Reference in New Issue
Block a user