anytls 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +5 -1
- data/docs/ARCHITECTURE.md +15 -2
- data/docs/VALIDATION.md +1 -1
- data/lib/anytls/client.rb +3 -2
- data/lib/anytls/inbound/http_proxy.rb +34 -12
- data/lib/anytls/inbound/socks5.rb +15 -6
- data/lib/anytls/relay.rb +1 -1
- data/lib/anytls/server.rb +31 -7
- data/lib/anytls/session.rb +9 -1
- data/lib/anytls/session_pool.rb +13 -2
- data/lib/anytls/stream.rb +7 -2
- data/lib/anytls/uot/server.rb +28 -5
- data/lib/anytls/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c797edcda4beba1d06a85d522a65ba7c4a3b7079955ea5640cc6f4320078e05b
|
|
4
|
+
data.tar.gz: e0d213605c4574257a28a0231b11dee4484db37aae667dda501532955fc4abf3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8d10834c474ffd7e04c325dcf00b187c2b68984af406591b947cc8dd2ec390d7cd425024d24af87d17de62e1e8217926faae0cb12e9cfc55355a54f85609052
|
|
7
|
+
data.tar.gz: 7799f998dd9825273c1de1dd045e64d7cfad11816f8dd1d594a105386e312a630c1cbd5ae62951d575079733048b36ca215726ab7119edb987125d9b9c33d4b1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.5 (2026-10-06)
|
|
4
|
+
|
|
5
|
+
- 修复 v1 多流集中报告目标就绪时,操作队列未排空却进入等待导致整条会话超时的问题。
|
|
6
|
+
- 服务端停止时继续回收其余资源,完成全部清理尝试后再报告首个关闭或线程异常。
|
|
7
|
+
- UoT 与 SOCKS5 UDP ASSOCIATE 在全部必需 worker 创建成功后才报告就绪,准备失败会回滚资源。
|
|
8
|
+
- Client 关闭时可取消已经创建、仍在 TLS 握手或认证阶段的会话。
|
|
9
|
+
- 大块 Stream 写入与 Relay 使用最多 65,535 字节的片段,减少同步提交次数。
|
|
10
|
+
- 有界合并 HTTP chunk 包装写入,保留流式 flush、提前响应和 trailer 边界。
|
|
11
|
+
- UoT 非固定模式复用最近一个数字目标的解析结果,减少重复调用;域名仍逐包解析。
|
|
12
|
+
|
|
3
13
|
## 0.1.4 (2026-10-05)
|
|
4
14
|
|
|
5
15
|
- 临时接入网络错误后继续接受连接,保留已有代理会话。
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Ruby 3.4+ 的 AnyTLS gem 与命令行客户端、服务端。参考版本固定
|
|
|
4
4
|
[`anytls-go` 的 `fd6167a`](https://github.com/anytls/anytls-go/tree/fd6167acd6d73b9fa3e607659951847fbc9e6c50)。
|
|
5
5
|
核心代码不依赖 Rails。
|
|
6
6
|
|
|
7
|
-
当前版本为 `0.1.
|
|
7
|
+
当前版本为 `0.1.5`,Ruby 实现使用 [MIT 许可证](LICENSE)。项目及参考代码的许可边界见
|
|
8
8
|
[SOURCES](docs/SOURCES.md)。
|
|
9
9
|
|
|
10
10
|
## 使用
|
|
@@ -71,6 +71,7 @@ end
|
|
|
71
71
|
发送,不证明目标应用已处理。错误或超时后的数据不自动重放。Stream 没有独立 fd,
|
|
72
72
|
不提供 `to_io` 或真正的 TCP half-close。
|
|
73
73
|
远端结束后可继续排空接收数据;显式 `close` 会丢弃未读数据,即使远端已经结束。
|
|
74
|
+
大块写入按最多 65,535 字节分片,每片完成后才提交下一片;排队与发送共用本次 write 的超时预算。
|
|
74
75
|
|
|
75
76
|
AnyTLS 没有独立的认证成功回执;首次连接或 v1 peer 可能先返回尚未确认目标连接的
|
|
76
77
|
Stream。`connection_status` 区分 `:unconfirmed`、`:confirmed`、`:failed`。远端拒绝、认证
|
|
@@ -90,6 +91,7 @@ end
|
|
|
90
91
|
字段可承载最多 65,535 字节,空 datagram 有效。实际 UDP socket 还受地址族与系统发送
|
|
91
92
|
上限约束,可能以 `EMSGSIZE` 拒绝较大的包;线协议上限不是网络发送保证。
|
|
92
93
|
SOCKS5 UDP ASSOCIATE 通过同一 UoT 能力实现。
|
|
94
|
+
转发资源与必需线程全部创建后才报告就绪;准备失败不会先回复成功。
|
|
93
95
|
|
|
94
96
|
```ruby
|
|
95
97
|
tls = AnyTLS::TLSConfig.new(server: true, cert: "fullchain.pem", key: "private-key.pem")
|
|
@@ -102,6 +104,8 @@ session 的数量,不会主动创建连接填满池。默认最多 128 个 ses
|
|
|
102
104
|
各 30 秒;归还 session 不提前触发空闲扫描。客户端租用空闲 session;并发请求需要时
|
|
103
105
|
建立新的 session。服务端默认最多
|
|
104
106
|
256 个活动 stream。`Client#stats`、`Server#stats` 返回冻结的计数快照。
|
|
107
|
+
`Client#close` 会取消已创建会话的在途 TLS 握手与认证;尚处于 DNS 解析或 TCP 拨号的创建操作
|
|
108
|
+
仍按既有连接预算结束,迟到的结果不会交给调用者。
|
|
105
109
|
|
|
106
110
|
## 功能与边界
|
|
107
111
|
|
data/docs/ARCHITECTURE.md
CHANGED
|
@@ -39,12 +39,17 @@ Client pool 同一时刻将一个 session 租给一条业务 stream,完成后
|
|
|
39
39
|
服务端 Session 解码器支持多条 stream 的帧,入站数量另受 session 和 server 限制。
|
|
40
40
|
旧空闲连接检测、心跳和 peer stream-ID 限制都在 session 生命周期中处理,不由应用代理层重试。
|
|
41
41
|
池容量等待与定时回收使用独立通知;租用、归还不触发全池回收扫描,关闭会立即唤醒两类等待者。
|
|
42
|
+
工厂取得 Session 后、等待 TLS 就绪前,通过登记回调交出取消句柄。池关闭时也唤醒这些
|
|
43
|
+
在途 owner;它们仍计入 creating 容量,成功租出或失败回收后才退出创建计数。
|
|
44
|
+
DNS 与 TCP 拨号阶段尚无 Session,继续受原连接预算约束。
|
|
42
45
|
|
|
43
46
|
## 线程与 TLS 所有权
|
|
44
47
|
|
|
45
48
|
Session owner 唯一持有 TLS IO 使用权;应用线程通过同步提交请求与 completion 等待写入结果。
|
|
46
49
|
owner 运行具体的 AnyTLS 事件循环:处理请求、选择待发片段、推进 TLS write/read、dispatch
|
|
47
50
|
完整帧、根据方向和最近 deadline 等待 TLS fd 或 wakeup socket。它不是通用调度器。
|
|
51
|
+
每轮最多处理 16 个提交操作;wake 字节可合并,队列仍非空时不得进入阻塞等待。
|
|
52
|
+
即使 v1 目标就绪报告无需发送帧,也必须继续处理剩余操作,同时保留每轮 TLS 读写机会。
|
|
48
53
|
|
|
49
54
|
同一 `SSLSocket` 不由 reader/writer 两线程直接共用。待重试的 `SSL_write` 保持完全相同
|
|
50
55
|
的数据和长度;只有成功返回正整数后才推进偏移。write 返回 WANT_WRITE 时,下一次 TLS
|
|
@@ -79,10 +84,11 @@ Session 状态锁只保护短操作;锁内不等待网络、不回调调用者
|
|
|
79
84
|
| 单 stream 接收缓冲 | 256 KiB |
|
|
80
85
|
| 单 session 接收缓冲 | 4 MiB |
|
|
81
86
|
| 单 session 提交发送预算 | 1 MiB,计入编码和 padding 预留 |
|
|
82
|
-
| Stream
|
|
87
|
+
| Stream 数据写片段与 Relay 读取上限 | 65,535 字节 |
|
|
83
88
|
| UoT 包长度字段 | 65,535 字节;实际 UDP socket 另受地址族/系统大小限制 |
|
|
84
89
|
| CLI 混合入站连接 | 256 |
|
|
85
90
|
|
|
91
|
+
Stream 的实际写片段还会扣除帧头与 padding 预留,适配小于默认值的 session 发送预算。
|
|
86
92
|
接收 owner 不能阻塞等待单个 Stream 消费。超过接收预算时终止对应 stream,并通知 peer;
|
|
87
93
|
控制帧预算也不能无限增长。发送预算不足时让提交者等待。限制按 bytes 计费,不能只用
|
|
88
94
|
队列条目数代表内存大小。上限约束 Ruby 可见缓冲,不宣称它是进程 RSS 的精确上界。
|
|
@@ -102,7 +108,14 @@ AnyTLS Stream,否则已接收的响应体会被丢弃。
|
|
|
102
108
|
预算从上传完成开始,到最终响应头结束;临时响应不延长预算,响应体不受此预算限制。
|
|
103
109
|
超时先记录原因再关闭上游,唤醒已经阻塞的读取者;提前响应与清理均解除等待并回收上传线程。
|
|
104
110
|
HTTP 字段值仅修剪 SP/HTAB,保持重复字段及定界冲突检查。头字段写入以最多 16 KiB
|
|
105
|
-
|
|
111
|
+
缓冲合并,保留依赖的校验和字段顺序,返回前发送。chunked body 同样有界合并包装字节;
|
|
112
|
+
沿用依赖的 flush 边界,在下一次可能阻塞的 body.read 前发送已有数据,并在 body.close
|
|
113
|
+
前发送终止块与 trailer。异常退出丢弃尚未发送的包装缓冲,不重放部分发送的数据。
|
|
114
|
+
|
|
115
|
+
UoT 与 SOCKS5 UDP association 使用启动门:全部必需线程取得后才报告成功并放行转发;
|
|
116
|
+
创建失败则唤醒尚未启动的线程退出、回收 socket/stream,并报告失败。后续延迟创建地址族
|
|
117
|
+
资源失败也会结束转发并触发统一清理。UoT 非固定模式在每条流内最多保留一个数字 IP 与
|
|
118
|
+
端口对应的解析结果,目标改变时更新;域名结果不缓存,继续逐包解析。
|
|
106
119
|
|
|
107
120
|
同一 TCP 连接天然存在队首阻塞;有限缓冲只能限制 Ruby 自身的资源占用和慢消费者影响。
|
|
108
121
|
当前没有扩展协议增加 per-stream credit,不承诺无限慢消费者仍可无损持续接收。
|
data/docs/VALIDATION.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
CI 的 Ubuntu / Ruby 4.0 任务在验证通过后上传 `anytls-gem` artifact,包含 gem 和 `SHA256SUMS`。
|
|
27
27
|
发布前须确认同一提交的完整 Ruby 矩阵和 Go 互通任务均通过,下载该 artifact 并核对校验和。
|
|
28
|
-
运行 `bundle exec ruby script/verify_package.rb /path/to/anytls-0.1.
|
|
28
|
+
运行 `bundle exec ruby script/verify_package.rb /path/to/anytls-0.1.5.gem` 可验证已有包与当前源码
|
|
29
29
|
的文件内容、元数据和依赖一致,并隔离安装该包;提供路径时不会重新构建 gem。
|
|
30
30
|
GitHub Release 和 RubyGems 必须使用这一份验证过的包,发布后下载两处产物并核对 SHA256。
|
|
31
31
|
|
data/lib/anytls/client.rb
CHANGED
|
@@ -28,8 +28,8 @@ module AnyTLS
|
|
|
28
28
|
@scheme_mutex = Mutex.new
|
|
29
29
|
@pool = SessionPool.new(max_sessions: max_sessions, min_idle_sessions: min_idle_sessions,
|
|
30
30
|
idle_timeout: idle_timeout, idle_check_interval: idle_check_interval,
|
|
31
|
-
reuse: reuse) do |sequence, timeout|
|
|
32
|
-
create_session(sequence, timeout)
|
|
31
|
+
reuse: reuse) do |sequence, timeout, ®ister|
|
|
32
|
+
create_session(sequence, timeout, ®ister)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -120,6 +120,7 @@ module AnyTLS
|
|
|
120
120
|
on_idle: ->(item) { @pool.release(item) },
|
|
121
121
|
on_close: ->(item) { @pool.remove(item) },
|
|
122
122
|
on_padding: ->(scheme) { @scheme_mutex.synchronize { @scheme = scheme } })
|
|
123
|
+
yield session
|
|
123
124
|
session.wait_ready(timeout: deadline.remaining)
|
|
124
125
|
rescue StandardError
|
|
125
126
|
session ? session.close : socket&.close
|
|
@@ -50,33 +50,55 @@ module AnyTLS
|
|
|
50
50
|
super
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
#
|
|
54
|
-
# 继续使用依赖的字段校验、顺序和重复字段语义。
|
|
53
|
+
# 头字段和 chunk 包装共用有界缓冲;字段校验、顺序和消息定界仍由依赖处理。
|
|
55
54
|
def write_headers(headers)
|
|
56
|
-
|
|
55
|
+
return super if @write_buffer
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
@write_buffer = +"".b
|
|
59
|
+
super
|
|
60
|
+
flush_write_buffer
|
|
61
|
+
ensure
|
|
62
|
+
@write_buffer = nil
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def write_chunked_body(...)
|
|
67
|
+
@write_buffer = +"".b
|
|
68
|
+
# 沿用依赖的 flush 边界:下一次 body.read 可能阻塞前,先发送已取得的字节。
|
|
57
69
|
super
|
|
58
|
-
bytes = @header_buffer
|
|
59
|
-
@header_buffer = nil
|
|
60
|
-
write_stream(bytes) unless bytes.empty?
|
|
61
70
|
ensure
|
|
62
|
-
@
|
|
71
|
+
@write_buffer = nil
|
|
63
72
|
end
|
|
64
73
|
|
|
65
74
|
private
|
|
66
75
|
|
|
67
76
|
def write_stream(bytes)
|
|
68
|
-
return super unless @
|
|
77
|
+
return super unless @write_buffer
|
|
69
78
|
|
|
70
|
-
if @
|
|
71
|
-
super(@
|
|
72
|
-
@
|
|
79
|
+
if @write_buffer.bytesize + bytes.bytesize > 16_384
|
|
80
|
+
super(@write_buffer) unless @write_buffer.empty?
|
|
81
|
+
@write_buffer.clear
|
|
73
82
|
end
|
|
74
83
|
if bytes.bytesize > 16_384
|
|
75
84
|
super
|
|
76
85
|
else
|
|
77
|
-
@
|
|
86
|
+
@write_buffer << bytes
|
|
78
87
|
end
|
|
79
88
|
end
|
|
89
|
+
|
|
90
|
+
def flush_stream
|
|
91
|
+
flush_write_buffer if @write_buffer
|
|
92
|
+
super
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def flush_write_buffer
|
|
96
|
+
bytes = @write_buffer
|
|
97
|
+
@write_buffer = nil
|
|
98
|
+
write_stream(bytes) unless bytes.empty?
|
|
99
|
+
ensure
|
|
100
|
+
@write_buffer = +"".b
|
|
101
|
+
end
|
|
80
102
|
end
|
|
81
103
|
|
|
82
104
|
# 复用上传线程守候最终响应头,不增加每请求的定时器线程。
|
|
@@ -72,15 +72,14 @@ module AnyTLS
|
|
|
72
72
|
socket = UDPSocket.new(io.addr[0] == "AF_INET6" ? Socket::AF_INET6 : Socket::AF_INET)
|
|
73
73
|
socket.bind(io.addr[3], 0)
|
|
74
74
|
packets = @client.open_datagrams(timeout: @handshake_timeout)
|
|
75
|
-
reply(io, 0, Address.new(host: socket.addr[3], port: socket.addr[1]))
|
|
76
|
-
io.timeout = nil
|
|
77
75
|
source_ip = IPAddr.new(io.peeraddr[3]).native.to_s
|
|
78
76
|
source_port = requested_source.port.zero? ? nil : requested_source.port
|
|
79
77
|
peer = nil
|
|
80
78
|
peer_lock = Mutex.new
|
|
81
79
|
done = Queue.new
|
|
82
80
|
workers = []
|
|
83
|
-
|
|
81
|
+
started = Queue.new
|
|
82
|
+
workers << packet_worker(done, started) do
|
|
84
83
|
loop do
|
|
85
84
|
wire, sender = socket.recvfrom(UoT::Connection::MAX_PACKET_SIZE + 263)
|
|
86
85
|
next unless IPAddr.new(sender[3]).native.to_s == source_ip
|
|
@@ -105,7 +104,7 @@ module AnyTLS
|
|
|
105
104
|
end
|
|
106
105
|
end
|
|
107
106
|
end
|
|
108
|
-
workers << packet_worker(done) do
|
|
107
|
+
workers << packet_worker(done, started) do
|
|
109
108
|
loop do
|
|
110
109
|
payload, destination = packets.receive_packet
|
|
111
110
|
target = peer_lock.synchronize { peer }
|
|
@@ -115,17 +114,27 @@ module AnyTLS
|
|
|
115
114
|
socket.send(wire, 0, target[0], target[1])
|
|
116
115
|
end
|
|
117
116
|
end
|
|
118
|
-
workers << packet_worker(done) { loop { io.readpartial(1024) } }
|
|
117
|
+
workers << packet_worker(done, started) { loop { io.readpartial(1024) } }
|
|
118
|
+
# 成功回复提交完整的转发资源;部分创建失败时尚未放行任何 worker。
|
|
119
|
+
return unless reply(io, 0, Address.new(host: socket.addr[3], port: socket.addr[1]))
|
|
120
|
+
|
|
121
|
+
io.timeout = nil
|
|
122
|
+
workers.size.times { started << true }
|
|
119
123
|
done.pop
|
|
124
|
+
rescue ThreadError
|
|
125
|
+
reply(io, 1)
|
|
120
126
|
ensure
|
|
127
|
+
started&.close
|
|
121
128
|
socket&.close
|
|
122
129
|
packets&.close
|
|
123
130
|
io.close
|
|
124
131
|
workers&.each(&:join)
|
|
125
132
|
end
|
|
126
133
|
|
|
127
|
-
def packet_worker(done, &block)
|
|
134
|
+
def packet_worker(done, started, &block)
|
|
128
135
|
Thread.new do
|
|
136
|
+
next unless started.pop
|
|
137
|
+
|
|
129
138
|
block.call
|
|
130
139
|
rescue IOError, SystemCallError, Error, SocketError
|
|
131
140
|
nil
|
data/lib/anytls/relay.rb
CHANGED
data/lib/anytls/server.rb
CHANGED
|
@@ -84,6 +84,7 @@ module AnyTLS
|
|
|
84
84
|
def stop(grace: 5)
|
|
85
85
|
deadline = Deadline.new(grace)
|
|
86
86
|
owns_stop = false
|
|
87
|
+
failure = nil
|
|
87
88
|
sessions = @mutex.synchronize do
|
|
88
89
|
return if @stopped || @stop_owner == Thread.current
|
|
89
90
|
|
|
@@ -96,7 +97,11 @@ module AnyTLS
|
|
|
96
97
|
@stop_owner = Thread.current
|
|
97
98
|
@stopping = true
|
|
98
99
|
@changed.broadcast
|
|
99
|
-
|
|
100
|
+
begin
|
|
101
|
+
@listener&.close unless @listener&.closed?
|
|
102
|
+
rescue StandardError => error
|
|
103
|
+
failure ||= error
|
|
104
|
+
end
|
|
100
105
|
while @active_streams.positive?
|
|
101
106
|
remaining = deadline.remaining
|
|
102
107
|
break if remaining&.zero?
|
|
@@ -105,11 +110,30 @@ module AnyTLS
|
|
|
105
110
|
end
|
|
106
111
|
@sessions.keys
|
|
107
112
|
end
|
|
108
|
-
|
|
109
|
-
sessions.each
|
|
113
|
+
# 单个 close/join 的异常不能跳过其他资源;完成全部回收尝试后再报告首错。
|
|
114
|
+
sessions.each do |session|
|
|
115
|
+
session.close(wait: false)
|
|
116
|
+
rescue StandardError => error
|
|
117
|
+
failure ||= error
|
|
118
|
+
end
|
|
110
119
|
workers, targets = @mutex.synchronize { [@workers.dup, @targets.keys] }
|
|
111
|
-
targets.each
|
|
112
|
-
|
|
120
|
+
targets.each do |socket|
|
|
121
|
+
IOHelpers.close(socket) unless socket.closed?
|
|
122
|
+
rescue StandardError => error
|
|
123
|
+
failure ||= error
|
|
124
|
+
end
|
|
125
|
+
sessions.each do |session|
|
|
126
|
+
session.close
|
|
127
|
+
rescue StandardError => error
|
|
128
|
+
failure ||= error
|
|
129
|
+
end
|
|
130
|
+
workers.each do |thread|
|
|
131
|
+
thread.join unless thread == Thread.current
|
|
132
|
+
rescue StandardError => error
|
|
133
|
+
failure ||= error
|
|
134
|
+
end
|
|
135
|
+
raise failure if failure
|
|
136
|
+
|
|
113
137
|
nil
|
|
114
138
|
ensure
|
|
115
139
|
if owns_stop
|
|
@@ -175,7 +199,7 @@ module AnyTLS
|
|
|
175
199
|
@workers.select!(&:alive?)
|
|
176
200
|
@workers << Thread.new do
|
|
177
201
|
handle_stream(stream, session)
|
|
178
|
-
rescue Error, IOError, SystemCallError, SocketError => error
|
|
202
|
+
rescue Error, IOError, SystemCallError, SocketError, ThreadError => error
|
|
179
203
|
Log.emit(@logger, :debug, "AnyTLS outbound ended: #{error.class}")
|
|
180
204
|
ensure
|
|
181
205
|
begin
|
|
@@ -217,7 +241,7 @@ module AnyTLS
|
|
|
217
241
|
reported = true
|
|
218
242
|
Relay.copy(stream, target)
|
|
219
243
|
end
|
|
220
|
-
rescue Error, IOError, SystemCallError, SocketError => error
|
|
244
|
+
rescue Error, IOError, SystemCallError, SocketError, ThreadError => error
|
|
221
245
|
session.report_connected(stream.id, error: error) unless reported || stream.closed?
|
|
222
246
|
raise
|
|
223
247
|
ensure
|
data/lib/anytls/session.rb
CHANGED
|
@@ -247,6 +247,10 @@ module AnyTLS
|
|
|
247
247
|
end
|
|
248
248
|
|
|
249
249
|
def wait_for_work(read_wait)
|
|
250
|
+
# wake 字节可以被合并排空,不能把它当作每个操作各有一次的通知。
|
|
251
|
+
# 每轮仍只处理有限操作并推进 TLS;队列未空时直接进入下一轮。
|
|
252
|
+
return if @mutex.synchronize { !@operations.empty? || @stopping }
|
|
253
|
+
|
|
250
254
|
readers = [@wake_reader]
|
|
251
255
|
writers = []
|
|
252
256
|
if @current && @transport.write_wait == :wait_writable
|
|
@@ -542,7 +546,11 @@ module AnyTLS
|
|
|
542
546
|
end
|
|
543
547
|
|
|
544
548
|
def register_stream(id)
|
|
545
|
-
|
|
549
|
+
# 大帧仍须容纳于会话预算;较小的自定义预算保留原有分片发送能力。
|
|
550
|
+
payload_limit = @send_limit - Protocol::HEADER_SIZE - (@server ? 0 : @scheme.max_padding_bytes)
|
|
551
|
+
write_limit = payload_limit.clamp(1, Protocol::MAX_PAYLOAD)
|
|
552
|
+
stream = Stream.new(self, id, receive_limit: @receive_limit, receive_budget: @receive_budget,
|
|
553
|
+
write_limit: write_limit)
|
|
546
554
|
@mutex.synchronize do
|
|
547
555
|
@streams[id] = stream
|
|
548
556
|
@active_count = @streams.size
|
data/lib/anytls/session_pool.rb
CHANGED
|
@@ -31,6 +31,7 @@ module AnyTLS
|
|
|
31
31
|
@changed = ConditionVariable.new
|
|
32
32
|
@maintenance_changed = ConditionVariable.new
|
|
33
33
|
@sessions = {}
|
|
34
|
+
@pending_sessions = {}
|
|
34
35
|
@idle = {}
|
|
35
36
|
@creating = 0
|
|
36
37
|
@sequence = 0
|
|
@@ -68,10 +69,18 @@ module AnyTLS
|
|
|
68
69
|
|
|
69
70
|
session = nil
|
|
70
71
|
begin
|
|
71
|
-
session = @factory.call(sequence, deadline.remaining)
|
|
72
|
+
session = @factory.call(sequence, deadline.remaining) do |pending|
|
|
73
|
+
# 工厂一旦取得 TLS session 就登记取消句柄;握手完成前也能由 close 唤醒 owner。
|
|
74
|
+
@mutex.synchronize do
|
|
75
|
+
raise ClosedError, "client closed during dial" if @closed
|
|
76
|
+
|
|
77
|
+
@pending_sessions[sequence] = pending
|
|
78
|
+
end
|
|
79
|
+
end
|
|
72
80
|
@mutex.synchronize do
|
|
73
81
|
raise ClosedError, "client closed during dial" if @closed || session.closed?
|
|
74
82
|
|
|
83
|
+
@pending_sessions.delete(sequence)
|
|
75
84
|
@sessions[session] = :leased
|
|
76
85
|
end
|
|
77
86
|
session
|
|
@@ -80,6 +89,7 @@ module AnyTLS
|
|
|
80
89
|
raise
|
|
81
90
|
ensure
|
|
82
91
|
@mutex.synchronize do
|
|
92
|
+
@pending_sessions.delete(sequence)
|
|
83
93
|
@creating -= 1
|
|
84
94
|
@changed.broadcast
|
|
85
95
|
end
|
|
@@ -125,8 +135,9 @@ module AnyTLS
|
|
|
125
135
|
@closed = true
|
|
126
136
|
@changed.broadcast
|
|
127
137
|
@maintenance_changed.broadcast
|
|
128
|
-
result = @sessions.keys
|
|
138
|
+
result = (@sessions.keys + @pending_sessions.values).uniq
|
|
129
139
|
@sessions.clear
|
|
140
|
+
@pending_sessions.clear
|
|
130
141
|
@idle.clear
|
|
131
142
|
result
|
|
132
143
|
end
|
data/lib/anytls/stream.rb
CHANGED
|
@@ -7,10 +7,15 @@ module AnyTLS
|
|
|
7
7
|
class Stream
|
|
8
8
|
attr_reader :id
|
|
9
9
|
|
|
10
|
-
def initialize(session, id, receive_limit: 262_144, receive_budget: nil)
|
|
10
|
+
def initialize(session, id, receive_limit: 262_144, receive_budget: nil, write_limit: Protocol::MAX_PAYLOAD)
|
|
11
|
+
unless write_limit.is_a?(Integer) && write_limit.between?(1, Protocol::MAX_PAYLOAD)
|
|
12
|
+
raise ArgumentError, "write limit must be between 1 and 65535"
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
@session = session
|
|
12
16
|
@id = id
|
|
13
17
|
@buffer = ByteBuffer.new(limit: receive_limit, budget: receive_budget)
|
|
18
|
+
@write_limit = write_limit
|
|
14
19
|
@mutex = Mutex.new
|
|
15
20
|
@changed = ConditionVariable.new
|
|
16
21
|
@closed = false
|
|
@@ -63,7 +68,7 @@ module AnyTLS
|
|
|
63
68
|
offset = 0
|
|
64
69
|
while offset < bytes.bytesize
|
|
65
70
|
ensure_open!
|
|
66
|
-
chunk = bytes.byteslice(offset,
|
|
71
|
+
chunk = bytes.byteslice(offset, @write_limit).b.freeze
|
|
67
72
|
@session.write_stream(id, chunk, deadline: deadline)
|
|
68
73
|
offset += chunk.bytesize
|
|
69
74
|
end
|
data/lib/anytls/uot/server.rb
CHANGED
|
@@ -20,6 +20,7 @@ module AnyTLS
|
|
|
20
20
|
lock = Mutex.new
|
|
21
21
|
finished = Queue.new
|
|
22
22
|
workers = []
|
|
23
|
+
started = Queue.new
|
|
23
24
|
stopping = false
|
|
24
25
|
create_socket = lambda do |family|
|
|
25
26
|
lock.synchronize do
|
|
@@ -33,6 +34,9 @@ module AnyTLS
|
|
|
33
34
|
connected_address.ip_port)
|
|
34
35
|
end
|
|
35
36
|
workers << Thread.new do
|
|
37
|
+
started.pop
|
|
38
|
+
next unless ready_reported
|
|
39
|
+
|
|
36
40
|
loop do
|
|
37
41
|
payload, sender = socket.recvfrom(Connection::MAX_PACKET_SIZE + 1)
|
|
38
42
|
address = connection.destination || Address.new(host: sender[3], port: sender[1])
|
|
@@ -48,20 +52,37 @@ module AnyTLS
|
|
|
48
52
|
end
|
|
49
53
|
end
|
|
50
54
|
end
|
|
51
|
-
#
|
|
55
|
+
# 所有必需线程创建成功后才确认就绪;启动门防止部分准备完成时提前转发。
|
|
52
56
|
create_socket.call(connected_address ? connected_address.afamily : Socket::AF_INET)
|
|
53
|
-
on_ready&.call
|
|
54
|
-
ready_reported = true
|
|
55
57
|
workers << Thread.new do
|
|
58
|
+
started.pop
|
|
59
|
+
next unless ready_reported
|
|
60
|
+
|
|
61
|
+
cached_destination = cached_address = nil
|
|
56
62
|
loop do
|
|
57
63
|
payload, destination = connection.receive_packet
|
|
58
|
-
address = connected_address
|
|
64
|
+
address = connected_address
|
|
65
|
+
unless address
|
|
66
|
+
if destination == cached_destination
|
|
67
|
+
address = cached_address
|
|
68
|
+
else
|
|
69
|
+
address = resolve(destination)
|
|
70
|
+
# 每条流只保留一个数字目标,包含端口;域名继续逐包解析,不引入 DNS 过期语义。
|
|
71
|
+
if destination.ip?
|
|
72
|
+
cached_destination = destination
|
|
73
|
+
cached_address = address
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
59
77
|
socket = create_socket.call(address.afamily)
|
|
60
78
|
connected_address ? socket.send(payload, 0) : socket.send(payload, 0, address.to_sockaddr)
|
|
61
79
|
end
|
|
62
|
-
rescue IOError, SystemCallError, Error, SocketError => error
|
|
80
|
+
rescue IOError, SystemCallError, Error, SocketError, ThreadError => error
|
|
63
81
|
finished << error
|
|
64
82
|
end
|
|
83
|
+
on_ready&.call
|
|
84
|
+
ready_reported = true
|
|
85
|
+
started.close
|
|
65
86
|
finished.pop
|
|
66
87
|
nil
|
|
67
88
|
rescue StandardError => error
|
|
@@ -75,6 +96,8 @@ module AnyTLS
|
|
|
75
96
|
end
|
|
76
97
|
raise
|
|
77
98
|
ensure
|
|
99
|
+
# 准备失败时同样唤醒已创建的线程;未确认就绪的线程直接退出。
|
|
100
|
+
started&.close
|
|
78
101
|
lock&.synchronize do
|
|
79
102
|
stopping = true
|
|
80
103
|
sockets&.each_value(&:close)
|
data/lib/anytls/version.rb
CHANGED