Esempio n. 1
0
        public static async Task <TResult> Call <TResult>(this IHttpService service,
                                                          string remoteAddress,
                                                          string contract,
                                                          object shardKey,
                                                          Func <IHttpTransport, CancellationToken?, Task <TResult> > body,
                                                          CancellationToken?cancellation = null,
                                                          string network = null,
                                                          string binding = null)
        {
            body.NonNull(nameof(body));
            var assignments = service.NonNull(nameof(service))
                              .GetEndpointsForCall(remoteAddress, contract, shardKey, network, binding);

            var tries = 1;
            List <Exception> errors = null;

            foreach (var assigned in assignments)
            {
                if (!assigned.Endpoint.IsAvailable)
                {
                    continue;                        //offline or Circuit tripped  todo:  instrument
                }
                var ep = assigned.Endpoint as IEndpointImplementation;

                var transport = service.AcquireTransport(assigned);
                try
                {
                    var http   = (transport as IHttpTransport).NonNull("Implementation error: cast to IHttpTransport");
                    var result = await body(http, cancellation);

                    CallGuardException.Protect(ep, _ => _.NotifyCallSuccess(transport));
                    return(result);
                }
                catch (Exception error)
                {
                    //Implementation error
                    if (error is CallGuardException)
                    {
                        throw;
                    }

                    //TaskCanceledException gets thrown on simple timeout even when cancellation was NOT requested
                    if (error is TaskCanceledException && cancellation.HasValue && cancellation.Value.IsCancellationRequested)
                    {
                        throw;
                    }

                    var errorClass = ep.NotifyCallError(transport, error);
                    //todo instrument

                    if (errorClass == CallErrorClass.ServiceLogic)
                    {
                        throw;                                 //throw logical errors
                    }
                    if (errors == null)
                    {
                        errors = new List <Exception>();
                    }
                    errors.Add(error);
                }
                finally
                {
                    service.ReleaseTransport(transport);
                }

                tries++;
            }//foreach

            throw new ClientException("Call eventually failed; {0} endpoints tried; See .InnerException".Args(tries),
                                      errors != null ? new AggregateException(errors) :
                                      new AggregateException("No inner errors"));           //todo LOG etc...
        }