QuickProxyNet is a high-performance, zero-dependency C# library for connecting to servers through proxy protocols. It provides direct Stream access with minimal allocations and latency — ideal for mass proxy checking, crawlers, and any scenario where thousands of proxy connections are made in parallel.
Targets: .NET 8 / .NET 9 / .NET 10 / .NET 11
- Zero runtime dependencies — BCL only, no third-party packages
- Zero-alloc protocol logic —
ArrayPool,stackalloc,Utf8Formatter,ValueTaskthroughout - 5 classic proxy protocols — HTTP, HTTPS, SOCKS4, SOCKS4a, SOCKS5
- 4 VPN-style protocols — VLESS, VMess (VMessAEAD), Trojan, over
tcp,wsorhttpupgrade; Shadowsocks AEAD overtcp - VLESS REALITY in-process — no external binary: a managed TLS 1.3 client (ClientHello, X25519, key schedule, record layer) lives in the core package, and
VlessClientuses it automatically whensecurity=reality - XTLS
xtls-rprx-vision— the flow used by ~95% of real-world REALITY nodes - Share-link parsing — pass a
vless://,vmess://,trojan://,ss://,socks5://,http://, … string directly; noUrigymnastics - Static one-liner API —
Proxy.ConnectAsync(link, host, port)for mass checkers - Structured error codes —
ProxyProtocolExceptionwithProxyErrorCodeenum for programmatic error handling - Timeout support — per-connection timeouts with
ProxyErrorCode.Timeout - Raw Stream access — full control over the tunneled connection
dotnet add package QuickProxyNet
Proxy.ConnectAsync and Proxy.Create accept the link as a string and dispatch on the scheme themselves. This matters for vmess:// links: they are base64-encoded JSON, and System.Uri rejects most real-world ones (host length limit, base64 padding). You no longer have to inspect the scheme yourself to pick a parser.
// Works for http/https/socks4/socks4a/socks5/vless/trojan/vmess/ss links
await using var stream = await Proxy.ConnectAsync(
"socks5://user:pass@127.0.0.1:1080",
"example.com", 443,
TimeSpan.FromSeconds(5));// A VLESS REALITY share link — handled in-process, no Xray required
await using var stream = await Proxy.ConnectAsync(
"vless://uuid@1.2.3.4:443?security=reality&pbk=...&sni=www.example.com&flow=xtls-rprx-vision",
"example.com", 443,
TimeSpan.FromSeconds(10));var client = Proxy.Create("socks5://proxy:1080");
client.NoDelay = true;
await using var stream = await client.ConnectAsync("example.com", 443);IProxyClient.ConnectAsync and Proxy.ConnectAsync(link, ...) also take the target as an EndPoint, the way Socket.ConnectAsync does: a DnsEndPoint for a name the proxy resolves, or an IPEndPoint. That is what SocketsHttpHandler.ConnectCallback hands over, so an HttpClient goes through any proxy this library speaks in one line:
var proxy = Proxy.Create("vless://...");
using var http = new HttpClient(new SocketsHttpHandler
{
ConnectCallback = (context, ct) => proxy.ConnectAsync(context.DnsEndPoint, ct)
});Proxy.TryCreate never throws, whatever the input. A subscription is other people's text, and one bad line must not stop the run. The error string starts with the exception type, so you can group the refusals.
foreach (var line in File.ReadLines("subscription.txt"))
{
if (!Proxy.TryCreate(line, out var client, out var error))
{
Console.WriteLine($"skipped: {error}");
continue;
}
// client.SourceLink is the original text, with the uuid, sni and transport
}var creds = new NetworkCredential("user", "pass");
var client = Proxy.Create(
ProxyType.Socks5, "proxy.example.com", 1080, creds);
await using var stream = await client.ConnectAsync("example.com", 80,
TimeSpan.FromSeconds(10));REALITY support is implemented in managed code inside the core package (QuickProxyNet/Internal/Reality/): a hand-written TLS 1.3 client — ClientHello construction, X25519 key exchange, the HKDF key schedule, and the record layer. There is no external process and no extra package; the zero-dependency promise holds. The xtls-rprx-vision flow is supported, including its padding protocol in both directions (VisionStream).
Honest limitation: the ClientHello is not yet a real browser fingerprint. There is no GREASE, no padding extension, the extension order does not match Chrome, and key_share offers bare X25519 where Chrome sends X25519MLKEM768. This does not prevent connecting or carrying traffic — verified against live servers — but DPI that fingerprints ClientHellos can tell it apart from a browser. See docs/reality-fingerprint-plan.md and the TlsClientHello class comment for the byte-level details and the plan.
Also not implemented: Vision's TLS-in-TLS splice. It is a throughput optimization and does not affect the wire format.
| Protocol | Status | Notes |
|---|---|---|
HTTP / HTTPS CONNECT |
Supported | optional basic auth |
| SOCKS4 / SOCKS4a / SOCKS5 | Supported | SOCKS5 with optional username/password auth |
| VLESS | Supported | security=none, tls, reality; flow xtls-rprx-vision |
| Trojan | Supported | over TLS |
| VMess | Supported | VMessAEAD, alterId=0, optional TLS |
| Shadowsocks | Supported | AEAD (SIP004/SIP007) over tcp: aes-128-gcm, aes-192-gcm, aes-256-gcm, chacha20-ietf-poly1305 |
| Hysteria2 / TUIC | Not supported | QUIC-based; the library has no datagram model |
ss:// links in both grammars are accepted — the legacy ss://base64(method:password@host:port)#tag
and SIP002 ss://userinfo@host:port/?plugin=…#tag with base64 or plain method:password userinfo.
The four AEAD ciphers above are spoken over raw TCP. Everything else is refused by name with a
NotSupportedException before a byte is written, never silently downgraded: the AEAD-2022
2022-blake3-* family (SIP022, needs BLAKE3), every legacy stream cipher (rc4-md5, aes-*-cfb,
chacha20-ietf, …), none/plain, xchacha20-ietf-poly1305 (no XChaCha20 in the .NET BCL),
and any plugin= (SIP003 plugins are separate processes). No UDP, no ws/TLS transport for
Shadowsocks, no SIP008 JSON subscriptions. chacha20-ietf-poly1305 needs an OS with
ChaCha20-Poly1305 — Windows 11 / Server 2022, not Windows 10. A wrong password is not reported as
one: the server sends nothing, so it looks exactly like a dead target (see ShadowsocksClient).
| Transport | Status |
|---|---|
tcp / raw |
Supported |
ws / websocket |
Supported |
httpupgrade |
Supported |
grpc |
Not supported — needs an HTTP/2 layer |
xhttp |
Not supported — needs HTTP/2/3 |
| Status | |
|---|---|
security=none |
Supported |
security=tls |
Supported — SslStream |
security=reality |
Supported — managed TLS 1.3, no external binary |
flow empty |
Supported |
flow=xtls-rprx-vision |
Supported — padding protocol both ways; TLS-in-TLS splice not implemented |
flow=xtls-rprx-vision-udp443 |
Not supported |
| Browser-grade ClientHello fingerprint | Not implemented — see the REALITY section above |
Measured by this library's own parsers over a corpus of 20 228 share links (snapshot of 2026-08-21; the list changes daily, so these are proportions, not constants).
| Share of corpus | Status | |
|---|---|---|
Plain VLESS / VMess / Trojan (tcp, ws, httpupgrade) |
41% | Works |
VLESS REALITY, incl. xtls-rprx-vision |
46% | Works |
grpc transport |
6.0% | Not supported |
| Hysteria2 | 2.9% | Not supported |
xhttp transport |
2.9% | Not supported |
xtls-rprx-vision-udp443 flow |
0.1% | Not supported |
UDP is not supported as a class: the whole library is built around ConnectAsync(...) -> Stream.
There is no companion package and no optional binary. An earlier QuickProxyNet.Reality package
drove a child Xray process to reach REALITY; it was removed once the managed implementation was
verified against live servers. What it also covered — grpc, xhttp, a real uTLS fingerprint —
is listed above as unsupported rather than quietly delegated.
All proxy protocol errors throw ProxyProtocolException with a specific ProxyErrorCode:
try
{
await using var stream = await Proxy.ConnectAsync(proxyLink, host, port,
TimeSpan.FromSeconds(5));
}
catch (ProxyProtocolException ex)
{
switch (ex.ErrorCode)
{
case ProxyErrorCode.Timeout:
// Connection timed out
break;
case ProxyErrorCode.ConnectionFailed:
// Could not reach the proxy (includes proxy host:port in message)
break;
case ProxyErrorCode.AuthRequired:
// Proxy requires credentials (HTTP 407)
break;
case ProxyErrorCode.AuthFailed:
// Wrong username/password; for REALITY, the server did not recognise our
// pbk/sid and relayed us to the decoy site; for VMess, the response was
// sealed under keys that are not ours
break;
case ProxyErrorCode.InvalidResponse:
// Proxy returned garbage
break;
}
// ex.Message includes proxy host:port and target host:port
// ex.InnerException contains the original SocketException/IOException
}The VPN-style protocols use the same type and codes. RealityHandshakeException is a
ProxyProtocolException, so the catch above sees it. Two rejections that protocols express
by simply closing the connection — a VLESS or VMess server that does not know the id — come
back as ConnectionFailed with a message naming the protocol and what to check (for VMess:
the id, a non-zero alterId on the server, and a clock more than ~2 minutes off). Error
messages never contain the credential: a malformed user id is reported by length, not value.
| Code | Description |
|---|---|
Timeout |
Connection timed out |
ConnectionFailed |
Failed to connect to the proxy server |
AuthRequired |
Proxy requires authentication (HTTP 407) |
AuthFailed |
Authentication credentials rejected |
InvalidResponse |
Proxy returned an invalid/unparseable response |
SocksUnexpectedVersion |
SOCKS protocol version mismatch |
SocksNoAuthMethod |
No suitable SOCKS5 auth method |
SocksBadAddressType |
Unknown SOCKS5 address type |
SocksIPv6NotSupported |
SOCKS4 does not support IPv6 |
SocksNoIPv4Address |
Failed to resolve host to IPv4 (SOCKS4) |
SocksStringTooLong |
SOCKS field exceeded 255-byte limit |
StringTooLong |
A protocol string field, such as a target host name, exceeded the 255-byte limit |
TransportUpgradeFailed |
The server refused the ws or httpupgrade upgrade, or did not answer with a WebSocket handshake |
TlsHandshakeFailed |
The TLS handshake with the proxy failed: an untrusted or expired certificate, a name the server does not serve, or no shared cipher suite |
| Type | Protocol | Auth | DNS through proxy |
|---|---|---|---|
Http |
HTTP CONNECT | Basic | No |
Https |
HTTPS CONNECT + TLS | Basic | No |
Socks4 |
SOCKS4 | UserId | No (resolved locally) |
Socks4a |
SOCKS4a | UserId | Yes |
Socks5 |
SOCKS5 (RFC 1928) | Username/Password (RFC 1929) | Yes |
Vless |
VLESS (incl. REALITY, xtls-rprx-vision) |
UUID | Yes |
Vmess |
VMess (VMessAEAD) | UUID | Yes |
Trojan |
Trojan | Password | Yes |
Shadowsocks |
Shadowsocks AEAD | Password + cipher | Yes |
When using the factory/client API, each client supports:
| Property | Default | Description |
|---|---|---|
NoDelay |
true |
Disable Nagle algorithm |
LingerState |
Linger(true, 0) |
Socket linger on close |
LocalEndPoint |
null |
Bind to specific local IP |
There is no read or write timeout on the client. The TimeSpan overloads of ConnectAsync bound
the connection and the handshake. For reads and writes on the returned stream, pass a
CancellationToken to the async calls, or set the stream's own ReadTimeout / WriteTimeout
when its CanTimeout is true.
MIT