Пример #1
0
        public IHttp Create(string namedClientName)
        {
            if (namedClientName == null)
            {
                // This is the old behaviour. NetStd20HttpEngine will use new HttpClient and new HttpClientHandler when
                // creating NetStd20HttpRequests, see NetStd20HttpEngine.CreateWebRequest(string method, Uri url).
                return(new NetStd20HttpEngine());
            }
            else
            {
                // The new behaviour is to create a new NetStd20HttpEngine instance but link it to a NamedHttpClientHandlerWrapper
                // (the behaviour implemented by the subclass NamedClientHttpEngine). This way we can easily ensure that
                // all HttpClientHandlers for the same named client are initialised just once (because they are re-used from
                // within a pool, while HttpClients are created new for each request, and HttpClientHandler properties cannot
                // change once used.)
                //
                // Note however, that we don't know which named HttpClient is mapped to which HttpClientHandler, all we know
                // is that the HttpClient is newly created, and it is linked with one of the HttpClientHandlers in
                // NamedHttpClientHandlerWrapper (because they have the same name). And we know the HttpClientHandlers in
                // NamedHttpClientHandlerWrapper hasn't been configured yet (because once configured, they are removed
                // from the wrapper).
                //
                // Such a set up can ensure all HttpClientHandlers with the same name are configured the same way, so then
                // no matter whichever HttpClientHandler the HttpClient is paired with, it will work in an expected way.
                //
                // However, there is an assumption that the user of MiniRestSharp does not attempt to use the same named client
                // with different set up (specifically, set ups that require different HttpClientHandler configurations).

                // This call is thread-safe because mNamedHandlerWrappers is a ConcurrentDictionary.
                NamedHttpClientHandlerWrapper wrapper = mNamedHandlerWrappers.GetOrAdd(namedClientName, name => new NamedHttpClientHandlerWrapper(name));
                return(new NamedClientHttpEngine(namedClientName, wrapper));
            }
        }
Пример #2
0
            /// <summary>
            /// This is the method that is called to configure the <see cref="NetStd20HttpRequest.RequestClient"/>
            /// and <see cref="NetStd20HttpRequest.RequestHandler"/> returned by <see cref="CreateWebRequest(string, Uri)"/>.
            /// </summary>
            /// <param name="request">Value returned by <see cref="CreateWebRequest(string, Uri)"/>.</param>
            protected override NetStd20HttpRequest ConfigureWebRequest(NetStd20HttpRequest request)
            {
                // Must ensure this method is thread-safe because the parent method will update
                // NetStd20HttpRequest.RequestHandler properties, which is really just calling NamedHttpClientHandlerWrapper.
                // We don't want multiple threads to be updating NamedHttpClientHandlerWrapper properties at the same
                // time because the wrapped HttpClientHandler properties are probably not thread-safe, and we don't want
                // another thread to add to the wrapper while we are updating wrapper's current set of handlers.
                NamedHttpClientHandlerWrapper wrapper = request.RequestHandler;

                // The lock will prevent new handlers from being added in the HttpEngineFactory.AddNamedClientHandler() method above.
                lock (wrapper)
                {
                    // By this point, it is an error if we do not have access to the HttpClientHandler associated with the named client.
                    // Because we clear the wrapper at the end of this code block, wrapper may be empty if we had previously
                    // configured the named HttpClientHandler already. The IsAlwaysEmpty property gets around this issue.
                    if (wrapper.IsAlwaysEmpty)
                    {
                        throw new InvalidOperationException(string.Format(
                                                                "HttpClientHandler for the named client '{0}' has not been registered with MiniRestSharp. Did you forget to call RestSharpServiceCollectionExtensions.AddRestSharpClient(IServiceCollection, string)?",
                                                                wrapper.Name));
                    }

                    // I've confirmed that the only place the wrapped HttpClientHandlers are configured is in this method.
                    NetStd20HttpRequest returnValue = base.ConfigureWebRequest(request);

                    // Now that the wrapped HttpClientHandlers are configured, we remove them from the wrapper
                    // since we can't configure them again anyway. It's of course possible that these handlers
                    // get recycled by IHttpClientFactory and become configurable again. When this happens I think the
                    // RestSharpServiceCollectionExtensions system will end up calling HttpEngineFactory.AddNamedClientHandler()
                    // again, which will result in it being added to wrapper again for another round of configuration later.
                    wrapper.Clear();

                    return(returnValue);
                }
            }
Пример #3
0
        private void AppendCookies(NamedHttpClientHandlerWrapper handler, Uri requestUri)
        {
            handler.CookieContainer = this.CookieContainer ?? new CookieContainer();

            foreach (HttpCookie httpCookie in this.Cookies)
            {
                Func <Cookie> createCookie = () => new Cookie
                {
                    Name   = httpCookie.Name,
                    Value  = httpCookie.Value,
                    Domain = requestUri.Host
                };

                handler.CookieContainer_Add(new Uri(string.Format("{0}://{1}", requestUri.Scheme, requestUri.Host)), createCookie);
            }

            if ((this.CookieContainer == null || this.CookieContainer.Count == 0) && this.Cookies.Count == 0)
            {
                //handler.CookieContainer = null; // In netstandard2.0 this property cannot be set to null.
                handler.UseCookies = false;
            }
            else
            {
                handler.UseCookies = true;
            }
        }
Пример #4
0
        /// <summary>
        /// Re-use a named <see cref="HttpClient"/> and its <see cref="HttpClientHandler"/>. It's normally not possible
        /// to access the <see cref="HttpClientHandler"/> from the <see cref="HttpClient"/>, we had to do some
        /// configuration magic to get access to the handler.
        /// </summary>
        public NetStd20HttpRequest(HttpClient httpClient, NamedHttpClientHandlerWrapper httpClientHandler, string method, Uri url)
        {
            this.RequestHandler = httpClientHandler;
            this.RequestClient  = httpClient;
            this.RequestMessage = new HttpRequestMessage(new HttpMethod(method), url);

            // Must set a HttpContent here to allow HttpContentHeader to be set.
            this.RequestMessage.Content = new ByteArrayContent(new byte[0]);
        }
Пример #5
0
        /// <summary>
        /// <para>
        /// Creates a new <see cref="HttpClientHandler"/> instance and add it to a new <see cref="NamedHttpClientHandlerWrapper"/>
        /// instance; also creates a new <see cref="HttpClient"/> using the <see cref="HttpClient(HttpMessageHandler)"/>
        /// constructor, passing in the <see cref="HttpClientHandler"/> instance as the constructor parameter.
        /// </para>
        /// <para>
        /// Finally calls <see cref="NetStd20HttpRequest(HttpClient, NamedHttpClientHandlerWrapper, string, Uri)"/>
        /// passing in the prepared objects as parameters.
        /// </para>
        /// <para>
        /// Override this method to provide your own implementation of creating a new NetStd20HttpRequest instance.
        /// </para>
        /// </summary>
        protected virtual NetStd20HttpRequest CreateWebRequest(string method, Uri url)
        {
            var clientHandler = new HttpClientHandler();
            var client        = new HttpClient(clientHandler);

            // Because we're not really using this wrapper with a named client, we set its name to null.
            var wrapper = new NamedHttpClientHandlerWrapper(null);

            wrapper.Add(clientHandler);

            return(new NetStd20HttpRequest(client, wrapper, method, url));
        }
Пример #6
0
        /// <summary>
        /// This method will wrap the HttpClientHandler given in a NamedHttpClientHandlerWrapper for the given name.
        /// A single NamedHttpClientHandlerWrapper can wrap many HttpClientHandlers, but all for the same name.
        /// This method is thread-safe.
        /// </summary>
        public void AddNamedClientHandler(string name, HttpClientHandler httpClientHandler)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            if (httpClientHandler == null)
            {
                throw new ArgumentNullException(nameof(httpClientHandler));
            }

            // Since we cannot guarantee this method won't be called concurrently (e.g. multiple requests for the same named
            // client), we must make sure it is thread-safe. Luckily mNamedHandlerWrappers is a ConcurrentDictionary.
            NamedHttpClientHandlerWrapper wrapper = mNamedHandlerWrappers.GetOrAdd(name, nam => new NamedHttpClientHandlerWrapper(nam));

            // Add the handler to the wrapper for the given name. Wrapper object is NOT thread-safe so we must
            // synchronize on it before adding. An implication is that this may block if the same wrapper object is being
            // used (as NetStd20HttpRequest.RequestHandler) in the NamedClientHttpEngine.ConfigureWebRequest() method below.
            lock (wrapper)
            {
                wrapper.Add(httpClientHandler);
            }
        }
Пример #7
0
 internal NamedClientHttpEngine(string name, NamedHttpClientHandlerWrapper httpClientHandlerWrapper)
 {
     mName = name;
     mHttpClientHandlerWrapper = httpClientHandlerWrapper;
 }