{"id":24307890,"url":"https://github.com/expecho/servicefabric-remoting-customheaders","last_synced_at":"2025-09-26T13:30:45.636Z","repository":{"id":46051257,"uuid":"138141452","full_name":"Expecho/ServiceFabric-Remoting-CustomHeaders","owner":"Expecho","description":"This package allows injecting custom message headers into remoting messages (Actors and Reliable Services, V2 remoting only) at runtime. The headers are available client side to read. It also provides message interception using BeforeHandleRequestResponseAsync and AfterHandleRequestResponseAsync to act on remoting events.","archived":false,"fork":false,"pushed_at":"2021-12-06T09:47:26.000Z","size":95,"stargazers_count":12,"open_issues_count":1,"forks_count":9,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-12-17T23:46:40.775Z","etag":null,"topics":["azure","azure-service-fabric","reliable-actors","reliable-stateful-service","remoting","service-fabric"],"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/Expecho.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}},"created_at":"2018-06-21T08:24:06.000Z","updated_at":"2022-07-17T04:10:33.000Z","dependencies_parsed_at":"2022-09-02T14:02:19.400Z","dependency_job_id":null,"html_url":"https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Expecho%2FServiceFabric-Remoting-CustomHeaders","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Expecho%2FServiceFabric-Remoting-CustomHeaders/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Expecho%2FServiceFabric-Remoting-CustomHeaders/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Expecho%2FServiceFabric-Remoting-CustomHeaders/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Expecho","download_url":"https://codeload.github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234310910,"owners_count":18812167,"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":["azure","azure-service-fabric","reliable-actors","reliable-stateful-service","remoting","service-fabric"],"created_at":"2025-01-17T04:20:29.703Z","updated_at":"2025-09-26T13:30:45.322Z","avatar_url":"https://github.com/Expecho.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ServiceFabric.Remoting.CustomHeaders\n\nThis package allows injecting custom headers into remoting messages (Actors and Reliable Services, V2 remoting only) at runtime. The headers are available client side to read. \nIt also provides message interception using BeforeHandleRequestResponseAsync and AfterHandleRequestResponseAsync to act on remoting events.\n\nCommon used classes:\n\n- [CustomHeaders](https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/ServiceFabric.Remoting.CustomHeaders/CustomHeaders.cs)\n- [RemotingContext](https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/ServiceFabric.Remoting.CustomHeaders/RemotingContext.cs)\n\n## NuGet\n\n[Download the NuGet package](https://www.nuget.org/packages/ServiceFabric.Remoting.CustomHeaders) [![NuGet Status](http://img.shields.io/nuget/v/ServiceFabric.Remoting.CustomHeaders.svg?style=flat)](https://www.nuget.org/packages/ServiceFabric.Remoting.CustomHeaders/)\n\n## Examples\n\nThis repository includes a Service Fabric application for demonstration purposes. A [Console Application](https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/Demo/Program.cs) is used to access the application and shows the usage of the package.\n\n## Usage scenarios\n\nCustom headers can be used to pass data between the sender and the receiver like tracing information or security context data. Using the BeforeHandleRequestResponseAsync and AfterHandleRequestResponseAsync actions additional logging can be applied monitor the flow between remoting calls.\n\n## How to use\n\n### Prepare Reliable Services\n\nModify the service and create a listener that can handle the requests\n\n```csharp\nprotected override IEnumerable\u003cServiceInstanceListener\u003e CreateServiceInstanceListeners()\n{\n    yield return new ServiceInstanceListener(context =\u003e\n        new FabricTransportServiceRemotingListener(context,\n            new ExtendedServiceRemotingMessageDispatcher(context, this)));\n}\n```\n\n### Prepare Actors\n\nRegister the actor using the `ExtendedActorService` service (usually done in the program.cs file):\n\n```csharp\nActorRuntime.RegisterActorAsync\u003cDemoActor\u003e (\n(context, actorType) =\u003e\n   {\n\t   return new ExtendedActorService(context, actorType);\n   }).GetAwaiter().GetResult();\n```\n### Sender\n\nOn the sender side, create a proxy to the actor or service. The `Create` method accepts an instance of the `CustomHeaders` class:\n\n**Calling Reliable Services**\n\n```csharp\nvar customHeaders = new CustomHeaders\n{\n\t{\"Header1\", DateTime.Now},\n\t{\"Header2\", Guid.NewGuid()}\n};\n\nvar serviceUri = new Uri(\"fabric:/ServiceFabric.Remoting.CustomHeaders.DemoApplication/DemoService\");\nvar proxyFactory = new ServiceProxyFactory(handler =\u003e \n                    new ExtendedServiceRemotingClientFactory(\n                        new FabricTransportServiceRemotingClientFactory(remotingCallbackMessageHandler: handler), customHeaders));\nvar proxy = proxyFactory.CreateServiceProxy\u003cIDemoService\u003e(serviceUri); // or  in case of actors\nvar actorResponse = proxy.SayHelloToActor().GetAwaiter().GetResult();\n```       \n\n**Sending Message to Actors**\n\n```csharp\nvar customHeaders = new CustomHeaders\n{\n\t{\"Header1\", DateTime.Now},\n\t{\"Header2\", Guid.NewGuid()}\n};\n\nvar serviceUri = new Uri(\"fabric:/ServiceFabric.Remoting.CustomHeaders.DemoApplication/DemoService\");\nvar proxyFactory = new ActorProxyFactory(handler =\u003e \n                    new ExtendedServiceRemotingClientFactory(\n\t\t        new FabricTransportActorRemotingClientFactory(handler), customHeaders));\nvar proxy = proxyFactory.CreateActorProxy\u003cIDemoService\u003e(serviceUri); \nvar response = proxy.SayHello().GetAwaiter().GetResult();\n```       \n\nThere is an overload of the `Create` method that accepts a `Func\u003cCustomHeaders\u003e`. This is useful in scenarios where the created proxy factory or proxy is reused. Since creating a proxy factory is expensive this is the preferred way if you need dynamic header values. The func is invoked on every request made using the proxy:\n\n```csharp\nvar customHeadersProvider = new Func\u003cCustomHeaders\u003e(() =\u003e new CustomHeaders\n{\n\t{\"Header1\", DateTime.Now},\n\t{\"Header2\", Guid.NewGuid()}\n});\nvar serviceUri = new Uri(\"fabric:/ServiceFabric.Remoting.CustomHeaders.DemoApplication/DemoService\");\nvar proxyFactory = new ServiceProxyFactory(handler =\u003e\n                    new ExtendedServiceRemotingClientFactory(\n                        new FabricTransportServiceRemotingClientFactory(remotingCallbackMessageHandler: handler), customHeadersProvider));\nvar proxy = proxyFactory.CreateServiceProxy\u003cIDemoService\u003e(serviceUri);\n```\n### Receiver\n\nThe receiving service or actor can extract the values in the custom headers using the `RemotingContext` class:\n\n```csharp\npublic async Task\u003cstring\u003e SayHello()\n{\n\tvar remotingContext =\n\t\tstring.Join(\", \", RemotingContext.Keys.Select(k =\u003e $\"{k}: {RemotingContext.GetData(k)}\"));\n\n\tServiceEventSource.Current.ServiceMessage(Context, $\"SayHelloToActor got context: {remotingContext}\");\n\treturn Task.FromResult($\"Got the following message headers: {remotingContext}\")\n}\n```\n\nSample content of remotingContext: \n\n\u003e Header1: 06/24/2018 08:30:18, Header2: 2c95548a-6efd-4855-82eb-29ea827be87b\n\n### Headers passthrough\n\nIn case the headers need to flow from one call to the other `CustomHeaders.FromRemotingContext` can be used as demonstrated:\n\n```csharp\npublic async Task\u003cstring\u003e SayHelloToActor()\n{\n\tvar remotingContext =\n\t\tstring.Join(\", \", RemotingContext.Keys.Select(k =\u003e $\"{k}: {RemotingContext.GetData(k)}\"));\n\n\tServiceEventSource.Current.ServiceMessage(Context, $\"SayHelloToActor got context: {remotingContext}\");\n\tvar proxyFactory = new ActorProxyFactory(handler =\u003e\n                new ExtendedServiceRemotingClientFactory(\n                    new FabricTransportActorRemotingClientFactory(handler), CustomHeaders.FromRemotingContext));\n\tvar proxy = proxyFactory.CreateActorProxy\u003cIDemoActor\u003e(new ActorId(1));\n\tvar response = await proxy.GetGreetingResponseAsync(CancellationToken.None);\n\n\treturn $\"DemoService passed context '{remotingContext}' to actor and got as response: {response}\";\n}\n```\n\nThis removes the need to create a new `CustomHeaders` instance based on the current values in the `RemotingContext`.\n\n## Message interception\n\nMessages can be intercepted on both the sending side and the receiving side. This can be used fo example to log method calls or performance.\n\n### Client-side message interception\n\nOn the receiving side messages can be intercepted using the `BeforeHandleRequestResponseAsync` and `AfterHandleRequestResponseAsync` extension points when creating a service listener:\n\n**For services**\n\n```csharp\nprotected override IEnumerable\u003cServiceInstanceListener\u003e CreateServiceInstanceListeners()\n{\n\tyield return new ServiceInstanceListener(context =\u003e\n\t\tnew FabricTransportServiceRemotingListener(context,\n\t\t\tnew ExtendedServiceRemotingMessageDispatcher(context, this)\n\t\t\t{\n\t\t\t\t// Optional, log the call before being handled\n\t\t\t\tBeforeHandleRequestResponseAsync = requestInfo =\u003e\n\t\t\t\t{\n\t\t\t\t\tvar sw = new Stopwatch();\n\t\t\t\t\tsw.Start();\n\t\t\t\t\tServiceEventSource.Current.ServiceMessage(Context, $\"BeforeHandleRequestResponseAsync {requestInfo.Service} {requestInfo.Method}\");\n\t\t\t\t\treturn Task.FromResult\u003cobject\u003e(sw);\n\t\t\t\t},\n\t\t\t\t// Optional, log the call after being handled\n\t\t\t\tAfterHandleRequestResponseAsync = responseInfo =\u003e\n\t\t\t\t{\n\t\t\t\t\tvar sw = (Stopwatch) responseInfo.State;\n\t\t\t\t\tServiceEventSource.Current.ServiceMessage(Context, $\"AfterHandleRequestResponseAsync {responseInfo.Service} {responseInfo.Method} took {sw.ElapsedMilliseconds}ms\");\n\t\t\t\t\treturn Task.CompletedTask;\n\t\t\t\t}\n\t\t\t}));\n}\n```` \n\n**For actors**\n\n```csharp\nActorRuntime.RegisterActorAsync\u003cDemoActor\u003e (\n(context, actorType) =\u003e\n   {\n\t   var service = new ExtendedActorService(context, actorType)\n\t   {\n\t\t   // Optional, allows call interception. Executed before the response is handled\n\t\t   BeforeHandleRequestResponseAsync = requestInfo =\u003e\n\t\t   {\n\t\t\t   ActorEventSource.Current.Message($\"BeforeHandleRequestResponseAsync {requestInfo.ActorService} {requestInfo.Method} for actor {requestInfo.ActorId.ToString()}\");\n\t\t\t   return Task.CompletedTask;\n\t\t   },\n\t\t   // Optional, allows call interception. Executed after the response is handled\n\t\t   AfterHandleRequestResponseAsync = responseInfo =\u003e\n\t\t   {\n\t\t\t   ActorEventSource.Current.Message($\"AfterHandleRequestResponseAsync {responseInfo.ActorService} {responseInfo.Method} for actor {responseInfo.ActorId.ToString()}\");\n\t\t\t   return Task.CompletedTask;\n\t\t   }\n\t   };\n\t   return service;\n   }).GetAwaiter().GetResult();\n```\n\n### Server-side message interception\n\nOn the sending side messages can be intercepted using the `BeforeSendRequestResponseAsync` and `AfterSendRequestResponseAsync` extension points when creating the `ExtendedServiceRemotingClientFactory` on constructor of the `ServiceProxyFactory`:\n\n```csharp\nvar proxyFactory = new ServiceProxyFactory(handler =\u003e // or ActorProxyFactory in case of actors\n        new ExtendedServiceRemotingClientFactory(\n            new FabricTransportServiceRemotingClientFactory(remotingCallbackMessageHandler: handler), customHeadersProvider)\n        {\n            // Optional, log the call before being handled\n            BeforeSendRequestResponseAsync = requestInfo =\u003e\n            {\n                var sw = new Stopwatch();\n                sw.Start();\n                Console.WriteLine($\"BeforeSendRequestResponseAsync {requestInfo.Method}\");\n                return Task.FromResult\u003cobject\u003e(sw);\n            },\n            // Optional, log the call after being handled\n            AfterSendRequestResponseAsync = responseInfo =\u003e\n            {\n                var sw = (Stopwatch)responseInfo.State;\n                Console.WriteLine($\"AfterSendRequestResponseAsync {responseInfo.Method} took {sw.ElapsedMilliseconds}ms\");\n                return Task.CompletedTask;\n            }\n        });\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexpecho%2Fservicefabric-remoting-customheaders","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexpecho%2Fservicefabric-remoting-customheaders","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexpecho%2Fservicefabric-remoting-customheaders/lists"}