14

I am wondering it is possible to inject custom message header to outgoing request to carry additional information without deserialize the payload to fullfill the functionality like authentication, validation or correlation of request like wcf provided by means of messagesinspector?

Ohad Schneider
  • 36,600
  • 15
  • 168
  • 198
Xiangdong
  • 139
  • 1
  • 6
  • I created a [NuGet package](https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders) that you can easily add to your project to add custom headers and message interception. The code is public available if you want to take a look. – Peter Bons Jul 02 '18 at 09:34

2 Answers2

21

Update

With SDK v2 you can now (relatively) easily modify the headers of both Reliable Services and Actors. Note in the examples below some wrapper members were omitted for brevity.

Client

We use ServiceProxyFactory to create proxies instead of the static ServiceProxy. Then we can wrap IServiceRemotingClientFactory and IServiceRemotingClient and intercept the service calls. The same can be done with ActorProxyFactory. Note this overrides the behavior of attributes such as the WcfServiceRemotingProviderAttribute, since we explicitly specify the client factory ourselves.

_proxyFactory = new ServiceProxyFactory(c => new ServiceRemotingClientFactoryWrapper(
 // we can use any factory here
 new WcfServiceRemotingClientFactory(callbackClient: c)));

    private class ServiceRemotingClientFactoryWrapper : IServiceRemotingClientFactory
    {
        private readonly IServiceRemotingClientFactory _inner;

        public ServiceRemotingClientFactoryWrapper(IServiceRemotingClientFactory inner)
        {
            _inner = inner;
        }

        public async Task<IServiceRemotingClient> GetClientAsync(Uri serviceUri, ServicePartitionKey partitionKey, TargetReplicaSelector targetReplicaSelector,
            string listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken)
        {
            var client = await _inner.GetClientAsync(serviceUri, partitionKey, targetReplicaSelector, listenerName, retrySettings, cancellationToken).ConfigureAwait(false);
            return new ServiceRemotingClientWrapper(client);
        }
    }

    private class ServiceRemotingClientWrapper : IServiceRemotingClient
    {
        private readonly IServiceRemotingClient _inner;

        public ServiceRemotingClientWrapper(IServiceRemotingClient inner)
        {
            _inner = inner;
        }

        public Task<byte[]> RequestResponseAsync(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
        {
            // use messageHeaders.AddHeader() here
            return _inner.RequestResponseAsync(messageHeaders, requestBody);
        }

        public void SendOneWay(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
        {
            // use messageHeaders.AddHeader() here
            _inner.SendOneWay(messageHeaders, requestBody);
        }
    }

Server

Inherit from ServiceRemotingDispatcher and ActorServiceRemotingDispatcher to examine the headers.

class CustomServiceRemotingDispatcher : ServiceRemotingDispatcher
{
    public override async Task<byte[]> RequestResponseAsync(IServiceRemotingRequestContext requestContext, ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
    {
        // read messageHeaders here
        // or alternatively put them in an AsyncLocal<T> scope
        // so they can be accessed down the call chain
        return base.RequestResponseAsync(requestContext, messageHeaders, requestBody);
    }
}

To use this class, again we need to override the ServiceRemotingProviderAttribute by directly creating the communication listener:

class MyService : StatelessService
{
     protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
          yield return new ServiceInstanceListener(context => new WcfServiceRemotingListener(context, new CustomServiceRemotingDispatcher());
     }
}
Eli Arbel
  • 22,391
  • 3
  • 45
  • 71
  • I am going in the same direction, your code example definite help alot. But I still wish Microsoft provides the same messages handler mechanism to enable such use cases. – Xiangdong Dec 11 '15 at 15:17
  • The ServiceInstanceListener does not have constructor parameter for dispatcher in the same way as the WcfServiceRemotingListener. How would one add a dispatcher when using Remoting? – SondreB Apr 22 '16 at 13:32
  • Issue is that ServiceInstanceListener takes ICommunicationListener and not IServiceRemotingMessageHandler, and that interface does not have methods that returns message headers. – SondreB Apr 22 '16 at 13:40
  • I have tried to implement this, but I'm getting: "The provided URI scheme 'localhost' is invalid; expected 'net.tcp'.". I'm creating the proxy like this: `return proxyFactory.CreateServiceProxy( new Uri("fabric:/myapp/myserviceService"), new ServicePartitionKey(0));` – Per Jan 12 '17 at 22:19
  • I wrote up a blog to summarize what EIi laid out here.[link](https://www.zhresearches.com/2017/03/01/azure-service-fabric-correlationid.html). Hope it will help. – Xiangdong Mar 01 '17 at 20:43
0

I asked the same question on the MSDN forum a few weeks ago, however I didn't get a response there.

I looked into the source code of the client library and haven't found a way to add headers. I'm afraid the only way is to add them as part of the method call. This could be done by using request-classes as method parameters and by using inheritance for them. (e.g. RequestBase class with headers [Authorization, ClientInfo, ...]). You then have to make sure these headers are set for every request by wrapping all invocations or by setting it manually.

Further clarification from the Service Fabric team would be greatly appreciated.

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Christian Weiss
  • 1,060
  • 10
  • 12
  • Where id you find the client source code? I checked out the api, there is a ServiceRemotingHeaders class that make me think that there are ways to add custom header to the outgoing request. Furthermore, the client seems to use wcf to perform the communication. if so, it should be feasible to expose messageinspector to developer. – Xiangdong Dec 10 '15 at 14:00
  • @Xiangdong I looked at Microsoft.ServiceFabric.Services.dll with ILSpy. ServiceRemotingHeaders seems to be the way to go, yes. However I haven't found a way to set them from outside. I think ServiceProxy.InvokeAsync() is the place that initiates a request and it creates the ServiceRemotingMessageHeaders-variable internally and passes it to the downstream ServicePartitionClient. But maybe I missed something... – Christian Weiss Dec 11 '15 at 07:44