From 10db534008060343665dd75f391c0fb2691c20ca Mon Sep 17 00:00:00 2001 From: geffzhang Date: Sat, 4 Mar 2017 18:18:00 +0800 Subject: [PATCH 1/3] Refactor HttpClientHttpRequester Cache HttpClient --- .../ServiceCollectionExtensions.cs | 1 + src/Ocelot/Requester/HttpClientBuilder.cs | 104 ++++++++++++++---- .../Requester/HttpClientHttpRequester.cs | 70 +++++++----- src/Ocelot/Requester/IHttpClient.cs | 13 +++ src/Ocelot/Requester/IHttpClientBuilder.cs | 42 +++++++ .../IHttpClientMessageCacheHandler.cs | 16 +++ .../MemoryHttpClientMessageCacheHandler.cs | 46 ++++++++ .../PollyCircuitBreakingDelegatingHandler.cs | 4 +- 8 files changed, 243 insertions(+), 53 deletions(-) create mode 100644 src/Ocelot/Requester/IHttpClient.cs create mode 100644 src/Ocelot/Requester/IHttpClientBuilder.cs create mode 100644 src/Ocelot/Requester/IHttpClientMessageCacheHandler.cs create mode 100644 src/Ocelot/Requester/MemoryHttpClientMessageCacheHandler.cs diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index c076f367..04ca1037 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -135,6 +135,7 @@ namespace Ocelot.DependencyInjection services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index da37ee02..8cad9d49 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -1,46 +1,110 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Net.Http; +using System.Threading.Tasks; using Ocelot.Logging; using Ocelot.Requester.QoS; namespace Ocelot.Requester { - internal class HttpClientBuilder + internal class HttpClientBuilder : IHttpClientBuilder { - private readonly Dictionary> _handlers = new Dictionary>(); + private TimeSpan? _timeout; + private readonly List _handlers = new List(); + private Dictionary _defaultHeaders; + private CookieContainer _cookieContainer; + private IQoSProvider _qoSProvider; - public HttpClientBuilder WithQoS(IQoSProvider qoSProvider, IOcelotLogger logger, HttpMessageHandler innerHandler) + public IHttpClientBuilder WithCookieContainer(CookieContainer cookieContainer) { - _handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qoSProvider, logger, innerHandler)); + _cookieContainer = cookieContainer; return this; } - internal HttpClient Build(HttpMessageHandler innerHandler) + public IHttpClientBuilder WithTimeout(TimeSpan timeout) { - return _handlers.Any() ? - new HttpClient(CreateHttpMessageHandler()) : - new HttpClient(innerHandler); + _timeout = timeout; + return this; } - private HttpMessageHandler CreateHttpMessageHandler() + public IHttpClientBuilder WithHandler(DelegatingHandler handler) { - HttpMessageHandler httpMessageHandler = new HttpClientHandler(); + _handlers.Add(handler); + return this; + } - _handlers - .OrderByDescending(handler => handler.Key) - .Select(handler => handler.Value) - .Reverse() - .ToList() - .ForEach(handler => + public IHttpClientBuilder WithDefaultRequestHeaders(Dictionary headers) + { + _defaultHeaders = headers; + return this; + } + + + public IHttpClient Create() + { + HttpClientHandler httpclientHandler = null; + if (_cookieContainer != null) { - var delegatingHandler = handler(); - delegatingHandler.InnerHandler = httpMessageHandler; - httpMessageHandler = delegatingHandler; - }); + httpclientHandler = new HttpClientHandler() { CookieContainer = _cookieContainer }; + } + else + { + httpclientHandler = new HttpClientHandler(); + } + if (httpclientHandler.SupportsAutomaticDecompression) + { + httpclientHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + + var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler)); + + if (_timeout.HasValue) + { + client.Timeout = _timeout.Value; + } + + if (_defaultHeaders == null) + { + return new HttpClientWrapper(client); + } + + foreach (var header in _defaultHeaders) + { + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + + return new HttpClientWrapper(client); + } + + private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler) + { + foreach (var handler in _handlers) + { + handler.InnerHandler = httpMessageHandler; + httpMessageHandler = handler; + } return httpMessageHandler; } } + + /// + /// This class was made to make unit testing easier when HttpClient is used. + /// + internal class HttpClientWrapper : IHttpClient + { + public HttpClient Client { get; } + + public HttpClientWrapper(HttpClient client) + { + Client = client; + } + + public Task SendAsync(HttpRequestMessage request) + { + return Client.SendAsync(request); + } + } } diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index 5a2c86c7..f0566fed 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Net.Http; using System.Threading.Tasks; using Ocelot.Logging; @@ -10,47 +11,56 @@ namespace Ocelot.Requester { public class HttpClientHttpRequester : IHttpRequester { + private IHttpClientMessageCacheHandler _cacheHandlers; private readonly IOcelotLogger _logger; - public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory) + public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientMessageCacheHandler cacheHandlers) { _logger = loggerFactory.CreateLogger(); + _cacheHandlers = cacheHandlers; } public async Task> GetResponse(Request.Request request) { - var builder = new HttpClientBuilder(); + var builder = new HttpClientBuilder(); - using (var handler = new HttpClientHandler { CookieContainer = request.CookieContainer }) + builder.WithCookieContainer(request.CookieContainer); + + string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}"; + + if (request.IsQos) { - if (request.IsQos) - { - builder.WithQoS(request.QosProvider, _logger, handler); - } - - using (var httpClient = builder.Build(handler)) - { - try - { - var response = await httpClient.SendAsync(request.HttpRequestMessage); - return new OkResponse(response); - } - catch (TimeoutRejectedException exception) - { - return - new ErrorResponse(new RequestTimedOutError(exception)); - } - catch (BrokenCircuitException exception) - { - return - new ErrorResponse(new RequestTimedOutError(exception)); - } - catch (Exception exception) - { - return new ErrorResponse(new UnableToCompleteRequestError(exception)); - } - } + builder.WithHandler(new PollyCircuitBreakingDelegatingHandler(request.QosProvider, _logger)); + baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}"; } + + IHttpClient httpClient = _cacheHandlers.Get(baseUrl); + if (httpClient == null) + { + httpClient = builder.Create(); + _cacheHandlers.Set(baseUrl, httpClient, TimeSpan.FromMinutes(30)); + } + + try + { + var response = await httpClient.SendAsync(request.HttpRequestMessage); + return new OkResponse(response); + } + catch (TimeoutRejectedException exception) + { + return + new ErrorResponse(new RequestTimedOutError(exception)); + } + catch (BrokenCircuitException exception) + { + return + new ErrorResponse(new RequestTimedOutError(exception)); + } + catch (Exception exception) + { + return new ErrorResponse(new UnableToCompleteRequestError(exception)); + } + } } } \ No newline at end of file diff --git a/src/Ocelot/Requester/IHttpClient.cs b/src/Ocelot/Requester/IHttpClient.cs new file mode 100644 index 00000000..8a169a36 --- /dev/null +++ b/src/Ocelot/Requester/IHttpClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ocelot.Requester +{ + public interface IHttpClient + { + HttpClient Client { get; } + + Task SendAsync(HttpRequestMessage request); + } +} diff --git a/src/Ocelot/Requester/IHttpClientBuilder.cs b/src/Ocelot/Requester/IHttpClientBuilder.cs new file mode 100644 index 00000000..85fd5f69 --- /dev/null +++ b/src/Ocelot/Requester/IHttpClientBuilder.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Ocelot.Logging; +using Ocelot.Requester.QoS; +using System.Net; + +namespace Ocelot.Requester +{ + public interface IHttpClientBuilder + { + /// + /// Sets the cookie container used to store server cookies by the handler + /// + /// + /// + IHttpClientBuilder WithCookieContainer(CookieContainer cookieContainer); + + /// + /// Sets the number of milliseconds to wait before the request times out. + /// + IHttpClientBuilder WithTimeout(TimeSpan timeout); + + /// + /// Sets a DelegatingHandler. + /// Can be reused to set several different Handlers in a pipeline. + /// + IHttpClientBuilder WithHandler(DelegatingHandler handler); + + /// + /// Sets Default HttpRequestHeaders + /// + IHttpClientBuilder WithDefaultRequestHeaders(Dictionary headers); + + /// + /// Creates the + /// + IHttpClient Create(); + } +} diff --git a/src/Ocelot/Requester/IHttpClientMessageCacheHandler.cs b/src/Ocelot/Requester/IHttpClientMessageCacheHandler.cs new file mode 100644 index 00000000..a2a93692 --- /dev/null +++ b/src/Ocelot/Requester/IHttpClientMessageCacheHandler.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ocelot.Requester +{ + public interface IHttpClientMessageCacheHandler + { + bool Exists(string id); + IHttpClient Get(string id); + void Remove(string id); + void Set(string id, IHttpClient handler, TimeSpan expirationTime); + } +} diff --git a/src/Ocelot/Requester/MemoryHttpClientMessageCacheHandler.cs b/src/Ocelot/Requester/MemoryHttpClientMessageCacheHandler.cs new file mode 100644 index 00000000..bb948b8a --- /dev/null +++ b/src/Ocelot/Requester/MemoryHttpClientMessageCacheHandler.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ocelot.Requester +{ + public class MemoryHttpClientMessageCacheHandler : IHttpClientMessageCacheHandler + { + private readonly IMemoryCache _memoryCache; + + public MemoryHttpClientMessageCacheHandler(IMemoryCache memoryCache) + { + _memoryCache = memoryCache; + } + + public void Set(string id, IHttpClient counter, TimeSpan expirationTime) + { + _memoryCache.Set(id, counter, new MemoryCacheEntryOptions().SetAbsoluteExpiration(expirationTime)); + } + + public bool Exists(string id) + { + IHttpClient counter; + return _memoryCache.TryGetValue(id, out counter); + } + + public IHttpClient Get(string id) + { + IHttpClient counter; + if (_memoryCache.TryGetValue(id, out counter)) + { + return counter; + } + + return null; + } + + public void Remove(string id) + { + _memoryCache.Remove(id); + } + } +} diff --git a/src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs b/src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs index a50ec91b..a09ed233 100644 --- a/src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs +++ b/src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs @@ -16,9 +16,7 @@ namespace Ocelot.Requester public PollyCircuitBreakingDelegatingHandler( IQoSProvider qoSProvider, - IOcelotLogger logger, - HttpMessageHandler innerHandler) - : base(innerHandler) + IOcelotLogger logger) { _qoSProvider = qoSProvider; _logger = logger; From 23ad6ed2642fce94b9a5d9d7be4e52b67a3a3a91 Mon Sep 17 00:00:00 2001 From: geffzhang Date: Tue, 7 Mar 2017 08:29:03 +0800 Subject: [PATCH 2/3] refactor: HttpClientCache and remove not used code current --- global.json | 2 +- .../ServiceCollectionExtensions.cs | 2 +- src/Ocelot/Requester/HttpClientBuilder.cs | 69 +++++-------------- .../Requester/HttpClientHttpRequester.cs | 35 ++++++---- src/Ocelot/Requester/IHttpClientBuilder.cs | 21 +----- ...ageCacheHandler.cs => IHttpClientCache.cs} | 2 +- ...cheHandler.cs => MemoryHttpClientCache.cs} | 8 +-- 7 files changed, 48 insertions(+), 91 deletions(-) rename src/Ocelot/Requester/{IHttpClientMessageCacheHandler.cs => IHttpClientCache.cs} (86%) rename src/Ocelot/Requester/{MemoryHttpClientMessageCacheHandler.cs => MemoryHttpClientCache.cs} (70%) diff --git a/global.json b/global.json index ff8d898e..616b2c4e 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@  { "projects": [ "src", "test" ], "sdk": { - "version": "1.0.0-preview2-003133" + "version": "1.0.0-preview2-003131" } } diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 04ca1037..dbb98810 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -135,7 +135,7 @@ namespace Ocelot.DependencyInjection services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index 8cad9d49..d7939df0 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -11,61 +11,23 @@ namespace Ocelot.Requester { internal class HttpClientBuilder : IHttpClientBuilder { - private TimeSpan? _timeout; - private readonly List _handlers = new List(); + private readonly Dictionary> _handlers = new Dictionary>(); private Dictionary _defaultHeaders; - private CookieContainer _cookieContainer; - private IQoSProvider _qoSProvider; - public IHttpClientBuilder WithCookieContainer(CookieContainer cookieContainer) + public IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger) { - _cookieContainer = cookieContainer; + _handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger)); return this; - } - public IHttpClientBuilder WithTimeout(TimeSpan timeout) - { - _timeout = timeout; - return this; - } - - public IHttpClientBuilder WithHandler(DelegatingHandler handler) - { - _handlers.Add(handler); - return this; - } - - public IHttpClientBuilder WithDefaultRequestHeaders(Dictionary headers) - { - _defaultHeaders = headers; - return this; - } + } public IHttpClient Create() { - HttpClientHandler httpclientHandler = null; - if (_cookieContainer != null) - { - httpclientHandler = new HttpClientHandler() { CookieContainer = _cookieContainer }; - } - else - { - httpclientHandler = new HttpClientHandler(); - } - - if (httpclientHandler.SupportsAutomaticDecompression) - { - httpclientHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - } - + var httpclientHandler = new HttpClientHandler(); + var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler)); - - if (_timeout.HasValue) - { - client.Timeout = _timeout.Value; - } - + if (_defaultHeaders == null) { return new HttpClientWrapper(client); @@ -81,11 +43,18 @@ namespace Ocelot.Requester private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler) { - foreach (var handler in _handlers) - { - handler.InnerHandler = httpMessageHandler; - httpMessageHandler = handler; - } + + _handlers + .OrderByDescending(handler => handler.Key) + .Select(handler => handler.Value) + .Reverse() + .ToList() + .ForEach(handler => + { + var delegatingHandler = handler(); + delegatingHandler.InnerHandler = httpMessageHandler; + httpMessageHandler = delegatingHandler; + }); return httpMessageHandler; } } diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index f0566fed..06185b75 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -11,10 +11,10 @@ namespace Ocelot.Requester { public class HttpClientHttpRequester : IHttpRequester { - private IHttpClientMessageCacheHandler _cacheHandlers; + private readonly IHttpClientCache _cacheHandlers; private readonly IOcelotLogger _logger; - public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientMessageCacheHandler cacheHandlers) + public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers) { _logger = loggerFactory.CreateLogger(); _cacheHandlers = cacheHandlers; @@ -24,23 +24,15 @@ namespace Ocelot.Requester { var builder = new HttpClientBuilder(); - builder.WithCookieContainer(request.CookieContainer); + var cacheKey = GetCacheKey(request, builder); - string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}"; - - if (request.IsQos) - { - builder.WithHandler(new PollyCircuitBreakingDelegatingHandler(request.QosProvider, _logger)); - baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}"; - } - - IHttpClient httpClient = _cacheHandlers.Get(baseUrl); + var httpClient = _cacheHandlers.Get(cacheKey); if (httpClient == null) { httpClient = builder.Create(); - _cacheHandlers.Set(baseUrl, httpClient, TimeSpan.FromMinutes(30)); - } - + _cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(6)); + } + try { var response = await httpClient.SendAsync(request.HttpRequestMessage); @@ -62,5 +54,18 @@ namespace Ocelot.Requester } } + + private string GetCacheKey(Request.Request request, HttpClientBuilder builder) + { + string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}"; + + if (request.IsQos) + { + builder.WithQos(request.QosProvider, _logger); + baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}"; + } + + return baseUrl; + } } } \ No newline at end of file diff --git a/src/Ocelot/Requester/IHttpClientBuilder.cs b/src/Ocelot/Requester/IHttpClientBuilder.cs index 85fd5f69..832fd8d1 100644 --- a/src/Ocelot/Requester/IHttpClientBuilder.cs +++ b/src/Ocelot/Requester/IHttpClientBuilder.cs @@ -11,28 +11,11 @@ namespace Ocelot.Requester { public interface IHttpClientBuilder { - /// - /// Sets the cookie container used to store server cookies by the handler - /// - /// - /// - IHttpClientBuilder WithCookieContainer(CookieContainer cookieContainer); /// - /// Sets the number of milliseconds to wait before the request times out. + /// Sets a PollyCircuitBreakingDelegatingHandler . /// - IHttpClientBuilder WithTimeout(TimeSpan timeout); - - /// - /// Sets a DelegatingHandler. - /// Can be reused to set several different Handlers in a pipeline. - /// - IHttpClientBuilder WithHandler(DelegatingHandler handler); - - /// - /// Sets Default HttpRequestHeaders - /// - IHttpClientBuilder WithDefaultRequestHeaders(Dictionary headers); + IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger); /// /// Creates the diff --git a/src/Ocelot/Requester/IHttpClientMessageCacheHandler.cs b/src/Ocelot/Requester/IHttpClientCache.cs similarity index 86% rename from src/Ocelot/Requester/IHttpClientMessageCacheHandler.cs rename to src/Ocelot/Requester/IHttpClientCache.cs index a2a93692..f4b8f76e 100644 --- a/src/Ocelot/Requester/IHttpClientMessageCacheHandler.cs +++ b/src/Ocelot/Requester/IHttpClientCache.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Ocelot.Requester { - public interface IHttpClientMessageCacheHandler + public interface IHttpClientCache { bool Exists(string id); IHttpClient Get(string id); diff --git a/src/Ocelot/Requester/MemoryHttpClientMessageCacheHandler.cs b/src/Ocelot/Requester/MemoryHttpClientCache.cs similarity index 70% rename from src/Ocelot/Requester/MemoryHttpClientMessageCacheHandler.cs rename to src/Ocelot/Requester/MemoryHttpClientCache.cs index bb948b8a..33ff15a1 100644 --- a/src/Ocelot/Requester/MemoryHttpClientMessageCacheHandler.cs +++ b/src/Ocelot/Requester/MemoryHttpClientCache.cs @@ -7,18 +7,18 @@ using System.Threading.Tasks; namespace Ocelot.Requester { - public class MemoryHttpClientMessageCacheHandler : IHttpClientMessageCacheHandler + public class MemoryHttpClientCache : IHttpClientCache { private readonly IMemoryCache _memoryCache; - public MemoryHttpClientMessageCacheHandler(IMemoryCache memoryCache) + public MemoryHttpClientCache(IMemoryCache memoryCache) { _memoryCache = memoryCache; } - public void Set(string id, IHttpClient counter, TimeSpan expirationTime) + public void Set(string id, IHttpClient client, TimeSpan expirationTime) { - _memoryCache.Set(id, counter, new MemoryCacheEntryOptions().SetAbsoluteExpiration(expirationTime)); + _memoryCache.Set(id, client, new MemoryCacheEntryOptions().SetAbsoluteExpiration(expirationTime)); } public bool Exists(string id) From 57285309429f0057e044bfa9dcd160a9f5ab133b Mon Sep 17 00:00:00 2001 From: geffzhang Date: Tue, 7 Mar 2017 08:41:28 +0800 Subject: [PATCH 3/3] Update global.json revert global.json sdk to 3133 --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 616b2c4e..ff8d898e 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@  { "projects": [ "src", "test" ], "sdk": { - "version": "1.0.0-preview2-003131" + "version": "1.0.0-preview2-003133" } }