Update On Tue Sep 16 20:39:27 CEST 2025

This commit is contained in:
github-action[bot]
2025-09-16 20:39:28 +02:00
parent ed4041c4a3
commit 2bcb715f2e
78 changed files with 2308 additions and 690 deletions

View File

@@ -58,7 +58,7 @@ jobs:
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
- name: Revert Golang1.25 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.25' }}
if: ${{ runner.os == 'Windows' && matrix.go-version == '1.25' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)

View File

@@ -1,9 +1,8 @@
package net
import (
"context"
"io"
"net"
"runtime"
"github.com/metacubex/mihomo/common/net/deadline"
@@ -56,9 +55,37 @@ type CountFunc = network.CountFunc
var Pipe = deadline.Pipe
// Relay copies between left and right bidirectionally.
func Relay(leftConn, rightConn net.Conn) {
defer runtime.KeepAlive(leftConn)
defer runtime.KeepAlive(rightConn)
_ = bufio.CopyConn(context.TODO(), leftConn, rightConn)
func closeWrite(writer io.Closer) error {
if c, ok := common.Cast[network.WriteCloser](writer); ok {
return c.CloseWrite()
}
return writer.Close()
}
// Relay copies between left and right bidirectionally.
// like [bufio.CopyConn] but remove unneeded [context.Context] handle and the cost of [task.Group]
func Relay(leftConn, rightConn net.Conn) {
defer func() {
_ = leftConn.Close()
_ = rightConn.Close()
}()
ch := make(chan struct{})
go func() {
_, err := bufio.Copy(leftConn, rightConn)
if err == nil {
_ = closeWrite(leftConn)
} else {
_ = leftConn.Close()
}
close(ch)
}()
_, err := bufio.Copy(rightConn, leftConn)
if err == nil {
_ = closeWrite(rightConn)
} else {
_ = rightConn.Close()
}
<-ch
}

View File

@@ -2,8 +2,6 @@ package queue
import (
"sync"
"github.com/samber/lo"
)
// Queue is a simple concurrent safe queue
@@ -24,33 +22,32 @@ func (q *Queue[T]) Put(items ...T) {
}
// Pop returns the head of items.
func (q *Queue[T]) Pop() T {
func (q *Queue[T]) Pop() (head T) {
if len(q.items) == 0 {
return lo.Empty[T]()
return
}
q.lock.Lock()
head := q.items[0]
head = q.items[0]
q.items = q.items[1:]
q.lock.Unlock()
return head
}
// Last returns the last of item.
func (q *Queue[T]) Last() T {
func (q *Queue[T]) Last() (last T) {
if len(q.items) == 0 {
return lo.Empty[T]()
return
}
q.lock.RLock()
last := q.items[len(q.items)-1]
last = q.items[len(q.items)-1]
q.lock.RUnlock()
return last
}
// Copy get the copy of queue.
func (q *Queue[T]) Copy() []T {
items := []T{}
func (q *Queue[T]) Copy() (items []T) {
q.lock.RLock()
items = append(items, q.items...)
q.lock.RUnlock()

View File

@@ -0,0 +1,215 @@
package queue
import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// TestQueuePut tests the Put method of Queue
func TestQueuePut(t *testing.T) {
// Initialize a new queue
q := New[int](10)
// Test putting a single item
q.Put(1)
assert.Equal(t, int64(1), q.Len(), "Queue length should be 1 after putting one item")
// Test putting multiple items
q.Put(2, 3, 4)
assert.Equal(t, int64(4), q.Len(), "Queue length should be 4 after putting three more items")
// Test putting zero items (should not change queue)
q.Put()
assert.Equal(t, int64(4), q.Len(), "Queue length should remain unchanged when putting zero items")
}
// TestQueuePop tests the Pop method of Queue
func TestQueuePop(t *testing.T) {
// Initialize a new queue with items
q := New[int](10)
q.Put(1, 2, 3)
// Test popping items in FIFO order
item := q.Pop()
assert.Equal(t, 1, item, "First item popped should be 1")
assert.Equal(t, int64(2), q.Len(), "Queue length should be 2 after popping one item")
item = q.Pop()
assert.Equal(t, 2, item, "Second item popped should be 2")
assert.Equal(t, int64(1), q.Len(), "Queue length should be 1 after popping two items")
item = q.Pop()
assert.Equal(t, 3, item, "Third item popped should be 3")
assert.Equal(t, int64(0), q.Len(), "Queue length should be 0 after popping all items")
}
// TestQueuePopEmpty tests the Pop method on an empty queue
func TestQueuePopEmpty(t *testing.T) {
// Initialize a new empty queue
q := New[int](0)
// Test popping from an empty queue
item := q.Pop()
assert.Equal(t, 0, item, "Popping from an empty queue should return the zero value")
assert.Equal(t, int64(0), q.Len(), "Queue length should remain 0 after popping from an empty queue")
}
// TestQueueLast tests the Last method of Queue
func TestQueueLast(t *testing.T) {
// Initialize a new queue with items
q := New[int](10)
q.Put(1, 2, 3)
// Test getting the last item
item := q.Last()
assert.Equal(t, 3, item, "Last item should be 3")
assert.Equal(t, int64(3), q.Len(), "Queue length should remain unchanged after calling Last")
// Test Last on an empty queue
emptyQ := New[int](0)
emptyItem := emptyQ.Last()
assert.Equal(t, 0, emptyItem, "Last on an empty queue should return the zero value")
}
// TestQueueCopy tests the Copy method of Queue
func TestQueueCopy(t *testing.T) {
// Initialize a new queue with items
q := New[int](10)
q.Put(1, 2, 3)
// Test copying the queue
copy := q.Copy()
assert.Equal(t, 3, len(copy), "Copy should have the same number of items as the original queue")
assert.Equal(t, 1, copy[0], "First item in copy should be 1")
assert.Equal(t, 2, copy[1], "Second item in copy should be 2")
assert.Equal(t, 3, copy[2], "Third item in copy should be 3")
// Verify that modifying the copy doesn't affect the original queue
copy[0] = 99
assert.Equal(t, 1, q.Pop(), "Original queue should not be affected by modifying the copy")
}
// TestQueueLen tests the Len method of Queue
func TestQueueLen(t *testing.T) {
// Initialize a new empty queue
q := New[int](10)
assert.Equal(t, int64(0), q.Len(), "New queue should have length 0")
// Add items and check length
q.Put(1, 2)
assert.Equal(t, int64(2), q.Len(), "Queue length should be 2 after putting two items")
// Remove an item and check length
q.Pop()
assert.Equal(t, int64(1), q.Len(), "Queue length should be 1 after popping one item")
}
// TestQueueNew tests the New constructor
func TestQueueNew(t *testing.T) {
// Test creating a new queue with different hints
q1 := New[int](0)
assert.NotNil(t, q1, "New queue should not be nil")
assert.Equal(t, int64(0), q1.Len(), "New queue should have length 0")
q2 := New[int](10)
assert.NotNil(t, q2, "New queue should not be nil")
assert.Equal(t, int64(0), q2.Len(), "New queue should have length 0")
// Test with a different type
q3 := New[string](5)
assert.NotNil(t, q3, "New queue should not be nil")
assert.Equal(t, int64(0), q3.Len(), "New queue should have length 0")
}
// TestQueueConcurrency tests the concurrency safety of Queue
func TestQueueConcurrency(t *testing.T) {
// Initialize a new queue
q := New[int](100)
// Number of goroutines and operations
goroutines := 10
operations := 100
// Wait group to synchronize goroutines
wg := sync.WaitGroup{}
wg.Add(goroutines * 2) // For both producers and consumers
// Start producer goroutines
for i := 0; i < goroutines; i++ {
go func(id int) {
defer wg.Done()
for j := 0; j < operations; j++ {
q.Put(id*operations + j)
// Small sleep to increase chance of race conditions
time.Sleep(time.Microsecond)
}
}(i)
}
// Start consumer goroutines
consumed := make(chan int, goroutines*operations)
for i := 0; i < goroutines; i++ {
go func() {
defer wg.Done()
for j := 0; j < operations; j++ {
// Try to pop an item, but don't block if queue is empty
// Use a mutex to avoid race condition between Len() check and Pop()
q.lock.Lock()
if len(q.items) > 0 {
item := q.items[0]
q.items = q.items[1:]
q.lock.Unlock()
consumed <- item
} else {
q.lock.Unlock()
}
// Small sleep to increase chance of race conditions
time.Sleep(time.Microsecond)
}
}()
}
// Wait for all goroutines to finish
wg.Wait()
// Close the consumed channel
close(consumed)
// Count the number of consumed items
consumedCount := 0
for range consumed {
consumedCount++
}
// Check that the queue is in a consistent state
totalItems := goroutines * operations
remaining := int(q.Len())
assert.Equal(t, totalItems, consumedCount+remaining, "Total items should equal consumed items plus remaining items")
}
// TestQueueWithDifferentTypes tests the Queue with different types
func TestQueueWithDifferentTypes(t *testing.T) {
// Test with string type
qString := New[string](5)
qString.Put("hello", "world")
assert.Equal(t, int64(2), qString.Len(), "Queue length should be 2")
assert.Equal(t, "hello", qString.Pop(), "First item should be 'hello'")
assert.Equal(t, "world", qString.Pop(), "Second item should be 'world'")
// Test with struct type
type Person struct {
Name string
Age int
}
qStruct := New[Person](5)
qStruct.Put(Person{Name: "Alice", Age: 30}, Person{Name: "Bob", Age: 25})
assert.Equal(t, int64(2), qStruct.Len(), "Queue length should be 2")
firstPerson := qStruct.Pop()
assert.Equal(t, "Alice", firstPerson.Name, "First person's name should be 'Alice'")
secondPerson := qStruct.Pop()
assert.Equal(t, "Bob", secondPerson.Name, "Second person's name should be 'Bob'")
}

View File

@@ -35,7 +35,7 @@ require (
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142
github.com/metacubex/utls v1.8.1-0.20250916021850-3fcad0728a32
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20
github.com/mroth/weightedrand/v2 v2.1.0

View File

@@ -139,8 +139,8 @@ github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113a
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 h1:yN3mQ4cT9sPUciw/rO0Isc/8QlO86DB6g9SEMRgQ8Cw=
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 h1:csEbKOzRAxJXffOeZnnS3/kA/F55JiTbKv5jcYqCXms=
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142/go.mod h1:67I3skhEY4Sya8f1YxELwWPoeQdXqZCrWNYLvq8gn2U=
github.com/metacubex/utls v1.8.1-0.20250916021850-3fcad0728a32 h1:endaN8dWxRofYpmJS46mPMQdzNyGEOwvXva42P8RY3I=
github.com/metacubex/utls v1.8.1-0.20250916021850-3fcad0728a32/go.mod h1:67I3skhEY4Sya8f1YxELwWPoeQdXqZCrWNYLvq8gn2U=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f/go.mod h1:oPGcV994OGJedmmxrcK9+ni7jUEMGhR+uVQAdaduIP4=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=