mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-04-16 20:45:50 +00:00
1. **Fixed XPadding Placement Dropdown**: - Added the missing `cookie` and `query` options to `xPaddingPlacement` (`stream_xhttp.html`). - *Why:* Previously, users wanting `cookie` obfuscation were forced to use the `header` placement string. This caused Xray-core to blindly intercept the entire monolithic HTTP Cookie header, failing internal padding-length validations and causing the inbound to silently drop the connection. 2. **Fixed Uplink Data Placement Validation**: - Replaced the unsupported `query` option with `cookie` in `uplinkDataPlacement`. - *Why:* Xray-core's `transport_internet.go` explicitly forbids `query` as an uplink placement option. Selecting it from the UI previously sent a payload that would cause Xray-core to instantly throw an `unsupported uplink data placement: query` panic. Adding `cookie` perfectly aligns the UI with Xray-core restrictions. ### Related Issues - Resolves #3992
99 lines
1.9 KiB
Go
99 lines
1.9 KiB
Go
package stats
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
localhostIPv4 = "127.0.0.1"
|
|
localhostIPv6 = "[::1]"
|
|
)
|
|
|
|
type ipEntry struct {
|
|
refCount int
|
|
lastSeen time.Time
|
|
}
|
|
|
|
// OnlineMap is a refcount-based implementation of stats.OnlineMap.
|
|
// IPs are tracked by reference counting: AddIP increments, RemoveIP decrements.
|
|
// An IP is removed from the map when its reference count reaches zero.
|
|
type OnlineMap struct {
|
|
entries map[string]*ipEntry
|
|
access sync.Mutex
|
|
count atomic.Int64
|
|
}
|
|
|
|
// NewOnlineMap creates a new OnlineMap instance.
|
|
func NewOnlineMap() *OnlineMap {
|
|
return &OnlineMap{
|
|
entries: make(map[string]*ipEntry),
|
|
}
|
|
}
|
|
|
|
// AddIP implements stats.OnlineMap.
|
|
func (om *OnlineMap) AddIP(ip string) {
|
|
if ip == localhostIPv4 || ip == localhostIPv6 {
|
|
return
|
|
}
|
|
|
|
om.access.Lock()
|
|
defer om.access.Unlock()
|
|
|
|
if e, ok := om.entries[ip]; ok {
|
|
e.refCount++
|
|
e.lastSeen = time.Now()
|
|
} else {
|
|
om.entries[ip] = &ipEntry{
|
|
refCount: 1,
|
|
lastSeen: time.Now(),
|
|
}
|
|
om.count.Add(1)
|
|
}
|
|
}
|
|
|
|
// RemoveIP implements stats.OnlineMap.
|
|
func (om *OnlineMap) RemoveIP(ip string) {
|
|
om.access.Lock()
|
|
defer om.access.Unlock()
|
|
|
|
e, ok := om.entries[ip]
|
|
if !ok {
|
|
return
|
|
}
|
|
e.refCount--
|
|
if e.refCount <= 0 {
|
|
delete(om.entries, ip)
|
|
om.count.Add(-1)
|
|
}
|
|
}
|
|
|
|
// Count implements stats.OnlineMap.
|
|
func (om *OnlineMap) Count() int {
|
|
return int(om.count.Load())
|
|
}
|
|
|
|
// List implements stats.OnlineMap.
|
|
func (om *OnlineMap) List() []string {
|
|
om.access.Lock()
|
|
defer om.access.Unlock()
|
|
|
|
keys := make([]string, 0, len(om.entries))
|
|
for ip := range om.entries {
|
|
keys = append(keys, ip)
|
|
}
|
|
return keys
|
|
}
|
|
|
|
// IPTimeMap implements stats.OnlineMap.
|
|
func (om *OnlineMap) IPTimeMap() map[string]time.Time {
|
|
om.access.Lock()
|
|
defer om.access.Unlock()
|
|
|
|
result := make(map[string]time.Time, len(om.entries))
|
|
for ip, e := range om.entries {
|
|
result[ip] = e.lastSeen
|
|
}
|
|
return result
|
|
}
|