Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0e432fd9b | |||
| a3d8a3a388 | |||
| 437d1a3329 | |||
| 746d93663d | |||
| a3f3fee253 | |||
| 53dee1fffc | |||
| 34dc0cb9b6 | |||
| c83c43194b |
28
changelog.md
28
changelog.md
@@ -1,5 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-03-26 - 26.2.4 - fix(rustproxy-http)
|
||||
improve HTTP/3 connection reuse and clean up stale proxy state
|
||||
|
||||
- Reuse pooled HTTP/3 SendRequest handles to skip repeated SETTINGS handshakes and reduce request overhead on QUIC pool hits
|
||||
- Add periodic cleanup for per-route rate limiters and orphaned backend metrics to prevent unbounded memory growth after traffic or backend errors stop
|
||||
- Enforce HTTP max connection lifetime alongside idle timeouts and apply configured lifetime values from the TCP listener
|
||||
- Reduce HTTP/3 body copying by using owned Bytes paths for request and response streaming, and replace the custom response body adapter with a stream-based implementation
|
||||
- Harden auxiliary proxy components by capping datagram handler buffer growth and removing duplicate RustProxy exit listeners
|
||||
|
||||
## 2026-03-25 - 26.2.3 - fix(repo)
|
||||
no changes to commit
|
||||
|
||||
|
||||
## 2026-03-25 - 26.2.2 - fix(proxy)
|
||||
improve connection cleanup and route validation handling
|
||||
|
||||
- add timeouts for HTTP/1 upstream connection drivers to prevent lingering tasks
|
||||
- ensure QUIC relay sessions cancel and abort background tasks on drop
|
||||
- avoid registering unnamed routes as duplicates and label unnamed catch-all conflicts clearly
|
||||
- fix offset mapping route helper to forward only remaining route options without overriding derived values
|
||||
- update project config filename and toolchain versions for the current build setup
|
||||
|
||||
## 2026-03-23 - 26.2.1 - fix(rustproxy-http)
|
||||
include the upstream request URL when caching H3 Alt-Svc discoveries
|
||||
|
||||
- Tracks the request path that triggered Alt-Svc discovery in connection activity state
|
||||
- Adds request URL context to Alt-Svc debug logging and protocol cache insertion reasons for better traceability
|
||||
|
||||
## 2026-03-23 - 26.2.0 - feat(protocol-cache)
|
||||
add sliding TTL re-probing and eviction for backend protocol detection
|
||||
|
||||
|
||||
2
license
2
license
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2019 Lossless GmbH (hello@lossless.com)
|
||||
Copyright (c) 2019 Task Venture Capital GmbH (hello@task.vc)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@push.rocks/smartproxy",
|
||||
"version": "26.2.0",
|
||||
"version": "26.2.4",
|
||||
"private": false,
|
||||
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
||||
"main": "dist_ts/index.js",
|
||||
@@ -16,13 +16,13 @@
|
||||
"buildDocs": "tsdoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^4.3.0",
|
||||
"@git.zone/tsrun": "^2.0.1",
|
||||
"@git.zone/tsrust": "^1.3.0",
|
||||
"@git.zone/tstest": "^3.5.0",
|
||||
"@push.rocks/smartserve": "^2.0.1",
|
||||
"@git.zone/tsbuild": "^4.4.0",
|
||||
"@git.zone/tsrun": "^2.0.2",
|
||||
"@git.zone/tsrust": "^1.3.2",
|
||||
"@git.zone/tstest": "^3.6.0",
|
||||
"@push.rocks/smartserve": "^2.0.3",
|
||||
"@types/node": "^25.5.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript": "^6.0.2",
|
||||
"why-is-node-running": "^3.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -41,7 +41,7 @@
|
||||
"dist_ts_web/**/*",
|
||||
"assets/**/*",
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
".smartconfig.json",
|
||||
"readme.md",
|
||||
"changelog.md"
|
||||
],
|
||||
|
||||
469
pnpm-lock.yaml
generated
469
pnpm-lock.yaml
generated
@@ -25,26 +25,26 @@ importers:
|
||||
version: 10.2.4
|
||||
devDependencies:
|
||||
'@git.zone/tsbuild':
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0
|
||||
specifier: ^4.4.0
|
||||
version: 4.4.0
|
||||
'@git.zone/tsrun':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
specifier: ^2.0.2
|
||||
version: 2.0.2
|
||||
'@git.zone/tsrust':
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
specifier: ^1.3.2
|
||||
version: 1.3.2
|
||||
'@git.zone/tstest':
|
||||
specifier: ^3.5.0
|
||||
version: 3.5.0(socks@2.8.7)(typescript@5.9.3)
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0(socks@2.8.7)(typescript@6.0.2)
|
||||
'@push.rocks/smartserve':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
specifier: ^2.0.3
|
||||
version: 2.0.3
|
||||
'@types/node':
|
||||
specifier: ^25.5.0
|
||||
version: 25.5.0
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
specifier: ^6.0.2
|
||||
version: 6.0.2
|
||||
why-is-node-running:
|
||||
specifier: ^3.2.2
|
||||
version: 3.2.2
|
||||
@@ -414,28 +414,28 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@git.zone/tsbuild@4.3.0':
|
||||
resolution: {integrity: sha512-lb6eMQ8RQPaJqAB4kC++GIElOiTAH1pClmoND/q7XHuiMZxv6cXz2/U/sZt339mon2c40dXRG2tkLF2jRsP0pQ==}
|
||||
'@git.zone/tsbuild@4.4.0':
|
||||
resolution: {integrity: sha512-98igHfppi6blFYDyzNukNkj4FUO5ZlyXEaSyJh8vCkkZM8SyAgfZj+NUWA1D1iaPXE58UvK1Pt/o8p8iI9UHHw==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tsbundle@2.9.1':
|
||||
resolution: {integrity: sha512-JW1xjSv7UjAm2lwAQPxhCWs14wqs+UIq5FqIGUPuI6rrDBWIMT2d0gpP6iP6TqXqgm6XpBlfU4rHcHheUXzXbQ==}
|
||||
'@git.zone/tsbundle@2.10.0':
|
||||
resolution: {integrity: sha512-dw2VFlgKssDlCxg92wSPiiAKwfCjJBOEOYXq1xO91OpjQLOkyogCxSLy0jzQ2BYnt4qmBnapjamzYzVjCr4CWg==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tspublish@1.11.2':
|
||||
resolution: {integrity: sha512-BcGap1OzXDgXpfQXMh9W17r/CkWNhPsJ3WzjG2wrGE+ePUJCJAm9w6+J8G5WdZZcZKPqTB07cp707LbSiksc5A==}
|
||||
'@git.zone/tspublish@1.11.5':
|
||||
resolution: {integrity: sha512-3tCGhVbH6S/17n3A6Tc6H+ncRdxxbTT0ABcj8S1wRLA8YuBSj9bY7k6uj/iFRy/B/OepB94m1goCiaWESdcZYg==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tsrun@2.0.1':
|
||||
resolution: {integrity: sha512-NEcnsjvlC1o3Z6SS3VhKCf6Ev+Sh4EAinmggslrIR/ppMrvjDbXNFXoyr3PB+GLeSAR0JRZ1fGvVYjpEzjBdIg==}
|
||||
'@git.zone/tsrun@2.0.2':
|
||||
resolution: {integrity: sha512-Rnp/wYHzI8A1pVBKOOePRJgQiBZdW+GEjpQk2uhvXz6A+ljUV2SXKc7NpQVVDsjEZaNFeAI9jMYOdk3lm3yMDA==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tsrust@1.3.0':
|
||||
resolution: {integrity: sha512-dvmTAiM04Pkd7J1Gail3fu7aasmILQhC5vKL71/g6HYhpvl16/c+Dj3We5G4HsFr0jvAr+Xu570ZGEuZrtRcCg==}
|
||||
'@git.zone/tsrust@1.3.2':
|
||||
resolution: {integrity: sha512-bUGomPk++He47Q6rnd9bihX6qoYtXgp9BtroBnNADk3q8WGyHivAcPwqIe4Bk32eByzW1Acc37u/h5gb/V8ekA==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tstest@3.5.0':
|
||||
resolution: {integrity: sha512-ugIJzdVkbgqSSw08SZajE7TB01GIYjEAmIy67O5skhvOyszGifwzJdR+8dS1VbQGlUUWQZMGQ2IowllHbAZYJQ==}
|
||||
'@git.zone/tstest@3.6.0':
|
||||
resolution: {integrity: sha512-5D6COywCXmCqeUB8v6/kOzjEWCTKTUTI3ZB99ebwEibENFXnFBoVxNSRKN0pSmBYlgBEkT7DLNfTfp5tclSg8A==}
|
||||
hasBin: true
|
||||
|
||||
'@img/colour@1.1.0':
|
||||
@@ -783,8 +783,8 @@ packages:
|
||||
'@napi-rs/wasm-runtime@1.1.1':
|
||||
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
|
||||
|
||||
'@oxc-project/types@0.99.0':
|
||||
resolution: {integrity: sha512-LLDEhXB7g1m5J+woRSgfKsFPS3LhR9xRhTeIoEBm5WrkwMxn6eZ0Ld0c0K5eHB57ChZX6I3uSmmLjZ8pcjlRcw==}
|
||||
'@oxc-project/types@0.122.0':
|
||||
resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==}
|
||||
|
||||
'@pdf-lib/standard-fonts@1.0.0':
|
||||
resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==}
|
||||
@@ -858,12 +858,12 @@ packages:
|
||||
'@push.rocks/lik@6.3.1':
|
||||
resolution: {integrity: sha512-UWDwGBaVx5yPtAFXqDDBtQZCzETUOA/7myQIXb+YBsuiIw4yQuhNZ23uY2ChQH2Zn6DLqdNSgQcYC0WywMZBNQ==}
|
||||
|
||||
'@push.rocks/lik@6.4.0':
|
||||
resolution: {integrity: sha512-GCdXyF2a6NP+i0W6Mib1PjtA6JGrl6Ae17SbaQwqTscn4JHNta6xm9r+D8/b83XGZsoU903FlJZli3YqJCxT9Q==}
|
||||
|
||||
'@push.rocks/mongodump@1.1.0':
|
||||
resolution: {integrity: sha512-kW0ZUGyf1e4nwloVwBQjNId+MzgTcNS834C+RxH21i1NqyOubbpWZtJtPP+K+s35nSJRyCTy3ICfBMdDBTAm2w==}
|
||||
|
||||
'@push.rocks/npmextra@5.3.3':
|
||||
resolution: {integrity: sha512-snLpSHwaQ5OXlZzF1KX/FY71W5LwajjBzor82Vue0smjEPnSeUPY5/JcVdMwtdprdJe13pc/EQQuIiL/zw4/yg==}
|
||||
|
||||
'@push.rocks/qenv@6.1.3':
|
||||
resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==}
|
||||
|
||||
@@ -888,6 +888,9 @@ packages:
|
||||
'@push.rocks/smartclickhouse@2.2.0':
|
||||
resolution: {integrity: sha512-eTzKiREIPSzL1kPkVyD6vEbn+WV/DvQqDjP67VlhNlQGbRcemnJG/eLrUUR1ytmdIqnsZGEK6UYBgyj5nhzLNQ==}
|
||||
|
||||
'@push.rocks/smartconfig@6.1.0':
|
||||
resolution: {integrity: sha512-B+xh63PhGAsSwuRyCKXr4PAjJ4HoVKhNysi67OGY6gGqGm6uopgEW1cvrUZ7T5ZSck9KlVx7ZTugbqm6dqBK1Q==}
|
||||
|
||||
'@push.rocks/smartcrypto@2.0.4':
|
||||
resolution: {integrity: sha512-1+/5bsjyataf5uUkUNnnVXGRAt+gHVk1KDzozjTqgqJxHvQk1d9fVDohL6CxUhUucTPtu5VR5xNBiV8YCDuGyw==}
|
||||
|
||||
@@ -1017,8 +1020,8 @@ packages:
|
||||
'@push.rocks/smartrx@3.0.10':
|
||||
resolution: {integrity: sha512-USjIYcsSfzn14cwOsxgq/bBmWDTTzy3ouWAnW5NdMyRRzEbmeNrvmy6TRqNeDlJ2PsYNTt1rr/zGUqvIy72ITg==}
|
||||
|
||||
'@push.rocks/smartserve@2.0.1':
|
||||
resolution: {integrity: sha512-YQb2qexfCzCqOlLWBBXKMg6xG4zahCPAxomz/KEKAwHtW6wMTtuHKSTSkRTQ0vl9jssLMAmRz2OyafiL9XGJXQ==}
|
||||
'@push.rocks/smartserve@2.0.3':
|
||||
resolution: {integrity: sha512-PttdFlh61lsDNSRvRCSlKjRzuxgD3WP2XLuBNXu1hLfqLpQXDESj0ZCRPDZslLZsyFT5aHP9godb4D4L3bzHWA==}
|
||||
|
||||
'@push.rocks/smartshell@3.3.8':
|
||||
resolution: {integrity: sha512-t9J/py0vnea4ZtOs7Anc9dc6lcvg6EDvYBw5eE1mB+KUWxMQf/ROIQwWMo6B9SMNY4JS2UwvfuJQJ8makP/7Tg==}
|
||||
@@ -1029,8 +1032,8 @@ packages:
|
||||
'@push.rocks/smartstate@2.0.27':
|
||||
resolution: {integrity: sha512-q4UKir7GV3hakJWXQR4DoA4tUVwT5GRkJ/MtanHYF0wZLHfS19+nGmyO9y974zk3eT9hmy3+Lq5cKtU2W6+Y3w==}
|
||||
|
||||
'@push.rocks/smartstorage@6.0.1':
|
||||
resolution: {integrity: sha512-W5PEVwO0J2K9YUZRTbKXadC11h6/IBzzqU+P0TIE/xpJZC4K1duEXwEhxGWcbfhCkPRRa51xH8Z5mAmzzm8qxA==}
|
||||
'@push.rocks/smartstorage@6.3.2':
|
||||
resolution: {integrity: sha512-g8rXlVZ+6iKmzNoybtwQntdb7EWA6WnVmbXNOdwDKWR8w4o/7UMErj+H5mt57iqYIy1pzQAoTb8IWJNsti7XQw==}
|
||||
|
||||
'@push.rocks/smartstream@3.4.0':
|
||||
resolution: {integrity: sha512-kePb44W9n5K96zj2Ms3K4xnYbNXP5AfxDd86zZMDQ1/T10nvkIpL9m5w4lG/VJ4KAsWFs81S87BkkcjhhrY5Kw==}
|
||||
@@ -1050,8 +1053,8 @@ packages:
|
||||
'@push.rocks/smartversion@3.0.5':
|
||||
resolution: {integrity: sha512-8MZSo1yqyaKxKq0Q5N188l4un++9GFWVbhCAX5mXJwewZHn97ujffTeL+eOQYpWFTEpUhaq1QhL4NhqObBCt1Q==}
|
||||
|
||||
'@push.rocks/smartwatch@6.3.0':
|
||||
resolution: {integrity: sha512-TeZ1PGBoBMpC4/CK8StIj5InEiFfKp7xWJSm3aYMjB/uaoeRP0vXqv1ORIC/TKYGJuEDuAXUsit8tZVjn0qT1Q==}
|
||||
'@push.rocks/smartwatch@6.4.0':
|
||||
resolution: {integrity: sha512-KDswRgE/siBmZRCsRA07MtW5oF4c9uQEBkwTGPIWneHzksbCDsvs/7agKFEL7WnNifLNwo8w1K1qoiVWkX1fvw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@push.rocks/smartyaml@2.0.5':
|
||||
@@ -1083,146 +1086,152 @@ packages:
|
||||
resolution: {integrity: sha512-bqorOaGXPOuiOSV81luTKrTghg4O4NBRD0zyv7TIqmrMGf4a0uoozaUMp1X8vQdZW+y0gTzUJP9wkzAE6Cci0g==}
|
||||
deprecated: This package has been deprecated in favour of the new package at @push.rocks/smartpromise
|
||||
|
||||
'@rolldown/binding-android-arm64@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-MBGIgysimZPqTDcLXI+i9VveijkP5C3EAncEogXhqfax6YXj1Tr2LY3DVuEOMIjWfMPMhtQSPup4fSTAmgjqIw==}
|
||||
'@rolldown/binding-android-arm64@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-MmKeoLnKu1d9j6r19K8B+prJnIZ7u+zQ+zGQ3YHXGnr41rzE3eqQLovlkvoZnRoxDGPA4ps0pGiwXy6YE3lJyg==}
|
||||
'@rolldown/binding-darwin-arm64@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-qpHedvQBmIjT8zdnjN3nWPR2qjQyJttbXniCEKKdHeAbZG9HyNPBUzQF7AZZGwmS9coQKL+hWg9FhWzh2dZ2IA==}
|
||||
'@rolldown/binding-darwin-x64@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-dDp7WbPapj/NVW0LSiH/CLwMhmLwwKb3R7mh2kWX+QW85X1DGVnIEyKh9PmNJjB/+suG1dJygdtdNPVXK1hylg==}
|
||||
'@rolldown/binding-freebsd-x64@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-9e4l6vy5qNSliDPqNfR6CkBOAx6PH7iDV4OJiEJzajajGrVy8gc/IKKJUsoE52G8ud8MX6r3PMl97NfwgOzB7g==}
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-V48oDR84feRU2KRuzpALp594Uqlx27+zFsT6+BgTcXOtu7dWy350J1G28ydoCwKB+oxwsRPx2e7aeQnmd3YJbQ==}
|
||||
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-ENLmSQCWqSA/+YN45V2FqTIemg7QspaiTjlm327eUAMeOLdqmSOVVyrQexJGNTQ5M8sDYCgVAig2Kk01Ggmqaw==}
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-klahlb2EIFltSUubn/VLjuc3qxp1E7th8ukayPfdkcKvvYcQ5rJztgx8JsJSuAKVzKtNTqUGOhy4On71BuyV8g==}
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-UuA+JqQIgqtkgGN2c/AQ5wi8M6mJHrahz/wciENPTeI6zEIbbLGoth5XN+sQe2pJDejEVofN9aOAp0kaazwnVg==}
|
||||
'@rolldown/binding-linux-x64-musl@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-1BNQW8u4ro8bsN1+tgKENJiqmvc+WfuaUhXzMImOVSMw28pkBKdfZtX2qJPADV3terx+vNJtlsgSGeb3+W6Jiw==}
|
||||
'@rolldown/binding-openharmony-arm64@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-K/p7clhCqJOQpXGykrFaBX2Dp9AUVIDHGc+PtFGBwg7V+mvBTv/tsm3LC3aUmH02H2y3gz4y+nUTQ0MLpofEEg==}
|
||||
'@rolldown/binding-wasm32-wasi@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-a4EkXBtnYYsKipjS7QOhEBM4bU5IlR9N1hU+JcVEVeuTiaslIyhWVKsvf7K2YkQHyVAJ+7/A9BtrGqORFcTgng==}
|
||||
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-5ZXcYyd4GxPA6QfbGrNcQjmjbuLGvfz6728pZMsQvGHI+06LT06M6TPtXvFvLgXtexc+OqvFe1yAIXJU1gob/w==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-tzpnRQXJrSzb8Z9sm97UD3cY0toKOImx+xRKsDLX4zHaAlRXWh7jbaKBePJXEN7gNw7Nm03PBNwphdtA8KSUYQ==}
|
||||
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.52':
|
||||
resolution: {integrity: sha512-/L0htLJZbaZFL1g9OHOblTxbCYIGefErJjtYOwgl9ZqNx27P3L0SDfjhhHIss32gu5NWgnxuT2a2Hnnv6QGHKA==}
|
||||
'@rolldown/pluginutils@1.0.0-rc.11':
|
||||
resolution: {integrity: sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==}
|
||||
|
||||
'@rspack/binding-darwin-arm64@1.7.9':
|
||||
resolution: {integrity: sha512-64dgstte0If5czi9bA/cpOe0ryY6wC9AIQRtyJ3DlOF6Tt+y9cKkmUoGu3V+WYaYIZRT7HNk8V7kL8amVjFTYw==}
|
||||
'@rspack/binding-darwin-arm64@1.7.10':
|
||||
resolution: {integrity: sha512-bsXi7I6TpH+a4L6okIUh1JDvwT+XcK/L7Yvhu5G2t5YYyd2fl5vMM5O9cePRpEb0RdqJZ3Z8i9WIWHap9aQ8Gw==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rspack/binding-darwin-x64@1.7.9':
|
||||
resolution: {integrity: sha512-2QSLs3w4rLy4UUGVnIlkt6IlIKOzR1e0RPsq2FYQW6s3p9JrwRCtOeHohyh7EJSqF54dtfhe9UZSAwba3LqH1Q==}
|
||||
'@rspack/binding-darwin-x64@1.7.10':
|
||||
resolution: {integrity: sha512-h/kOGL1bUflDDYnbiUjaRE9kagJpour4FatGihueV03+cRGQ6jpde+BjUakqzMx65CeDbeYI6jAiPhElnlAtRw==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rspack/binding-linux-arm64-gnu@1.7.9':
|
||||
resolution: {integrity: sha512-qhUGI/uVfvLmKWts4QkVHGL8yfUyJkblZs+OFD5Upa2y676EOsbQgWsCwX4xGB6Tv+TOzFP0SLh/UfO8ZfdE+w==}
|
||||
'@rspack/binding-linux-arm64-gnu@1.7.10':
|
||||
resolution: {integrity: sha512-Z4reus7UxGM4+JuhiIht8KuGP1KgM7nNhOlXUHcQCMswP/Rymj5oJQN3TDWgijFUZs09ULl8t3T+AQAVTd/WvA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rspack/binding-linux-arm64-musl@1.7.9':
|
||||
resolution: {integrity: sha512-VjfmR1hgO9n3L6MaE5KG+DXSrrLVqHHOkVcOtS2LMq3bjMTwbBywY7ycymcLnX5KJsol8d3ZGYep6IfSOt3lFA==}
|
||||
'@rspack/binding-linux-arm64-musl@1.7.10':
|
||||
resolution: {integrity: sha512-LYaoVmWizG4oQ3g+St3eM5qxsyfH07kLirP7NJcDMgvu3eQ29MeyTZ3ugkgW6LvlmJue7eTQyf6CZlanoF5SSg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rspack/binding-linux-x64-gnu@1.7.9':
|
||||
resolution: {integrity: sha512-0kldV+3WTs/VYDWzxJ7K40hCW26IHtnk8xPK3whKoo1649rgeXXa0EdsU5P7hG8Ef5SWQjHHHZ/fuHYSO3Y6HA==}
|
||||
'@rspack/binding-linux-x64-gnu@1.7.10':
|
||||
resolution: {integrity: sha512-aIm2G4Kcm3qxDTNqKarK0oaLY2iXnCmpRQQhAcMlR0aS2LmxL89XzVeRr9GFA1MzGrAsZONWCLkxQvn3WUbm4Q==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rspack/binding-linux-x64-musl@1.7.9':
|
||||
resolution: {integrity: sha512-Gi4872cFtc2d83FKATR6Qcf2VBa/tFCqffI/IwRRl6Hx5FulEBqx+tH7gAuRVF693vrbXNxK+FQ+k4iEsEJxrw==}
|
||||
'@rspack/binding-linux-x64-musl@1.7.10':
|
||||
resolution: {integrity: sha512-SIHQbAgB9IPH0H3H+i5rN5jo9yA/yTMq8b7XfRkTMvZ7P7MXxJ0dE8EJu3BmCLM19sqnTc2eX+SVfE8ZMDzghA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rspack/binding-wasm32-wasi@1.7.9':
|
||||
resolution: {integrity: sha512-5QEzqo6EaolpuZmK6w/mgSueorgGnnzp7dJaAvBj6ECFIg/aLXhXXmWCWbxt7Ws2gKvG5/PgaxDqbUxYL51juA==}
|
||||
'@rspack/binding-wasm32-wasi@1.7.10':
|
||||
resolution: {integrity: sha512-J9HDXHD1tj+9FmX4+K3CTkO7dCE2bootlR37YuC2Owc0Lwl1/i2oGT71KHnMqI9faF/hipAaQM5OywkiiuNB7w==}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@rspack/binding-win32-arm64-msvc@1.7.9':
|
||||
resolution: {integrity: sha512-MMqvcrIc8aOqTuHjWkjdzilvoZ3Hv07Od0Foogiyq3JMudsS3Wcmh7T1dFerGg19MOJcRUeEkrg2NQOMOQ6xDA==}
|
||||
'@rspack/binding-win32-arm64-msvc@1.7.10':
|
||||
resolution: {integrity: sha512-FaQGSCXH89nMOYW0bVp0bKQDQbrOEFFm7yedla7g6mkWlFVQo5UyBxid5wJUCqGJBtJepRxeRfByWiaI5nVGvg==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rspack/binding-win32-ia32-msvc@1.7.9':
|
||||
resolution: {integrity: sha512-4kYYS+NZ2CuNbKjq40yB/UEyB51o1PHj5wpr+Y943oOJXpEKWU2Q4vkF8VEohPEcnA9cKVotYCnqStme+02suA==}
|
||||
'@rspack/binding-win32-ia32-msvc@1.7.10':
|
||||
resolution: {integrity: sha512-/66TNLOeM4R5dHhRWRVbMTgWghgxz+32ym0c/zGGXQRoMbz7210EoL40ALUgdBdeeREO8LoV+Mn7v8/QZCwHzw==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rspack/binding-win32-x64-msvc@1.7.9':
|
||||
resolution: {integrity: sha512-1g+QyXXvs+838Un/4GaUvJfARDGHMCs15eXDYWBl5m/Skubyng8djWAgr6ag1+cVoJZXCPOvybTItcblWF3gbQ==}
|
||||
'@rspack/binding-win32-x64-msvc@1.7.10':
|
||||
resolution: {integrity: sha512-SUa3v1W7PGFCy6AHRmDsm43/tkfaZFi1TN2oIk5aCdT9T51baDVBjAbehRDu9xFbK4piL3k7uqIVSIrKgVqk1g==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rspack/binding@1.7.9':
|
||||
resolution: {integrity: sha512-A56e0NdfNwbOSJoilMkxzaPuVYaKCNn1shuiwWnCIBmhV9ix1n9S1XvquDjkGyv+gCdR1+zfJBOa5DMB7htLHw==}
|
||||
'@rspack/binding@1.7.10':
|
||||
resolution: {integrity: sha512-j+DPEaSJLRgasxXNpYQpvC7wUkQF5WoWPiTfm4fLczwlAmYwGSVkJiyWDrOlvVPiGGYiXIaXEjVWTw6fT6/vnA==}
|
||||
|
||||
'@rspack/core@1.7.9':
|
||||
resolution: {integrity: sha512-VHuSKvRkuv42Ya+TxEGO0LE0r9+8P4tKGokmomj4R1f/Nu2vtS3yoaIMfC4fR6VuHGd3MZ+KTI0cNNwHfFcskw==}
|
||||
'@rspack/core@1.7.10':
|
||||
resolution: {integrity: sha512-dO7J0aHSa9Fg2kGT0+ZsM500lMdlNIyCHavIaz7dTDn6KXvFz1qbWQ/48x3OlNFw1mA0jxAjjw9e7h3sWQZUNg==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
peerDependencies:
|
||||
'@swc/helpers': '>=0.5.1'
|
||||
@@ -2933,8 +2942,8 @@ packages:
|
||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||
hasBin: true
|
||||
|
||||
rolldown@1.0.0-beta.52:
|
||||
resolution: {integrity: sha512-Hbnpljue+JhMJrlOjQ1ixp9me7sUec7OjFvS+A1Qm8k8Xyxmw3ZhxFu7LlSXW1s9AX3POE9W9o2oqCEeR5uDmg==}
|
||||
rolldown@1.0.0-rc.11:
|
||||
resolution: {integrity: sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
|
||||
@@ -2995,8 +3004,8 @@ packages:
|
||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||
|
||||
smol-toml@1.6.0:
|
||||
resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==}
|
||||
smol-toml@1.6.1:
|
||||
resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
@@ -3162,8 +3171,8 @@ packages:
|
||||
typed-query-selector@2.12.1:
|
||||
resolution: {integrity: sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
typescript@6.0.2:
|
||||
resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
@@ -3275,6 +3284,18 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
ws@8.20.0:
|
||||
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
xml-parse-from-string@1.0.1:
|
||||
resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==}
|
||||
|
||||
@@ -3938,9 +3959,9 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@git.zone/tsbuild@4.3.0':
|
||||
'@git.zone/tsbuild@4.4.0':
|
||||
dependencies:
|
||||
'@git.zone/tspublish': 1.11.2
|
||||
'@git.zone/tspublish': 1.11.5
|
||||
'@push.rocks/early': 4.0.4
|
||||
'@push.rocks/smartcli': 4.0.20
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
@@ -3949,7 +3970,7 @@ snapshots:
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
typescript: 5.9.3
|
||||
typescript: 6.0.2
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- aws-crt
|
||||
@@ -3960,11 +3981,11 @@ snapshots:
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@git.zone/tsbundle@2.9.1':
|
||||
'@git.zone/tsbundle@2.10.0':
|
||||
dependencies:
|
||||
'@push.rocks/early': 4.0.4
|
||||
'@push.rocks/npmextra': 5.3.3
|
||||
'@push.rocks/smartcli': 4.0.20
|
||||
'@push.rocks/smartconfig': 6.1.0
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfs': 1.5.0
|
||||
'@push.rocks/smartinteract': 2.0.16
|
||||
@@ -3973,12 +3994,12 @@ snapshots:
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartspawn': 3.0.3
|
||||
'@rspack/core': 1.7.9
|
||||
'@rspack/core': 1.7.10
|
||||
'@types/html-minifier': 4.0.6
|
||||
esbuild: 0.27.4
|
||||
html-minifier: 4.0.0
|
||||
rolldown: 1.0.0-beta.52
|
||||
typescript: 5.9.3
|
||||
rolldown: 1.0.0-rc.11
|
||||
typescript: 6.0.2
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- '@swc/helpers'
|
||||
@@ -3986,11 +4007,11 @@ snapshots:
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@git.zone/tspublish@1.11.2':
|
||||
'@git.zone/tspublish@1.11.5':
|
||||
dependencies:
|
||||
'@push.rocks/consolecolor': 2.0.3
|
||||
'@push.rocks/npmextra': 5.3.3
|
||||
'@push.rocks/smartcli': 4.0.20
|
||||
'@push.rocks/smartconfig': 6.1.0
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartfs': 1.5.0
|
||||
@@ -4009,34 +4030,34 @@ snapshots:
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@git.zone/tsrun@2.0.1':
|
||||
'@git.zone/tsrun@2.0.2':
|
||||
dependencies:
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartshell': 3.3.8
|
||||
tsx: 4.21.0
|
||||
|
||||
'@git.zone/tsrust@1.3.0':
|
||||
'@git.zone/tsrust@1.3.2':
|
||||
dependencies:
|
||||
'@push.rocks/early': 4.0.4
|
||||
'@push.rocks/npmextra': 5.3.3
|
||||
'@push.rocks/smartcli': 4.0.20
|
||||
'@push.rocks/smartconfig': 6.1.0
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartshell': 3.3.8
|
||||
smol-toml: 1.6.0
|
||||
smol-toml: 1.6.1
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- react
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@git.zone/tstest@3.5.0(socks@2.8.7)(typescript@5.9.3)':
|
||||
'@git.zone/tstest@3.6.0(socks@2.8.7)(typescript@6.0.2)':
|
||||
dependencies:
|
||||
'@git.zone/tsbundle': 2.9.1
|
||||
'@git.zone/tsrun': 2.0.1
|
||||
'@git.zone/tsbundle': 2.10.0
|
||||
'@git.zone/tsrun': 2.0.2
|
||||
'@push.rocks/consolecolor': 2.0.3
|
||||
'@push.rocks/qenv': 6.1.3
|
||||
'@push.rocks/smartbrowser': 2.0.11(typescript@5.9.3)
|
||||
'@push.rocks/smartbrowser': 2.0.11(typescript@6.0.2)
|
||||
'@push.rocks/smartcrypto': 2.0.4
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartenv': 6.0.0
|
||||
@@ -4050,14 +4071,14 @@ snapshots:
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrequest': 5.0.1
|
||||
'@push.rocks/smartserve': 2.0.1
|
||||
'@push.rocks/smartserve': 2.0.3
|
||||
'@push.rocks/smartshell': 3.3.8
|
||||
'@push.rocks/smartstorage': 6.0.1
|
||||
'@push.rocks/smartstorage': 6.3.2
|
||||
'@push.rocks/smarttime': 4.2.3
|
||||
'@push.rocks/smartwatch': 6.3.0
|
||||
'@push.rocks/smartwatch': 6.4.0
|
||||
'@types/ws': 8.18.1
|
||||
figures: 6.1.0
|
||||
ws: 8.19.0
|
||||
ws: 8.20.0
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/credential-providers'
|
||||
- '@mongodb-js/zstd'
|
||||
@@ -4513,7 +4534,7 @@ snapshots:
|
||||
'@tybys/wasm-util': 0.10.1
|
||||
optional: true
|
||||
|
||||
'@oxc-project/types@0.99.0': {}
|
||||
'@oxc-project/types@0.122.0': {}
|
||||
|
||||
'@pdf-lib/standard-fonts@1.0.0':
|
||||
dependencies:
|
||||
@@ -4684,6 +4705,15 @@ snapshots:
|
||||
'@types/symbol-tree': 3.2.5
|
||||
symbol-tree: 3.2.4
|
||||
|
||||
'@push.rocks/lik@6.4.0':
|
||||
dependencies:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartmatch': 2.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smarttime': 4.2.3
|
||||
symbol-tree: 3.2.4
|
||||
|
||||
'@push.rocks/mongodump@1.1.0(socks@2.8.7)':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.3.1
|
||||
@@ -4702,23 +4732,6 @@ snapshots:
|
||||
- snappy
|
||||
- socks
|
||||
|
||||
'@push.rocks/npmextra@5.3.3':
|
||||
dependencies:
|
||||
'@push.rocks/qenv': 6.1.3
|
||||
'@push.rocks/smartfile': 11.2.7
|
||||
'@push.rocks/smartjson': 5.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/taskbuffer': 3.5.0
|
||||
'@tsclass/tsclass': 9.5.0
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- react
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@push.rocks/qenv@6.1.3':
|
||||
dependencies:
|
||||
'@api.global/typedrequest': 3.2.5
|
||||
@@ -4748,11 +4761,11 @@ snapshots:
|
||||
- react-native-b4a
|
||||
- supports-color
|
||||
|
||||
'@push.rocks/smartbrowser@2.0.11(typescript@5.9.3)':
|
||||
'@push.rocks/smartbrowser@2.0.11(typescript@6.0.2)':
|
||||
dependencies:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartpdf': 4.2.0(typescript@5.9.3)
|
||||
'@push.rocks/smartpuppeteer': 2.0.5(typescript@5.9.3)
|
||||
'@push.rocks/smartpdf': 4.2.0(typescript@6.0.2)
|
||||
'@push.rocks/smartpuppeteer': 2.0.5(typescript@6.0.2)
|
||||
'@push.rocks/smartunique': 3.0.9
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
@@ -4811,6 +4824,23 @@ snapshots:
|
||||
'@push.rocks/smarturl': 3.1.0
|
||||
'@push.rocks/webrequest': 4.0.5
|
||||
|
||||
'@push.rocks/smartconfig@6.1.0':
|
||||
dependencies:
|
||||
'@push.rocks/qenv': 6.1.3
|
||||
'@push.rocks/smartfile': 11.2.7
|
||||
'@push.rocks/smartjson': 5.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/taskbuffer': 3.5.0
|
||||
'@tsclass/tsclass': 9.5.0
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- react
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@push.rocks/smartcrypto@2.0.4':
|
||||
dependencies:
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
@@ -5131,7 +5161,7 @@ snapshots:
|
||||
|
||||
'@push.rocks/smartpath@6.0.0': {}
|
||||
|
||||
'@push.rocks/smartpdf@4.2.0(typescript@5.9.3)':
|
||||
'@push.rocks/smartpdf@4.2.0(typescript@6.0.2)':
|
||||
dependencies:
|
||||
'@push.rocks/smartbuffer': 3.0.5
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
@@ -5140,8 +5170,8 @@ snapshots:
|
||||
'@push.rocks/smartnetwork': 4.4.0
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartpuppeteer': 2.0.5(typescript@5.9.3)
|
||||
'@push.rocks/smartserve': 2.0.1
|
||||
'@push.rocks/smartpuppeteer': 2.0.5(typescript@6.0.2)
|
||||
'@push.rocks/smartserve': 2.0.3
|
||||
'@push.rocks/smartunique': 3.0.9
|
||||
'@tsclass/tsclass': 9.5.0
|
||||
pdf-lib: 1.17.1
|
||||
@@ -5166,11 +5196,11 @@ snapshots:
|
||||
|
||||
'@push.rocks/smartpromise@4.2.3': {}
|
||||
|
||||
'@push.rocks/smartpuppeteer@2.0.5(typescript@5.9.3)':
|
||||
'@push.rocks/smartpuppeteer@2.0.5(typescript@6.0.2)':
|
||||
dependencies:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartshell': 3.3.8
|
||||
puppeteer: 24.40.0(typescript@5.9.3)
|
||||
puppeteer: 24.40.0(typescript@6.0.2)
|
||||
tree-kill: 1.2.2
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
@@ -5221,7 +5251,7 @@ snapshots:
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
rxjs: 7.8.2
|
||||
|
||||
'@push.rocks/smartserve@2.0.1':
|
||||
'@push.rocks/smartserve@2.0.3':
|
||||
dependencies:
|
||||
'@api.global/typedrequest': 3.2.5
|
||||
'@cfworker/json-schema': 4.1.1
|
||||
@@ -5260,7 +5290,7 @@ snapshots:
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/webstore': 2.0.20
|
||||
|
||||
'@push.rocks/smartstorage@6.0.1':
|
||||
'@push.rocks/smartstorage@6.3.2':
|
||||
dependencies:
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartrust': 1.3.2
|
||||
@@ -5301,11 +5331,12 @@ snapshots:
|
||||
'@types/semver': 7.7.1
|
||||
semver: 7.7.4
|
||||
|
||||
'@push.rocks/smartwatch@6.3.0':
|
||||
'@push.rocks/smartwatch@6.4.0':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.3.1
|
||||
'@push.rocks/lik': 6.4.0
|
||||
'@push.rocks/smartenv': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrust': 1.3.2
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
chokidar: 5.0.0
|
||||
picomatch: 4.0.3
|
||||
@@ -5374,101 +5405,104 @@ snapshots:
|
||||
|
||||
'@pushrocks/smartpromise@4.0.2': {}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.0.0-beta.52':
|
||||
'@rolldown/binding-android-arm64@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.0.0-beta.52':
|
||||
'@rolldown/binding-darwin-arm64@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.0.0-beta.52':
|
||||
'@rolldown/binding-darwin-x64@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.0.0-beta.52':
|
||||
'@rolldown/binding-freebsd-x64@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.52':
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.52':
|
||||
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.52':
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.52':
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.0.0-beta.52':
|
||||
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.0.0-beta.52':
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.0.0-beta.52':
|
||||
'@rolldown/binding-linux-x64-musl@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.0.0-rc.11':
|
||||
dependencies:
|
||||
'@napi-rs/wasm-runtime': 1.1.1
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.52':
|
||||
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.52':
|
||||
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.11':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.52':
|
||||
'@rolldown/pluginutils@1.0.0-rc.11': {}
|
||||
|
||||
'@rspack/binding-darwin-arm64@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.52': {}
|
||||
|
||||
'@rspack/binding-darwin-arm64@1.7.9':
|
||||
'@rspack/binding-darwin-x64@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-darwin-x64@1.7.9':
|
||||
'@rspack/binding-linux-arm64-gnu@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-linux-arm64-gnu@1.7.9':
|
||||
'@rspack/binding-linux-arm64-musl@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-linux-arm64-musl@1.7.9':
|
||||
'@rspack/binding-linux-x64-gnu@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-linux-x64-gnu@1.7.9':
|
||||
'@rspack/binding-linux-x64-musl@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-linux-x64-musl@1.7.9':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-wasm32-wasi@1.7.9':
|
||||
'@rspack/binding-wasm32-wasi@1.7.10':
|
||||
dependencies:
|
||||
'@napi-rs/wasm-runtime': 1.0.7
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-win32-arm64-msvc@1.7.9':
|
||||
'@rspack/binding-win32-arm64-msvc@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-win32-ia32-msvc@1.7.9':
|
||||
'@rspack/binding-win32-ia32-msvc@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding-win32-x64-msvc@1.7.9':
|
||||
'@rspack/binding-win32-x64-msvc@1.7.10':
|
||||
optional: true
|
||||
|
||||
'@rspack/binding@1.7.9':
|
||||
'@rspack/binding@1.7.10':
|
||||
optionalDependencies:
|
||||
'@rspack/binding-darwin-arm64': 1.7.9
|
||||
'@rspack/binding-darwin-x64': 1.7.9
|
||||
'@rspack/binding-linux-arm64-gnu': 1.7.9
|
||||
'@rspack/binding-linux-arm64-musl': 1.7.9
|
||||
'@rspack/binding-linux-x64-gnu': 1.7.9
|
||||
'@rspack/binding-linux-x64-musl': 1.7.9
|
||||
'@rspack/binding-wasm32-wasi': 1.7.9
|
||||
'@rspack/binding-win32-arm64-msvc': 1.7.9
|
||||
'@rspack/binding-win32-ia32-msvc': 1.7.9
|
||||
'@rspack/binding-win32-x64-msvc': 1.7.9
|
||||
'@rspack/binding-darwin-arm64': 1.7.10
|
||||
'@rspack/binding-darwin-x64': 1.7.10
|
||||
'@rspack/binding-linux-arm64-gnu': 1.7.10
|
||||
'@rspack/binding-linux-arm64-musl': 1.7.10
|
||||
'@rspack/binding-linux-x64-gnu': 1.7.10
|
||||
'@rspack/binding-linux-x64-musl': 1.7.10
|
||||
'@rspack/binding-wasm32-wasi': 1.7.10
|
||||
'@rspack/binding-win32-arm64-msvc': 1.7.10
|
||||
'@rspack/binding-win32-ia32-msvc': 1.7.10
|
||||
'@rspack/binding-win32-x64-msvc': 1.7.10
|
||||
|
||||
'@rspack/core@1.7.9':
|
||||
'@rspack/core@1.7.10':
|
||||
dependencies:
|
||||
'@module-federation/runtime-tools': 0.22.0
|
||||
'@rspack/binding': 1.7.9
|
||||
'@rspack/binding': 1.7.10
|
||||
'@rspack/lite-tapable': 1.1.0
|
||||
|
||||
'@rspack/lite-tapable@1.1.0': {}
|
||||
@@ -6191,14 +6225,14 @@ snapshots:
|
||||
ini: 1.3.8
|
||||
proto-list: 1.2.4
|
||||
|
||||
cosmiconfig@9.0.1(typescript@5.9.3):
|
||||
cosmiconfig@9.0.1(typescript@6.0.2):
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
typescript: 6.0.2
|
||||
|
||||
croner@10.0.1: {}
|
||||
|
||||
@@ -7438,7 +7472,7 @@ snapshots:
|
||||
devtools-protocol: 0.0.1581282
|
||||
typed-query-selector: 2.12.1
|
||||
webdriver-bidi-protocol: 0.4.1
|
||||
ws: 8.19.0
|
||||
ws: 8.20.0
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- bare-buffer
|
||||
@@ -7447,11 +7481,11 @@ snapshots:
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
puppeteer@24.40.0(typescript@5.9.3):
|
||||
puppeteer@24.40.0(typescript@6.0.2):
|
||||
dependencies:
|
||||
'@puppeteer/browsers': 2.13.0
|
||||
chromium-bidi: 14.0.0(devtools-protocol@0.0.1581282)
|
||||
cosmiconfig: 9.0.1(typescript@5.9.3)
|
||||
cosmiconfig: 9.0.1(typescript@6.0.2)
|
||||
devtools-protocol: 0.0.1581282
|
||||
puppeteer-core: 24.40.0
|
||||
typed-query-selector: 2.12.1
|
||||
@@ -7570,25 +7604,26 @@ snapshots:
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
|
||||
rolldown@1.0.0-beta.52:
|
||||
rolldown@1.0.0-rc.11:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.99.0
|
||||
'@rolldown/pluginutils': 1.0.0-beta.52
|
||||
'@oxc-project/types': 0.122.0
|
||||
'@rolldown/pluginutils': 1.0.0-rc.11
|
||||
optionalDependencies:
|
||||
'@rolldown/binding-android-arm64': 1.0.0-beta.52
|
||||
'@rolldown/binding-darwin-arm64': 1.0.0-beta.52
|
||||
'@rolldown/binding-darwin-x64': 1.0.0-beta.52
|
||||
'@rolldown/binding-freebsd-x64': 1.0.0-beta.52
|
||||
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.52
|
||||
'@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.52
|
||||
'@rolldown/binding-linux-arm64-musl': 1.0.0-beta.52
|
||||
'@rolldown/binding-linux-x64-gnu': 1.0.0-beta.52
|
||||
'@rolldown/binding-linux-x64-musl': 1.0.0-beta.52
|
||||
'@rolldown/binding-openharmony-arm64': 1.0.0-beta.52
|
||||
'@rolldown/binding-wasm32-wasi': 1.0.0-beta.52
|
||||
'@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.52
|
||||
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.52
|
||||
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.52
|
||||
'@rolldown/binding-android-arm64': 1.0.0-rc.11
|
||||
'@rolldown/binding-darwin-arm64': 1.0.0-rc.11
|
||||
'@rolldown/binding-darwin-x64': 1.0.0-rc.11
|
||||
'@rolldown/binding-freebsd-x64': 1.0.0-rc.11
|
||||
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.11
|
||||
'@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.11
|
||||
'@rolldown/binding-linux-arm64-musl': 1.0.0-rc.11
|
||||
'@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.11
|
||||
'@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.11
|
||||
'@rolldown/binding-linux-x64-gnu': 1.0.0-rc.11
|
||||
'@rolldown/binding-linux-x64-musl': 1.0.0-rc.11
|
||||
'@rolldown/binding-openharmony-arm64': 1.0.0-rc.11
|
||||
'@rolldown/binding-wasm32-wasi': 1.0.0-rc.11
|
||||
'@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.11
|
||||
'@rolldown/binding-win32-x64-msvc': 1.0.0-rc.11
|
||||
|
||||
run-async@3.0.0: {}
|
||||
|
||||
@@ -7660,7 +7695,7 @@ snapshots:
|
||||
|
||||
smart-buffer@4.2.0: {}
|
||||
|
||||
smol-toml@1.6.0: {}
|
||||
smol-toml@1.6.1: {}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
dependencies:
|
||||
@@ -7861,7 +7896,7 @@ snapshots:
|
||||
|
||||
typed-query-selector@2.12.1: {}
|
||||
|
||||
typescript@5.9.3: {}
|
||||
typescript@6.0.2: {}
|
||||
|
||||
uglify-js@3.19.3: {}
|
||||
|
||||
@@ -7963,6 +7998,8 @@ snapshots:
|
||||
|
||||
ws@8.19.0: {}
|
||||
|
||||
ws@8.20.0: {}
|
||||
|
||||
xml-parse-from-string@1.0.1: {}
|
||||
|
||||
xml2js@0.5.0:
|
||||
|
||||
1
rust/Cargo.lock
generated
1
rust/Cargo.lock
generated
@@ -1270,6 +1270,7 @@ dependencies = [
|
||||
"arc-swap",
|
||||
"bytes",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"h3",
|
||||
"h3-quinn",
|
||||
"http-body",
|
||||
|
||||
@@ -30,3 +30,4 @@ socket2 = { workspace = true }
|
||||
quinn = { workspace = true }
|
||||
h3 = { workspace = true }
|
||||
h3-quinn = { workspace = true }
|
||||
futures = { version = "0.3", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -56,7 +56,11 @@ struct PooledH2 {
|
||||
}
|
||||
|
||||
/// A pooled QUIC/HTTP/3 connection (multiplexed like H2).
|
||||
/// Stores the h3 `SendRequest` handle so pool hits skip the h3 SETTINGS handshake.
|
||||
pub struct PooledH3 {
|
||||
/// Multiplexed h3 request handle — clone to open a new stream.
|
||||
pub send_request: h3::client::SendRequest<h3_quinn::OpenStreams, Bytes>,
|
||||
/// Raw QUIC connection — kept for liveness probing (close_reason) only.
|
||||
pub connection: quinn::Connection,
|
||||
pub created_at: Instant,
|
||||
pub generation: u64,
|
||||
@@ -197,7 +201,10 @@ impl ConnectionPool {
|
||||
|
||||
/// Try to get a pooled QUIC connection for the given key.
|
||||
/// QUIC connections are multiplexed — the connection is shared, not removed.
|
||||
pub fn checkout_h3(&self, key: &PoolKey) -> Option<(quinn::Connection, Duration)> {
|
||||
pub fn checkout_h3(
|
||||
&self,
|
||||
key: &PoolKey,
|
||||
) -> Option<(h3::client::SendRequest<h3_quinn::OpenStreams, Bytes>, quinn::Connection, Duration)> {
|
||||
let entry = self.h3_pool.get(key)?;
|
||||
let pooled = entry.value();
|
||||
let age = pooled.created_at.elapsed();
|
||||
@@ -215,13 +222,20 @@ impl ConnectionPool {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((pooled.connection.clone(), age))
|
||||
Some((pooled.send_request.clone(), pooled.connection.clone(), age))
|
||||
}
|
||||
|
||||
/// Register a QUIC connection in the pool. Returns the generation ID.
|
||||
pub fn register_h3(&self, key: PoolKey, connection: quinn::Connection) -> u64 {
|
||||
/// Register a QUIC connection and its h3 SendRequest handle in the pool.
|
||||
/// Returns the generation ID.
|
||||
pub fn register_h3(
|
||||
&self,
|
||||
key: PoolKey,
|
||||
connection: quinn::Connection,
|
||||
send_request: h3::client::SendRequest<h3_quinn::OpenStreams, Bytes>,
|
||||
) -> u64 {
|
||||
let gen = self.h2_generation.fetch_add(1, Ordering::Relaxed);
|
||||
self.h3_pool.insert(key, PooledH3 {
|
||||
send_request,
|
||||
connection,
|
||||
created_at: Instant::now(),
|
||||
generation: gen,
|
||||
|
||||
@@ -116,7 +116,7 @@ async fn handle_h3_request(
|
||||
cancel: CancellationToken,
|
||||
) -> anyhow::Result<()> {
|
||||
// Stream request body from H3 client via an mpsc channel.
|
||||
let (body_tx, body_rx) = tokio::sync::mpsc::channel::<Bytes>(4);
|
||||
let (body_tx, body_rx) = tokio::sync::mpsc::channel::<Bytes>(32);
|
||||
|
||||
// Spawn the H3 body reader task with cancellation
|
||||
let body_cancel = cancel.clone();
|
||||
@@ -132,8 +132,7 @@ async fn handle_h3_request(
|
||||
}
|
||||
};
|
||||
let mut chunk = chunk;
|
||||
let data = Bytes::copy_from_slice(chunk.chunk());
|
||||
chunk.advance(chunk.remaining());
|
||||
let data = chunk.copy_to_bytes(chunk.remaining());
|
||||
if body_tx.send(data).await.is_err() {
|
||||
break;
|
||||
}
|
||||
@@ -179,8 +178,8 @@ async fn handle_h3_request(
|
||||
while let Some(frame) = resp_body.frame().await {
|
||||
match frame {
|
||||
Ok(frame) => {
|
||||
if let Some(data) = frame.data_ref() {
|
||||
stream.send_data(Bytes::copy_from_slice(data)).await
|
||||
if let Ok(data) = frame.into_data() {
|
||||
stream.send_data(data).await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to send H3 data: {}", e))?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ pub struct ConnActivity {
|
||||
/// checks the backend's original response headers for Alt-Svc before our
|
||||
/// ResponseFilter injects its own. None when not in auto-detect mode or after H3 failure.
|
||||
alt_svc_cache_key: Option<crate::protocol_cache::ProtocolCacheKey>,
|
||||
/// The upstream request path that triggered Alt-Svc discovery. Logged for traceability.
|
||||
alt_svc_request_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ConnActivity {
|
||||
@@ -58,6 +60,7 @@ impl ConnActivity {
|
||||
start: std::time::Instant::now(),
|
||||
active_requests: None,
|
||||
alt_svc_cache_key: None,
|
||||
alt_svc_request_url: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,15 +72,16 @@ const DEFAULT_CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_s
|
||||
/// If no new request arrives within this duration, the connection is closed.
|
||||
const DEFAULT_HTTP_IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(60);
|
||||
|
||||
/// Default HTTP max connection lifetime (1 hour).
|
||||
/// HTTP connections are forcefully closed after this duration regardless of activity.
|
||||
const DEFAULT_HTTP_MAX_LIFETIME: std::time::Duration = std::time::Duration::from_secs(3600);
|
||||
|
||||
/// Default WebSocket inactivity timeout (1 hour).
|
||||
const DEFAULT_WS_INACTIVITY_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3600);
|
||||
|
||||
/// Default WebSocket max lifetime (24 hours).
|
||||
const DEFAULT_WS_MAX_LIFETIME: std::time::Duration = std::time::Duration::from_secs(86400);
|
||||
|
||||
/// Timeout for QUIC (H3) backend connections. Short because UDP is often firewalled.
|
||||
const QUIC_CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3);
|
||||
|
||||
/// Protocol decision for backend connection.
|
||||
#[derive(Debug)]
|
||||
enum ProtocolDecision {
|
||||
@@ -219,6 +223,8 @@ pub struct HttpProxyService {
|
||||
protocol_cache: Arc<crate::protocol_cache::ProtocolCache>,
|
||||
/// HTTP keep-alive idle timeout: close connection if no new request arrives within this duration.
|
||||
http_idle_timeout: std::time::Duration,
|
||||
/// HTTP max connection lifetime: forcefully close connection after this duration regardless of activity.
|
||||
http_max_lifetime: std::time::Duration,
|
||||
/// WebSocket inactivity timeout (no data in either direction).
|
||||
ws_inactivity_timeout: std::time::Duration,
|
||||
/// WebSocket maximum connection lifetime.
|
||||
@@ -245,6 +251,7 @@ impl HttpProxyService {
|
||||
connection_pool: Arc::new(crate::connection_pool::ConnectionPool::new()),
|
||||
protocol_cache: Arc::new(crate::protocol_cache::ProtocolCache::new()),
|
||||
http_idle_timeout: DEFAULT_HTTP_IDLE_TIMEOUT,
|
||||
http_max_lifetime: DEFAULT_HTTP_MAX_LIFETIME,
|
||||
ws_inactivity_timeout: DEFAULT_WS_INACTIVITY_TIMEOUT,
|
||||
ws_max_lifetime: DEFAULT_WS_MAX_LIFETIME,
|
||||
quinn_client_endpoint: Arc::new(Self::create_quinn_client_endpoint()),
|
||||
@@ -272,21 +279,24 @@ impl HttpProxyService {
|
||||
connection_pool: Arc::new(crate::connection_pool::ConnectionPool::new()),
|
||||
protocol_cache: Arc::new(crate::protocol_cache::ProtocolCache::new()),
|
||||
http_idle_timeout: DEFAULT_HTTP_IDLE_TIMEOUT,
|
||||
http_max_lifetime: DEFAULT_HTTP_MAX_LIFETIME,
|
||||
ws_inactivity_timeout: DEFAULT_WS_INACTIVITY_TIMEOUT,
|
||||
ws_max_lifetime: DEFAULT_WS_MAX_LIFETIME,
|
||||
quinn_client_endpoint: Arc::new(Self::create_quinn_client_endpoint()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the HTTP keep-alive idle timeout, WebSocket inactivity timeout, and
|
||||
/// WebSocket max lifetime from connection config values.
|
||||
/// Set the HTTP keep-alive idle timeout, HTTP max lifetime, WebSocket inactivity
|
||||
/// timeout, and WebSocket max lifetime from connection config values.
|
||||
pub fn set_connection_timeouts(
|
||||
&mut self,
|
||||
http_idle_timeout: std::time::Duration,
|
||||
http_max_lifetime: std::time::Duration,
|
||||
ws_inactivity_timeout: std::time::Duration,
|
||||
ws_max_lifetime: std::time::Duration,
|
||||
) {
|
||||
self.http_idle_timeout = http_idle_timeout;
|
||||
self.http_max_lifetime = http_max_lifetime;
|
||||
self.ws_inactivity_timeout = ws_inactivity_timeout;
|
||||
self.ws_max_lifetime = ws_max_lifetime;
|
||||
}
|
||||
@@ -311,6 +321,15 @@ impl HttpProxyService {
|
||||
self.protocol_cache.clear();
|
||||
}
|
||||
|
||||
/// Clean up expired entries in all per-route rate limiters.
|
||||
/// Called from the background sampling task to prevent unbounded growth
|
||||
/// when traffic stops after a burst of unique IPs.
|
||||
pub fn cleanup_all_rate_limiters(&self) {
|
||||
for entry in self.route_rate_limiters.iter() {
|
||||
entry.value().cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/// Snapshot the protocol cache for metrics/UI display.
|
||||
pub fn protocol_cache_snapshot(&self) -> Vec<crate::protocol_cache::ProtocolCacheEntry> {
|
||||
self.protocol_cache.snapshot()
|
||||
@@ -351,6 +370,7 @@ impl HttpProxyService {
|
||||
|
||||
// Capture timeouts before `self` is moved into the service closure.
|
||||
let idle_timeout = self.http_idle_timeout;
|
||||
let max_lifetime = self.http_max_lifetime;
|
||||
|
||||
// Activity tracker: updated at the START and END of each request.
|
||||
// The idle watchdog checks this to determine if the connection is idle
|
||||
@@ -371,7 +391,7 @@ impl HttpProxyService {
|
||||
let cn = cancel_inner.clone();
|
||||
let la = Arc::clone(&la_inner);
|
||||
let st = start;
|
||||
let ca = ConnActivity { last_activity: Arc::clone(&la_inner), start, active_requests: Some(Arc::clone(&ar_inner)), alt_svc_cache_key: None };
|
||||
let ca = ConnActivity { last_activity: Arc::clone(&la_inner), start, active_requests: Some(Arc::clone(&ar_inner)), alt_svc_cache_key: None, alt_svc_request_url: None };
|
||||
async move {
|
||||
let req = req.map(|body| BoxBody::new(body));
|
||||
let result = svc.handle_request(req, peer, port, cn, ca).await;
|
||||
@@ -409,15 +429,23 @@ impl HttpProxyService {
|
||||
}
|
||||
}
|
||||
_ = async {
|
||||
// Idle watchdog: check every 5s whether the connection has been idle
|
||||
// (no active requests AND no activity for idle_timeout).
|
||||
// This avoids killing long-running requests or upgraded connections.
|
||||
// Idle + lifetime watchdog: check every 5s whether the connection has been
|
||||
// idle (no active requests AND no activity for idle_timeout) or exceeded
|
||||
// the max connection lifetime.
|
||||
let check_interval = std::time::Duration::from_secs(5);
|
||||
let mut last_seen = 0u64;
|
||||
loop {
|
||||
tokio::time::sleep(check_interval).await;
|
||||
|
||||
// Never close while a request is in progress
|
||||
// Check max connection lifetime (unconditional — even active connections
|
||||
// must eventually be recycled to prevent resource accumulation).
|
||||
if start.elapsed() >= max_lifetime {
|
||||
debug!("HTTP connection exceeded max lifetime ({}s) from {}",
|
||||
max_lifetime.as_secs(), peer_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Never close for idleness while a request is in progress
|
||||
if active_requests.load(Ordering::Relaxed) > 0 {
|
||||
last_seen = last_activity.load(Ordering::Relaxed);
|
||||
continue;
|
||||
@@ -434,7 +462,7 @@ impl HttpProxyService {
|
||||
last_seen = current;
|
||||
}
|
||||
} => {
|
||||
debug!("HTTP connection idle timeout ({}s) from {}", idle_timeout.as_secs(), peer_addr);
|
||||
debug!("HTTP connection timeout from {}", peer_addr);
|
||||
conn.as_mut().graceful_shutdown();
|
||||
// Give any in-flight work 5s to drain after graceful shutdown
|
||||
let _ = tokio::time::timeout(std::time::Duration::from_secs(5), conn).await;
|
||||
@@ -775,6 +803,7 @@ impl HttpProxyService {
|
||||
// the backend's original Alt-Svc header before ResponseFilter injects our own.
|
||||
if is_auto_detect_mode {
|
||||
conn_activity.alt_svc_cache_key = Some(protocol_cache_key.clone());
|
||||
conn_activity.alt_svc_request_url = Some(upstream_path.to_string());
|
||||
}
|
||||
|
||||
// --- H3 path: try QUIC connection before TCP ---
|
||||
@@ -787,10 +816,10 @@ impl HttpProxyService {
|
||||
};
|
||||
|
||||
// Try H3 pool checkout first
|
||||
if let Some((quic_conn, _age)) = self.connection_pool.checkout_h3(&h3_pool_key) {
|
||||
if let Some((pooled_sr, quic_conn, _age)) = self.connection_pool.checkout_h3(&h3_pool_key) {
|
||||
self.metrics.backend_pool_hit(&upstream_key);
|
||||
let result = self.forward_h3(
|
||||
quic_conn, parts, body, upstream_headers, &upstream_path,
|
||||
quic_conn, Some(pooled_sr), parts, body, upstream_headers, &upstream_path,
|
||||
route_match.route, route_id, &ip_str, &h3_pool_key, domain_str, &conn_activity, &upstream_key,
|
||||
).await;
|
||||
self.upstream_selector.connection_ended(&upstream_key);
|
||||
@@ -803,7 +832,7 @@ impl HttpProxyService {
|
||||
self.metrics.backend_pool_miss(&upstream_key);
|
||||
self.metrics.backend_connection_opened(&upstream_key, std::time::Instant::now().elapsed());
|
||||
let result = self.forward_h3(
|
||||
quic_conn, parts, body, upstream_headers, &upstream_path,
|
||||
quic_conn, None, parts, body, upstream_headers, &upstream_path,
|
||||
route_match.route, route_id, &ip_str, &h3_pool_key, domain_str, &conn_activity, &upstream_key,
|
||||
).await;
|
||||
self.upstream_selector.connection_ended(&upstream_key);
|
||||
@@ -962,7 +991,7 @@ impl HttpProxyService {
|
||||
protocol: crate::connection_pool::PoolProtocol::H3,
|
||||
};
|
||||
let result = self.forward_h3(
|
||||
quic_conn, parts, body, upstream_headers, &upstream_path,
|
||||
quic_conn, None, parts, body, upstream_headers, &upstream_path,
|
||||
route_match.route, route_id, &ip_str, &h3_pool_key, domain_str, &conn_activity, &upstream_key,
|
||||
).await;
|
||||
self.upstream_selector.connection_ended(&upstream_key);
|
||||
@@ -1005,7 +1034,7 @@ impl HttpProxyService {
|
||||
protocol: crate::connection_pool::PoolProtocol::H3,
|
||||
};
|
||||
let result = self.forward_h3(
|
||||
quic_conn, parts, body, upstream_headers, &upstream_path,
|
||||
quic_conn, None, parts, body, upstream_headers, &upstream_path,
|
||||
route_match.route, route_id, &ip_str, &h3_pool_key, domain_str, &conn_activity, &upstream_key,
|
||||
).await;
|
||||
self.upstream_selector.connection_ended(&upstream_key);
|
||||
@@ -1064,7 +1093,7 @@ impl HttpProxyService {
|
||||
protocol: crate::connection_pool::PoolProtocol::H3,
|
||||
};
|
||||
let result = self.forward_h3(
|
||||
quic_conn, parts, body, upstream_headers, &upstream_path,
|
||||
quic_conn, None, parts, body, upstream_headers, &upstream_path,
|
||||
route_match.route, route_id, &ip_str, &h3_pool_key, domain_str, &conn_activity, &upstream_key,
|
||||
).await;
|
||||
self.upstream_selector.connection_ended(&upstream_key);
|
||||
@@ -1107,7 +1136,7 @@ impl HttpProxyService {
|
||||
protocol: crate::connection_pool::PoolProtocol::H3,
|
||||
};
|
||||
let result = self.forward_h3(
|
||||
quic_conn, parts, body, upstream_headers, &upstream_path,
|
||||
quic_conn, None, parts, body, upstream_headers, &upstream_path,
|
||||
route_match.route, route_id, &ip_str, &h3_pool_key, domain_str, &conn_activity, &upstream_key,
|
||||
).await;
|
||||
self.upstream_selector.connection_ended(&upstream_key);
|
||||
@@ -1205,8 +1234,10 @@ impl HttpProxyService {
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = conn.await {
|
||||
debug!("Upstream connection error: {}", e);
|
||||
match tokio::time::timeout(std::time::Duration::from_secs(300), conn).await {
|
||||
Ok(Err(e)) => debug!("Upstream connection error: {}", e),
|
||||
Err(_) => debug!("H1 connection driver timed out after 300s"),
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1799,8 +1830,10 @@ impl HttpProxyService {
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = conn.await {
|
||||
debug!("H1 fallback: upstream connection error: {}", e);
|
||||
match tokio::time::timeout(std::time::Duration::from_secs(300), conn).await {
|
||||
Ok(Err(e)) => debug!("H1 fallback: upstream connection error: {}", e),
|
||||
Err(_) => debug!("H1 fallback: connection driver timed out after 300s"),
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1979,8 +2012,10 @@ impl HttpProxyService {
|
||||
if let Some(ref cache_key) = conn_activity.alt_svc_cache_key {
|
||||
if let Some(alt_svc) = resp_parts.headers.get("alt-svc").and_then(|v| v.to_str().ok()) {
|
||||
if let Some(h3_port) = parse_alt_svc_h3_port(alt_svc) {
|
||||
debug!(h3_port, "Backend advertises H3 via Alt-Svc");
|
||||
self.protocol_cache.insert_h3(cache_key.clone(), h3_port, "Alt-Svc response header");
|
||||
let url = conn_activity.alt_svc_request_url.as_deref().unwrap_or("-");
|
||||
debug!(h3_port, url, "Backend advertises H3 via Alt-Svc");
|
||||
let reason = format!("Alt-Svc response header ({})", url);
|
||||
self.protocol_cache.insert_h3(cache_key.clone(), h3_port, &reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2734,7 +2769,12 @@ impl HttpProxyService {
|
||||
|
||||
let quic_crypto = quinn::crypto::rustls::QuicClientConfig::try_from(tls_config)
|
||||
.expect("Failed to create QUIC client crypto config");
|
||||
let client_config = quinn::ClientConfig::new(Arc::new(quic_crypto));
|
||||
|
||||
// Tune QUIC transport to match H2 flow-control: 2 MB per-stream receive window.
|
||||
let mut transport = quinn::TransportConfig::default();
|
||||
transport.stream_receive_window(quinn::VarInt::from_u32(2 * 1024 * 1024));
|
||||
let mut client_config = quinn::ClientConfig::new(Arc::new(quic_crypto));
|
||||
client_config.transport_config(Arc::new(transport));
|
||||
|
||||
let mut endpoint = quinn::Endpoint::client("0.0.0.0:0".parse().unwrap())
|
||||
.expect("Failed to create QUIC client endpoint");
|
||||
@@ -2756,8 +2796,8 @@ impl HttpProxyService {
|
||||
let server_name = host.to_string();
|
||||
let connecting = self.quinn_client_endpoint.connect(addr, &server_name)?;
|
||||
|
||||
let connection = tokio::time::timeout(QUIC_CONNECT_TIMEOUT, connecting).await
|
||||
.map_err(|_| format!("QUIC connect timeout (3s) for {}", host))??;
|
||||
let connection = tokio::time::timeout(self.connect_timeout, connecting).await
|
||||
.map_err(|_| format!("QUIC connect timeout ({:?}) for {}", self.connect_timeout, host))??;
|
||||
|
||||
debug!("QUIC backend connection established to {}:{}", host, port);
|
||||
Ok(connection)
|
||||
@@ -2767,6 +2807,7 @@ impl HttpProxyService {
|
||||
async fn forward_h3(
|
||||
&self,
|
||||
quic_conn: quinn::Connection,
|
||||
pooled_sender: Option<h3::client::SendRequest<h3_quinn::OpenStreams, Bytes>>,
|
||||
parts: hyper::http::request::Parts,
|
||||
body: BoxBody<Bytes, hyper::Error>,
|
||||
upstream_headers: hyper::HeaderMap,
|
||||
@@ -2779,33 +2820,42 @@ impl HttpProxyService {
|
||||
conn_activity: &ConnActivity,
|
||||
backend_key: &str,
|
||||
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
|
||||
let h3_quinn_conn = h3_quinn::Connection::new(quic_conn.clone());
|
||||
let (mut driver, mut send_request) = match h3::client::builder()
|
||||
.send_grease(false)
|
||||
.build(h3_quinn_conn)
|
||||
.await
|
||||
{
|
||||
Ok(pair) => pair,
|
||||
Err(e) => {
|
||||
error!(backend = %backend_key, domain = %domain, error = %e, "H3 client handshake failed");
|
||||
self.metrics.backend_handshake_error(backend_key);
|
||||
return Ok(error_response(StatusCode::BAD_GATEWAY, "H3 handshake failed"));
|
||||
}
|
||||
};
|
||||
// Obtain the h3 SendRequest handle: skip handshake + driver on pool hit.
|
||||
let (mut send_request, gen_holder) = if let Some(sr) = pooled_sender {
|
||||
// Pool hit — reuse existing h3 session, no SETTINGS round-trip
|
||||
(sr, None)
|
||||
} else {
|
||||
// Fresh QUIC connection — full h3 handshake + driver spawn
|
||||
let h3_quinn_conn = h3_quinn::Connection::new(quic_conn.clone());
|
||||
let (mut driver, sr) = match h3::client::builder()
|
||||
.send_grease(false)
|
||||
.build(h3_quinn_conn)
|
||||
.await
|
||||
{
|
||||
Ok(pair) => pair,
|
||||
Err(e) => {
|
||||
error!(backend = %backend_key, domain = %domain, error = %e, "H3 client handshake failed");
|
||||
self.metrics.backend_handshake_error(backend_key);
|
||||
return Ok(error_response(StatusCode::BAD_GATEWAY, "H3 handshake failed"));
|
||||
}
|
||||
};
|
||||
|
||||
// Spawn the h3 connection driver
|
||||
let driver_pool = Arc::clone(&self.connection_pool);
|
||||
let driver_pool_key = pool_key.clone();
|
||||
let gen_holder = Arc::new(std::sync::atomic::AtomicU64::new(u64::MAX));
|
||||
let driver_gen = Arc::clone(&gen_holder);
|
||||
tokio::spawn(async move {
|
||||
let close_err = std::future::poll_fn(|cx| driver.poll_close(cx)).await;
|
||||
debug!("H3 connection driver closed: {:?}", close_err);
|
||||
let g = driver_gen.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if g != u64::MAX {
|
||||
driver_pool.remove_h3_if_generation(&driver_pool_key, g);
|
||||
let gen_holder = Arc::new(std::sync::atomic::AtomicU64::new(u64::MAX));
|
||||
{
|
||||
let driver_pool = Arc::clone(&self.connection_pool);
|
||||
let driver_pool_key = pool_key.clone();
|
||||
let driver_gen = Arc::clone(&gen_holder);
|
||||
tokio::spawn(async move {
|
||||
let close_err = std::future::poll_fn(|cx| driver.poll_close(cx)).await;
|
||||
debug!("H3 connection driver closed: {:?}", close_err);
|
||||
let g = driver_gen.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if g != u64::MAX {
|
||||
driver_pool.remove_h3_if_generation(&driver_pool_key, g);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
(sr, Some(gen_holder))
|
||||
};
|
||||
|
||||
// Build the H3 request
|
||||
let uri = hyper::Uri::builder()
|
||||
@@ -2835,7 +2885,7 @@ impl HttpProxyService {
|
||||
}
|
||||
};
|
||||
|
||||
// Stream request body
|
||||
// Stream request body (zero-copy: into_data yields owned Bytes)
|
||||
let rid: Option<Arc<str>> = route_id.map(Arc::from);
|
||||
let sip: Arc<str> = Arc::from(source_ip);
|
||||
|
||||
@@ -2845,9 +2895,9 @@ impl HttpProxyService {
|
||||
while let Some(frame) = body.frame().await {
|
||||
match frame {
|
||||
Ok(frame) => {
|
||||
if let Some(data) = frame.data_ref() {
|
||||
if let Ok(data) = frame.into_data() {
|
||||
self.metrics.record_bytes(data.len() as u64, 0, rid.as_deref(), Some(&sip));
|
||||
if let Err(e) = stream.send_data(Bytes::copy_from_slice(data)).await {
|
||||
if let Err(e) = stream.send_data(data).await {
|
||||
error!(backend = %backend_key, error = %e, "H3 send_data failed");
|
||||
return Ok(error_response(StatusCode::BAD_GATEWAY, "H3 body send failed"));
|
||||
}
|
||||
@@ -2889,8 +2939,23 @@ impl HttpProxyService {
|
||||
ResponseFilter::apply_headers(route, headers, None);
|
||||
}
|
||||
|
||||
// Stream response body back via an adapter
|
||||
let h3_body = H3ClientResponseBody { stream };
|
||||
// Stream response body back via unfold — correctly preserves waker across polls
|
||||
let body_stream = futures::stream::unfold(stream, |mut s| async move {
|
||||
match s.recv_data().await {
|
||||
Ok(Some(mut buf)) => {
|
||||
use bytes::Buf;
|
||||
let data = buf.copy_to_bytes(buf.remaining());
|
||||
Some((Ok::<_, hyper::Error>(http_body::Frame::data(data)), s))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(e) => {
|
||||
warn!("H3 response body recv error: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
let h3_body = http_body_util::StreamBody::new(body_stream);
|
||||
|
||||
let counting_body = CountingBody::new(
|
||||
h3_body,
|
||||
Arc::clone(&self.metrics),
|
||||
@@ -2907,10 +2972,16 @@ impl HttpProxyService {
|
||||
|
||||
let body: BoxBody<Bytes, hyper::Error> = BoxBody::new(counting_body);
|
||||
|
||||
// Register connection in pool on success
|
||||
// Register connection in pool on success (fresh connections only)
|
||||
if status != StatusCode::BAD_GATEWAY {
|
||||
let g = self.connection_pool.register_h3(pool_key.clone(), quic_conn);
|
||||
gen_holder.store(g, std::sync::atomic::Ordering::Relaxed);
|
||||
if let Some(gh) = gen_holder {
|
||||
let g = self.connection_pool.register_h3(
|
||||
pool_key.clone(),
|
||||
quic_conn,
|
||||
send_request,
|
||||
);
|
||||
gh.store(g, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
self.metrics.set_backend_protocol(backend_key, "h3");
|
||||
@@ -2939,41 +3010,6 @@ fn parse_alt_svc_h3_port(header_value: &str) -> Option<u16> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Response body adapter for H3 client responses.
|
||||
/// Reads data from the h3 `RequestStream` recv side and presents it as an `http_body::Body`.
|
||||
struct H3ClientResponseBody {
|
||||
stream: h3::client::RequestStream<h3_quinn::BidiStream<Bytes>, Bytes>,
|
||||
}
|
||||
|
||||
impl http_body::Body for H3ClientResponseBody {
|
||||
type Data = Bytes;
|
||||
type Error = hyper::Error;
|
||||
|
||||
fn poll_frame(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
|
||||
// h3's recv_data is async, so we need to poll it manually.
|
||||
// Use a small future to poll the recv_data call.
|
||||
use std::future::Future;
|
||||
let mut fut = Box::pin(self.stream.recv_data());
|
||||
match fut.as_mut().poll(_cx) {
|
||||
Poll::Ready(Ok(Some(mut buf))) => {
|
||||
use bytes::Buf;
|
||||
let data = Bytes::copy_from_slice(buf.chunk());
|
||||
buf.advance(buf.remaining());
|
||||
Poll::Ready(Some(Ok(http_body::Frame::data(data))))
|
||||
}
|
||||
Poll::Ready(Ok(None)) => Poll::Ready(None),
|
||||
Poll::Ready(Err(e)) => {
|
||||
warn!("H3 response body recv error: {}", e);
|
||||
Poll::Ready(None)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Insecure certificate verifier for backend TLS connections (fallback only).
|
||||
/// The production path uses the shared config from tls_handler which has the same
|
||||
/// behavior but with session resumption across all outbound connections.
|
||||
@@ -3042,6 +3078,7 @@ impl Default for HttpProxyService {
|
||||
connection_pool: Arc::new(crate::connection_pool::ConnectionPool::new()),
|
||||
protocol_cache: Arc::new(crate::protocol_cache::ProtocolCache::new()),
|
||||
http_idle_timeout: DEFAULT_HTTP_IDLE_TIMEOUT,
|
||||
http_max_lifetime: DEFAULT_HTTP_MAX_LIFETIME,
|
||||
ws_inactivity_timeout: DEFAULT_WS_INACTIVITY_TIMEOUT,
|
||||
ws_max_lifetime: DEFAULT_WS_MAX_LIFETIME,
|
||||
quinn_client_endpoint: Arc::new(Self::create_quinn_client_endpoint()),
|
||||
|
||||
@@ -624,6 +624,24 @@ impl MetricsCollector {
|
||||
self.ip_pending_tp.retain(|k, _| self.ip_connections.contains_key(k));
|
||||
self.ip_throughput.retain(|k, _| self.ip_connections.contains_key(k));
|
||||
self.ip_total_connections.retain(|k, _| self.ip_connections.contains_key(k));
|
||||
|
||||
// Safety-net: prune orphaned backend error/stats entries for backends
|
||||
// that have no active or total connections (error-only backends).
|
||||
// These accumulate when backend_connect_error/backend_handshake_error
|
||||
// create entries but backend_connection_opened is never called.
|
||||
let known_backends: HashSet<String> = self.backend_active.iter()
|
||||
.map(|e| e.key().clone())
|
||||
.chain(self.backend_total.iter().map(|e| e.key().clone()))
|
||||
.collect();
|
||||
self.backend_connect_errors.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_handshake_errors.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_request_errors.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_connect_time_us.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_connect_count.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_pool_hits.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_pool_misses.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_h2_failures.retain(|k, _| known_backends.contains(k));
|
||||
self.backend_protocol.retain(|k, _| known_backends.contains(k));
|
||||
}
|
||||
|
||||
/// Remove per-route metrics for route IDs that are no longer active.
|
||||
|
||||
@@ -10,7 +10,6 @@ pub mod forwarder;
|
||||
pub mod proxy_protocol;
|
||||
pub mod tls_handler;
|
||||
pub mod connection_tracker;
|
||||
pub mod socket_relay;
|
||||
pub mod socket_opts;
|
||||
pub mod udp_session;
|
||||
pub mod udp_listener;
|
||||
@@ -22,7 +21,6 @@ pub use forwarder::*;
|
||||
pub use proxy_protocol::*;
|
||||
pub use tls_handler::*;
|
||||
pub use connection_tracker::*;
|
||||
pub use socket_relay::*;
|
||||
pub use socket_opts::*;
|
||||
pub use udp_session::*;
|
||||
pub use udp_listener::*;
|
||||
|
||||
@@ -77,6 +77,13 @@ struct RelaySession {
|
||||
cancel: CancellationToken,
|
||||
}
|
||||
|
||||
impl Drop for RelaySession {
|
||||
fn drop(&mut self) {
|
||||
self.cancel.cancel();
|
||||
self.return_task.abort();
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a QUIC endpoint with a PROXY protocol v2 relay layer.
|
||||
///
|
||||
/// Instead of giving the external socket to quinn, we:
|
||||
@@ -634,7 +641,7 @@ async fn forward_quic_stream_to_tcp(
|
||||
let la_watch = Arc::clone(&last_activity);
|
||||
let c2b_abort = c2b.abort_handle();
|
||||
let b2c_abort = b2c.abort_handle();
|
||||
tokio::spawn(async move {
|
||||
let watchdog = tokio::spawn(async move {
|
||||
let check_interval = std::time::Duration::from_secs(5);
|
||||
let mut last_seen = 0u64;
|
||||
loop {
|
||||
@@ -665,6 +672,7 @@ async fn forward_quic_stream_to_tcp(
|
||||
|
||||
let bytes_in = c2b.await.unwrap_or(0);
|
||||
let bytes_out = b2c.await.unwrap_or(0);
|
||||
watchdog.abort();
|
||||
|
||||
Ok((bytes_in, bytes_out))
|
||||
}
|
||||
|
||||
@@ -1,126 +1,4 @@
|
||||
//! Socket handler relay for connecting client connections to a TypeScript handler
|
||||
//! via a Unix domain socket.
|
||||
//! Socket handler relay module.
|
||||
//!
|
||||
//! Protocol: Send a JSON metadata line terminated by `\n`, then bidirectional relay.
|
||||
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::io::{AsyncWriteExt, AsyncReadExt};
|
||||
use tokio::net::TcpStream;
|
||||
use serde::Serialize;
|
||||
use tracing::debug;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct RelayMetadata {
|
||||
connection_id: u64,
|
||||
remote_ip: String,
|
||||
remote_port: u16,
|
||||
local_port: u16,
|
||||
sni: Option<String>,
|
||||
route_name: String,
|
||||
initial_data_base64: Option<String>,
|
||||
}
|
||||
|
||||
/// Relay a client connection to a TypeScript handler via Unix domain socket.
|
||||
///
|
||||
/// Protocol: Send a JSON metadata line terminated by `\n`, then bidirectional relay.
|
||||
pub async fn relay_to_handler(
|
||||
client: TcpStream,
|
||||
relay_socket_path: &str,
|
||||
connection_id: u64,
|
||||
remote_ip: String,
|
||||
remote_port: u16,
|
||||
local_port: u16,
|
||||
sni: Option<String>,
|
||||
route_name: String,
|
||||
initial_data: Option<&[u8]>,
|
||||
) -> std::io::Result<()> {
|
||||
debug!(
|
||||
"Relaying connection {} to handler socket {}",
|
||||
connection_id, relay_socket_path
|
||||
);
|
||||
|
||||
// Connect to TypeScript handler Unix socket
|
||||
let mut handler = UnixStream::connect(relay_socket_path).await?;
|
||||
|
||||
// Build and send metadata header
|
||||
let initial_data_base64 = initial_data.map(base64_encode);
|
||||
|
||||
let metadata = RelayMetadata {
|
||||
connection_id,
|
||||
remote_ip,
|
||||
remote_port,
|
||||
local_port,
|
||||
sni,
|
||||
route_name,
|
||||
initial_data_base64,
|
||||
};
|
||||
|
||||
let metadata_json = serde_json::to_string(&metadata)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
handler.write_all(metadata_json.as_bytes()).await?;
|
||||
handler.write_all(b"\n").await?;
|
||||
|
||||
// Bidirectional relay between client and handler
|
||||
let (mut client_read, mut client_write) = client.into_split();
|
||||
let (mut handler_read, mut handler_write) = handler.into_split();
|
||||
|
||||
let c2h = tokio::spawn(async move {
|
||||
let mut buf = vec![0u8; 65536];
|
||||
loop {
|
||||
let n = match client_read.read(&mut buf).await {
|
||||
Ok(0) | Err(_) => break,
|
||||
Ok(n) => n,
|
||||
};
|
||||
if handler_write.write_all(&buf[..n]).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let _ = handler_write.shutdown().await;
|
||||
});
|
||||
|
||||
let h2c = tokio::spawn(async move {
|
||||
let mut buf = vec![0u8; 65536];
|
||||
loop {
|
||||
let n = match handler_read.read(&mut buf).await {
|
||||
Ok(0) | Err(_) => break,
|
||||
Ok(n) => n,
|
||||
};
|
||||
if client_write.write_all(&buf[..n]).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let _ = client_write.shutdown().await;
|
||||
});
|
||||
|
||||
let _ = tokio::join!(c2h, h2c);
|
||||
|
||||
debug!("Relay connection {} completed", connection_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Simple base64 encoding without external dependency.
|
||||
fn base64_encode(data: &[u8]) -> String {
|
||||
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
let mut result = String::new();
|
||||
for chunk in data.chunks(3) {
|
||||
let b0 = chunk[0] as u32;
|
||||
let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
|
||||
let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
|
||||
let n = (b0 << 16) | (b1 << 8) | b2;
|
||||
result.push(CHARS[((n >> 18) & 0x3F) as usize] as char);
|
||||
result.push(CHARS[((n >> 12) & 0x3F) as usize] as char);
|
||||
if chunk.len() > 1 {
|
||||
result.push(CHARS[((n >> 6) & 0x3F) as usize] as char);
|
||||
} else {
|
||||
result.push('=');
|
||||
}
|
||||
if chunk.len() > 2 {
|
||||
result.push(CHARS[(n & 0x3F) as usize] as char);
|
||||
} else {
|
||||
result.push('=');
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
//! Note: The actual relay logic lives in `tcp_listener::relay_to_socket_handler()`
|
||||
//! which has proper timeouts, cancellation, and metrics integration.
|
||||
|
||||
@@ -182,6 +182,7 @@ impl TcpListenerManager {
|
||||
http_proxy_svc.set_backend_tls_config_alpn(tls_handler::shared_backend_tls_config_alpn());
|
||||
http_proxy_svc.set_connection_timeouts(
|
||||
std::time::Duration::from_millis(conn_config.socket_timeout_ms),
|
||||
std::time::Duration::from_millis(conn_config.max_connection_lifetime_ms),
|
||||
std::time::Duration::from_millis(conn_config.socket_timeout_ms),
|
||||
std::time::Duration::from_millis(conn_config.max_connection_lifetime_ms),
|
||||
);
|
||||
@@ -220,6 +221,7 @@ impl TcpListenerManager {
|
||||
http_proxy_svc.set_backend_tls_config_alpn(tls_handler::shared_backend_tls_config_alpn());
|
||||
http_proxy_svc.set_connection_timeouts(
|
||||
std::time::Duration::from_millis(conn_config.socket_timeout_ms),
|
||||
std::time::Duration::from_millis(conn_config.max_connection_lifetime_ms),
|
||||
std::time::Duration::from_millis(conn_config.socket_timeout_ms),
|
||||
std::time::Duration::from_millis(conn_config.max_connection_lifetime_ms),
|
||||
);
|
||||
@@ -263,6 +265,7 @@ impl TcpListenerManager {
|
||||
http_proxy_svc.set_backend_tls_config_alpn(tls_handler::shared_backend_tls_config_alpn());
|
||||
http_proxy_svc.set_connection_timeouts(
|
||||
std::time::Duration::from_millis(config.socket_timeout_ms),
|
||||
std::time::Duration::from_millis(config.max_connection_lifetime_ms),
|
||||
std::time::Duration::from_millis(config.socket_timeout_ms),
|
||||
std::time::Duration::from_millis(config.max_connection_lifetime_ms),
|
||||
);
|
||||
|
||||
@@ -363,6 +363,7 @@ impl RustProxy {
|
||||
// Start the throughput sampling task with cooperative cancellation
|
||||
let metrics = Arc::clone(&self.metrics);
|
||||
let conn_tracker = self.listener_manager.as_ref().unwrap().conn_tracker().clone();
|
||||
let http_proxy = self.listener_manager.as_ref().unwrap().http_proxy().clone();
|
||||
let interval_ms = self.options.metrics.as_ref()
|
||||
.and_then(|m| m.sample_interval_ms)
|
||||
.unwrap_or(1000);
|
||||
@@ -378,6 +379,9 @@ impl RustProxy {
|
||||
metrics.sample_all();
|
||||
// Periodically clean up stale rate-limit timestamp entries
|
||||
conn_tracker.cleanup_stale_timestamps();
|
||||
// Clean up expired rate limiter entries to prevent unbounded
|
||||
// growth from unique IPs after traffic stops
|
||||
http_proxy.cleanup_all_rate_limiters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartproxy',
|
||||
version: '26.2.0',
|
||||
version: '26.2.4',
|
||||
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ interface IDatagramRelayMessage {
|
||||
* - TS→Rust: { type: "reply", sourceIp, sourcePort, destPort, payloadBase64 }
|
||||
*/
|
||||
export class DatagramHandlerServer {
|
||||
private static readonly MAX_BUFFER_SIZE = 50 * 1024 * 1024; // 50 MB
|
||||
|
||||
private server: plugins.net.Server | null = null;
|
||||
private connection: plugins.net.Socket | null = null;
|
||||
private socketPath: string;
|
||||
@@ -100,6 +102,11 @@ export class DatagramHandlerServer {
|
||||
|
||||
socket.on('data', (chunk: Buffer) => {
|
||||
this.readBuffer = Buffer.concat([this.readBuffer, chunk]);
|
||||
if (this.readBuffer.length > DatagramHandlerServer.MAX_BUFFER_SIZE) {
|
||||
logger.log('error', `DatagramHandlerServer: buffer exceeded ${DatagramHandlerServer.MAX_BUFFER_SIZE} bytes, resetting`);
|
||||
this.readBuffer = Buffer.alloc(0);
|
||||
return;
|
||||
}
|
||||
this.processFrames();
|
||||
});
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
||||
}
|
||||
|
||||
// Handle unexpected exit (only emits error if not intentionally stopping)
|
||||
this.bridge.removeAllListeners('exit');
|
||||
this.bridge.on('exit', (code: number | null, signal: string | null) => {
|
||||
if (this.stopping) return;
|
||||
logger.log('error', `RustProxy exited unexpectedly (code=${code}, signal=${signal})`, { component: 'smart-proxy' });
|
||||
|
||||
@@ -69,14 +69,15 @@ export function createOffsetPortMappingRoute(options: {
|
||||
priority?: number;
|
||||
[key: string]: any;
|
||||
}): IRouteConfig {
|
||||
const { ports, targetHost, offset, name, domains, priority, ...rest } = options;
|
||||
return createPortMappingRoute({
|
||||
sourcePortRange: options.ports,
|
||||
targetHost: options.targetHost,
|
||||
portMapper: (context) => context.port + options.offset,
|
||||
name: options.name || `Offset Mapping (${options.offset > 0 ? '+' : ''}${options.offset}) for ${options.domains || 'all domains'}`,
|
||||
domains: options.domains,
|
||||
priority: options.priority,
|
||||
...options
|
||||
sourcePortRange: ports,
|
||||
targetHost,
|
||||
portMapper: (context) => context.port + offset,
|
||||
name: name || `Offset Mapping (${offset > 0 ? '+' : ''}${offset}) for ${domains || 'all domains'}`,
|
||||
domains,
|
||||
priority,
|
||||
...rest
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +258,9 @@ export class RouteValidator {
|
||||
errorMap.set(route.name, existingErrors);
|
||||
valid = false;
|
||||
}
|
||||
routeNames.add(route.name);
|
||||
if (route.name) {
|
||||
routeNames.add(route.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate each route
|
||||
@@ -328,7 +330,7 @@ export class RouteValidator {
|
||||
if (catchAllRoutes.length > 1) {
|
||||
for (const route of catchAllRoutes) {
|
||||
conflicts.push({
|
||||
route: route.name,
|
||||
route: route.name || 'unnamed',
|
||||
message: `Multiple catch-all routes on port ${port}`
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {}
|
||||
"ignoreDeprecations": "6.0"
|
||||
},
|
||||
"exclude": [
|
||||
"dist_*/**/*.d.ts"
|
||||
|
||||
Reference in New Issue
Block a user