{"id":15636422,"url":"https://github.com/xljiulang/cyarp","last_synced_at":"2025-04-06T16:11:10.972Z","repository":{"id":228392559,"uuid":"773870405","full_name":"xljiulang/CYarp","owner":"xljiulang","description":"A reverse proxy toolkit to help you expose multiple local http servers behind a NAT or firewall to the internet","archived":false,"fork":false,"pushed_at":"2025-03-24T05:19:41.000Z","size":14943,"stargazers_count":133,"open_issues_count":2,"forks_count":25,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-30T15:08:46.139Z","etag":null,"topics":["cyarp","expose","firewall","frp","http","http-proxy","middleware","nat","reverse-proxy","tcp","tunnel","yarp"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xljiulang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-18T14:43:57.000Z","updated_at":"2025-03-24T05:19:45.000Z","dependencies_parsed_at":"2024-03-18T15:45:55.067Z","dependency_job_id":"796664b3-5fa9-40ee-89cd-ee5547380837","html_url":"https://github.com/xljiulang/CYarp","commit_stats":{"total_commits":171,"total_committers":1,"mean_commits":171.0,"dds":0.0,"last_synced_commit":"fcadcb323754bf11ea4b782f56b4d73511f9bcdc"},"previous_names":["xljiulang/cyarp"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xljiulang%2FCYarp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xljiulang%2FCYarp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xljiulang%2FCYarp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xljiulang%2FCYarp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xljiulang","download_url":"https://codeload.github.com/xljiulang/CYarp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247509221,"owners_count":20950232,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cyarp","expose","firewall","frp","http","http-proxy","middleware","nat","reverse-proxy","tcp","tunnel","yarp"],"created_at":"2024-10-03T11:03:49.338Z","updated_at":"2025-04-06T16:11:10.946Z","avatar_url":"https://github.com/xljiulang.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[README](README.md) | [中文文档](README_zh.md)\n\n## CYarp\nA reverse proxy toolkit to help you expose multiple local http servers behind a NAT or firewall to the internet. It currently supports four connection methods: `HTTP/1.1 Upgrade`, `HTTP/2 Extended CONNECT`, `WebSocket` and `WebSocket over Http/2`.\n\n### Features\n1. Use high-performance [kestrel](https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-8.0) as server\n2. Use high-performance [YARP](https://github.com/microsoft/reverse-proxy) for http forwarding\n3. Designed as middleware for asp.netcore\n4. Open server-client interaction protocol\n5. Provides .NET, C/C++ client libraries\n\n### Network structure\n\n![net](images/net.png)\n\n\n### Apache Bench\n\nNginx, CYarp and frp_0.56.0 are deployed simultaneously on an Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz CentOS Linux 7 (Core) system machine. The ab tool for testing is on another machine on the LAN. The test sequence is the parameter order from top to bottom of the table.\n\n#### ab -c 1 -n 10000\n| Product          | Requests per second | Rps Ratio | P95 | P99 |\n| ---------------- | ------------------- | --------- | --- | --- |\n| ab-\u003enginx        | 1539.22             | 1.00      | 1   | 1   |\n| ab-\u003ecyarp-\u003enginx | 700.31              | 0.45      | 2   | 2   |\n| ab-\u003efrp-\u003enginx   | 593.76              | 0.39      | 2   | 2   |\n\n#### ab -c 10 -n 50000\n| Product          | Requests per second | Rps Ratio | P95 | P99 |\n| ---------------- | ------------------- | --------- | --- | --- |\n| ab-\u003enginx        | 9915.55             | 1.00      | 3   | 4   |\n| ab-\u003ecyarp-\u003enginx | 9563.64             | 0.96      | 1   | 2   |\n| ab-\u003efrp-\u003enginx   | 5980.79             | 0.60      | 3   | 4   |\n\n#### ab -c 20 -n 100000\n| Product          | Requests per second | Rps Ratio | P95 | P99 |\n| ---------------- | ------------------- | --------- | --- | --- |\n| ab-\u003enginx        | 11948.84            | 1.00      | 4   | 7   |\n| ab-\u003ecyarp-\u003enginx | 12542.54            | 1.05      | 3   | 3   |\n| ab-\u003efrp-\u003enginx   | 6238.09             | 0.52      | 5   | 7   |\n\n#### ab -c 50 -n 200000\n| Product          | Requests per second | Rps Ratio | P95 | P99 |\n| ---------------- | ------------------- | --------- | --- | --- |\n| ab-\u003enginx        | 12801.34            | 1.00      | 6   | 12  |\n| ab-\u003ecyarp-\u003enginx | 13472.69            | 1.05      | 6   | 7   |\n| ab-\u003efrp-\u003enginx   | 5675.19             | 0.44      | 20  | 49  |\n\n#### ab -c 100 -n 500000\n| Product          | Requests per second | Rps Ratio | P95 | P99 |\n| ---------------- | ------------------- | --------- | --- | --- |\n| ab-\u003enginx        | 14088.43            | 1.00      | 10  | 17  |\n| ab-\u003ecyarp-\u003enginx | 14216.45            | 1.01      | 10  | 12  |\n| ab-\u003efrp-\u003enginx   | 6504.36             | 0.46      | 20  | 49  |\n\n###  Demo and experience\n\n1. Run the `Host/CYarpServer` project\n2. Run the `Host/CYarpClient` project\n3. When PostMan requests `http://localhost`, it receives `401` authorization failed\n4. Add PostMan's Auth, select Bearer Token, and use the following test token to request\n\n\u003e test token\n\n```\neyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJNb2JpbGUiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiJNb2JpbGUwMDEiLCJDbGllbnRJZCI6IkNsaWVudDAwMSIsImlhdCI6MTcxMDgxNjQ1MiwibmJmIjoxNzEwODE2NDUyLCJleHAiOjI3MTA5MDI4NTJ9.aC-9pVDvyhXsUub-wzZVttfc34wMtFrARDlUj3BYNFhy3Axr0U93CV_QFUP-m6DYI6gK0HkxUr6xlkWwItIFzvS95TsoMXOARVXlVQEP18_wQRQ0G3WRMmNJ_uElJ4uIcrha_Dr4e0cp38olHdABQgOXZgUNHFAHCY3rqtn6-gyTaTu6qAgoj2imi4tsOYFF_OPrCNkRoJavubzDTTXRB95cGz5kxzTSDdWCuIaktNsWN7WDK864VKyVgwca6ueQJogidvES_x26TZuLF6VNhYEkM6UjUZtT8WiD3nBhi2_dVS7BODMLfSyiFa68k1NK50DDfnYgiFU6Clb24Ra-2A\n```\n\n### Development Guide\n#### Server side\n\nThe [CYarp.Server](https://www.nuget.org/packages/CYarp.Server/) package is designed as an http middleware for asp.net core. By default, it relies on the `Authentication` middleware to verify the IClient connection. Use the following methods to register and configure the middleware.\n\n```c#\nbuilder.Services.AddAuthentication(\u003cDefaultScheme\u003e).AddYourScheme();\nbuilder.Services.AddCYarp().Configure(cyarp=\u003e{ ... });\n\nvar app = builder.Build();\napp.UseAuthentication();\napp.UseCYarp();\n...\n// app.UseAuthorization();\n// app.MapControllers();\napp.Run();\n```\nAuthentication and authorization when connecting to IClient can be skipped using the following method\n```c#\nbuilder.Services.AddCYarp().Configure(cyarp=\u003e{ ... });\n\nvar app = builder.Build();\napp.UseCYarp().AllowAnonymous();\n... \n```\n\nFinally, handle the http forwarding in the Controller, endpoint handler or the last custom middleware.\n```c#\n// Authorization verification of the requester, here the role is verified\n[Authorize(Roles = \"Mobile\")]\npublic class CYarpController : ControllerBase\n{ \n    private static readonly string clientIdClaimType = \"ClientId\";\n\n    /// \u003csummary\u003e\n    /// Handle cyarp\n    /// The core operation is to get the clientId from the request context\n    /// Then use clientId to get IClient from IClientViewer service to forward HttpContext\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"clientViewer\"\u003e\u003c/param\u003e\n    /// \u003creturns\u003e\u003c/returns\u003e\n    [Route(\"/{**cyarp}\")]\n    public async Task InvokeAsync([FromServices] IClientViewer clientViewer)\n    {\n        var clientId = this.User.FindFirstValue(clientIdClaimType);\n        if (clientId != null \u0026\u0026 clientViewer.TryGetValue(clientId, out var client))\n        {\n            this.Request.Headers.Remove(HeaderNames.Authorization);\n            await client.ForwardHttpAsync(this.HttpContext);\n        }\n        else\n        {\n            this.Response.StatusCode = StatusCodes.Status502BadGateway;\n        }\n    }\n}\n```\n\n#### Client side\n\nUsing the [CYarp.Client](https://www.nuget.org/packages/CYarp.Client/) package, you can develop the client by `.NET` as follows\n```c#\nvar options = this.clientOptions.CurrentValue;\nusing var client = new CYarpClient(options);\nawait client.TransportAsync(stoppingToken);\n```\n\nFor C and C++ clients, you can [AOT compile](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net8plus%2Cwindows) the source code of the `CYarp.Client` project into a dynamic shared library exported from C for use. The [Host/CYarpClient.Native](https://github.com/xljiulang/CYarp/blob/master/Host/CYarpClient.Native) project is a C and C++ client demo, and you need to run `Host/CYarpServer` first for the server.\n\nThe following are the dynamic shared library commands exported by AOT compilation into C for the CYarp.Client project\n\n| Platform and architecture | command                                                     |\n| ------------------------- | ----------------------------------------------------------- |\n| win-x64                   | dotnet publish -c Release /p:PublishAot=true -r win-x64     |\n| linux-x64                 | dotnet publish -c Release /p:PublishAot=true -r linux-x64   |\n| linux-arm64               | dotnet publish -c Release /p:PublishAot=true -r linux-arm64 |\n\n\n### CYarp protocol\n#### Interaction process\n![cyarp](images/cyarp.png)\n\n\n#### Establish a long connection\n\u003e by HTTP/1.1\n\nClient initiates the following request\n```\nGet / HTTP/1.1\nConnection: Upgrade\nHost: {host}\nUpgrade: CYarp\nAuthorization：{Client identity information}\nCYarp-TargetUri: {Access Uri of target httpServer}\n```\n\nIf the server authentication passes, it will respond with a `101` status code, and if the identity authentication fails, it will respond with a `401` status code. In addition, the response may also carry the Set-Cookie response header.\n\n```\nHTTP/1.1 101 Switching Protocols\nConnection: Upgrade\nSet-Cookie: \u003cload balancer cookie\u003e\n```\n\nAt this time, the long connection based on `tcp` has been completed. Then the Stream following the long connection must implement the functions of the following table, where {tunnelId} is a 36-character guid format text, such as `c0248b3a-171c-1e9c-e75c-188daf5e773f`.\n\n| Sender | Content          | Intention                                               | Receiver's actions                   |\n| ------ | ---------------- | ------------------------------------------------------- | ------------------------------------ |\n| Client | `PING\\r\\n`       | Detect server survival                                  | Reply `PONG\\r\\n`                     |\n| Server | `PING\\r\\n`       | Detect client survival                                  | Reply `PONG\\r\\n`                     |\n| Server | `{tunnelId}\\r\\n` | Let the Client to create a new HttpTunnel to the Server | Create HttpTunnel using `{tunnelId}` |\n\n\u003e by HTTP/2\n\nClient initiates the following request\n```\n:authority = {host}\n:method = CONNECT\n:protocol = CYarp\n:scheme = https\n:path = /\nauthorization = {Client identity information}\ncyarp-targeturi = {Access Uri of target httpServer}\n```\n\nIf the server authentication passes, it will respond with a `200` status code, and if the identity authentication fails, it will respond with a `401` status code. In addition, the response may also carry the set-cookie response header.\n```\n:status = 200\nset-cookie = \u003cload balancer cookie\u003e\n```\n\nAt this time, the long connection based on `HTTP/2` has been completed. Then the Stream following the long connection must implement the functions of the following table, where {tunnelId} is a 36-character guid format text, such as `c0248b3a-171c-1e9c-e75c-188daf5e773f`.\n\n| Sender | Content          | Intention                                               | Receiver's actions                   |\n| ------ | ---------------- | ------------------------------------------------------- | ------------------------------------ |\n| Client | `PING\\r\\n`       | Detect server survival                                  | Reply `PONG\\r\\n`                     |\n| Server | `PING\\r\\n`       | Detect client survival                                  | Reply `PONG\\r\\n`                     |\n| Server | `{tunnelId}\\r\\n` | Let the Client to create a new HttpTunnel to the Server | Create HttpTunnel using `{tunnelId}` |\n\n\u003e by WebSocket\n\nWebSocket connection requires the following request header, requesting the `/` path. After the connection is successful, multiple binary frames are used to carry CYarp's Stream.\n| HeaderName             | HeaderValue                       |\n| ---------------------- | --------------------------------- |\n| Authorization          | {Client identity information}     |\n| CYarp-TargetUri        | {Access Uri of target httpServer} |\n| Sec-WebSocket-Protocol | `CYarp`                           |\n\n\n#### Creation of HttpTunnel\n\u003e by HTTP/1.1\n\nClient send the following request\n```\nGet /{tunnelId} HTTP/1.1\nHost: {host}\nConnection: Upgrade\nUpgrade: CYarp\nCookie：\u003cif have Set-Cookie\u003e\n```\n\nIf the server passes the verification {tunnelId}, it will respond with a `101` status code. If the verification fails, it will respond with a `401` status code. In addition, the response may also carry the Set-Cookie response header.\n```\nHTTP/1.1 101 Switching Protocols\nConnection: Upgrade\nSet-Cookie: \u003cload balancer cookie\u003e\n```\n\nAt this time, the creation of the HttpTunnel over `tcp` has been completed, and then the server will send an `HTTP/1.1` request to the client and receive the client's `HTTP/1.1` response in the subsequent Stream.\n\n\u003e by HTTP/2\n\nClient send the following request\n```\n:authority = {host}\n:method = CONNECT\n:protocol = CYarp\n:scheme = https\n:path = /{tunnelId}\ncookie = \u003cif have set-cookie\u003e\n```\n\nIf the server passes the verification {tunnelId}, it will respond with a `200` status code. If the verification fails, it will respond with a `401` status code. In addition, the response may also carry the set-cookie response header.\n```\n:status = 200\nset-cookie = \u003cload balancer cookie\u003e\n```\n\nAt this time, the creation of the HttpTunnel over `HTTP/2` has been completed, and then the server will send an `HTTP/1.1` request to the client and receive the client's `HTTP/1.1` response in the subsequent Stream.\n\n\u003e by WebSocket\n\nWebSocket connection requires the following request header, requesting the `/{tunnelId}` path. After the connection is successful, multiple binary frames are used to carry CYarp's Stream.\n\n| HeaderName             | HeaderValue                       |\n| ---------------------- | --------------------------------- |\n| Sec-WebSocket-Protocol | `CYarp`                           |\n\n\n### Security\nWhen the server side uses https, the following parts are tls secure transmission\n1. The long connection establishment process and the subsequent Stream of the long connection\n2. The creation process of HttpTunnel and its subsequent Stream\n\nIf the TargetUri of the http server is also https, the traffic in HttpTunnel will appear as tls in tls.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxljiulang%2Fcyarp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxljiulang%2Fcyarp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxljiulang%2Fcyarp/lists"}