Func <HttpClient, object[], Task> buildVoidTaskFuncForMethod(RestMethodInfo restMethod)
        {
            return(async(client, paramList) => {
                var factory = BuildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath);
                var rq = factory(paramList);
                var resp = await client.SendAsync(rq);

                if (!resp.IsSuccessStatusCode)
                {
                    throw await ApiException.Create(resp, settings);
                }
            });
        }
Пример #2
0
        Func <HttpClient, object[], Task <T> > BuildTaskFuncForMethod <T>(RestMethodInfo restMethod)
        {
            var ret = BuildCancellableTaskFuncForMethod <T>(restMethod);

            return((client, paramList) => {
                if (restMethod.CancellationToken != null)
                {
                    return ret(client, paramList.OfType <CancellationToken>().FirstOrDefault(), paramList);
                }

                return ret(client, CancellationToken.None, paramList);
            });
        }
Пример #3
0
        private void AddInterfaceHttpMethods(Type interfaceType, Dictionary <string, List <RestMethodInfo> > methods)
        {
            foreach (var methodInfo in interfaceType.GetMethods())
            {
                var attrs         = methodInfo.GetCustomAttributes(true);
                var hasHttpMethod = attrs.OfType <HttpMethodAttribute>().Any();
                if (hasHttpMethod)
                {
                    if (!methods.ContainsKey(methodInfo.Name))
                    {
                        methods.Add(methodInfo.Name, new List <RestMethodInfo>());
                    }

                    var restinfo = new RestMethodInfo(interfaceType, methodInfo, settings);
                    methods[methodInfo.Name].Add(restinfo);
                }
            }
        }
        Func <HttpClient, object[], IObservable <T> > buildRxFuncForMethod <T>(RestMethodInfo restMethod)
        {
            var taskFunc = buildCancellableTaskFuncForMethod <T>(restMethod);

            return((client, paramList) => {
                return new TaskToObservable <T>(ct => {
                    var methodCt = CancellationToken.None;
                    if (restMethod.CancellationToken != null)
                    {
                        methodCt = paramList.OfType <CancellationToken>().FirstOrDefault();
                    }

                    // link the two
                    var cts = CancellationTokenSource.CreateLinkedTokenSource(methodCt, ct);

                    return taskFunc(client, cts.Token, paramList);
                });
            });
        }
        Func <HttpClient, object[], Task> buildVoidTaskFuncForMethod(RestMethodInfo restMethod)
        {
            return(async(client, paramList) => {
                var factory = buildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath, restMethod.CancellationToken != null);
                var rq = factory(paramList);

                var ct = CancellationToken.None;

                if (restMethod.CancellationToken != null)
                {
                    ct = paramList.OfType <CancellationToken>().FirstOrDefault();
                }

                using (var resp = await client.SendAsync(rq, ct).ConfigureAwait(false)) {
                    if (!resp.IsSuccessStatusCode)
                    {
                        throw await ApiException.Create(rq.RequestUri, restMethod.HttpMethod, resp, settings).ConfigureAwait(false);
                    }
                }
            });
        }
Пример #6
0
        Func <HttpClient, object[], IObservable <T> > buildRxFuncForMethod <T>(RestMethodInfo restMethod)
            where T : class
        {
            var taskFunc = buildTaskFuncForMethod <T>(restMethod);

            return((client, paramList) => {
                var ret = new FakeAsyncSubject <T>();

                taskFunc(client, paramList).ContinueWith(t => {
                    if (t.Exception != null)
                    {
                        ret.OnError(t.Exception);
                    }
                    else
                    {
                        ret.OnNext(t.Result);
                        ret.OnCompleted();
                    }
                });

                return ret;
            });
        }
        Func <HttpClient, CancellationToken, object[], Task <T> > buildCancellableTaskFuncForMethod <T>(RestMethodInfo restMethod)
        {
            return(async(client, ct, paramList) => {
                var factory = buildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath, restMethod.CancellationToken != null);
                var rq = factory(paramList);

                var resp = await client.SendAsync(rq, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);

                if (restMethod.SerializedReturnType == typeof(HttpResponseMessage))
                {
                    // NB: This double-casting manual-boxing hate crime is the only way to make
                    // this work without a 'class' generic constraint. It could blow up at runtime
                    // and would be A Bad Idea if we hadn't already vetted the return type.
                    return (T)(object)resp;
                }

                if (!resp.IsSuccessStatusCode)
                {
                    throw await ApiException.Create(rq.RequestUri, restMethod.HttpMethod, resp, restMethod.RefitSettings).ConfigureAwait(false);
                }

                if (restMethod.SerializedReturnType == typeof(HttpContent))
                {
                    return (T)(object)resp.Content;
                }

                var ms = new MemoryStream();
                using (var fromStream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false)) {
                    await fromStream.CopyToAsync(ms, 4096, ct).ConfigureAwait(false);
                }

                var bytes = ms.ToArray();
                var content = Encoding.UTF8.GetString(bytes, 0, bytes.Length);

                if (restMethod.SerializedReturnType == typeof(string))
                {
                    return (T)(object)content;
                }

                return JsonConvert.DeserializeObject <T>(content, settings.JsonSerializerSettings);
            });
        }
        Func <HttpClient, CancellationToken, object[], Task <T> > BuildCancellableTaskFuncForMethod <T>(RestMethodInfo restMethod)
        {
            return(async(client, ct, paramList) => {
                if (client.BaseAddress == null)
                {
                    throw new InvalidOperationException("BaseAddress must be set on the HttpClient instance");
                }

                var factory = BuildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath, restMethod.CancellationToken != null);
                var rq = factory(paramList);
                HttpResponseMessage resp = null;
                var disposeResponse = true;
                try
                {
                    resp = await client.SendAsync(rq, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);

                    if (restMethod.SerializedReturnType == typeof(HttpResponseMessage))
                    {
                        disposeResponse = false; // caller has to dispose

                        // NB: This double-casting manual-boxing hate crime is the only way to make
                        // this work without a 'class' generic constraint. It could blow up at runtime
                        // and would be A Bad Idea if we hadn't already vetted the return type.
                        return (T)(object)resp;
                    }

                    if (!resp.IsSuccessStatusCode)
                    {
                        disposeResponse = false;
                        throw await ApiException.Create(rq.RequestUri, restMethod.HttpMethod, resp, restMethod.RefitSettings).ConfigureAwait(false);
                    }

                    if (restMethod.SerializedReturnType == typeof(HttpContent))
                    {
                        disposeResponse = false; // caller has to clean up the content
                        return (T)(object)resp.Content;
                    }

                    if (restMethod.SerializedReturnType == typeof(Stream))
                    {
                        disposeResponse = false; // caller has to dispose
                        return (T)(object)await resp.Content.ReadAsStreamAsync().ConfigureAwait(false);
                    }

                    using (var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false))
                        using (var reader = new StreamReader(stream))
                        {
                            if (restMethod.SerializedReturnType == typeof(string))
                            {
                                return (T)(object)await reader.ReadToEndAsync().ConfigureAwait(false);
                            }

                            using (var jsonReader = new JsonTextReader(reader))
                            {
                                return serializer.Deserialize <T>(jsonReader);
                            }
                        }
                }
                finally
                {
                    // Ensure we clean up the request
                    // Especially important if it has open files/streams
                    rq.Dispose();
                    if (disposeResponse)
                    {
                        resp?.Dispose();
                    }
                }
            });
        }
Пример #9
0
        Func <object[], HttpRequestMessage> BuildRequestFactoryForMethod(RestMethodInfo restMethod, string basePath, bool paramsContainsCancellationToken)
        {
            return(paramList =>
            {
                // make sure we strip out any cancelation tokens
                if (paramsContainsCancellationToken)
                {
                    paramList = paramList.Where(o => o == null || o.GetType() != typeof(CancellationToken)).ToArray();
                }

                var ret = new HttpRequestMessage
                {
                    Method = restMethod.HttpMethod
                };

                // set up multipart content
                MultipartFormDataContent multiPartContent = null;
                if (restMethod.IsMultipart)
                {
                    multiPartContent = new MultipartFormDataContent("----MyGreatBoundary");
                    ret.Content = multiPartContent;
                }

                var urlTarget = (basePath == "/" ? string.Empty : basePath) + restMethod.RelativePath;
                var queryParamsToAdd = new List <KeyValuePair <string, string> >();
                var headersToAdd = new Dictionary <string, string>(restMethod.Headers);

                for (var i = 0; i < paramList.Length; i++)
                {
                    // if part of REST resource URL, substitute it in
                    if (restMethod.ParameterMap.ContainsKey(i))
                    {
                        urlTarget = Regex.Replace(
                            urlTarget,
                            "{" + restMethod.ParameterMap[i] + "}",
                            settings.UrlParameterFormatter
                            .Format(paramList[i], restMethod.ParameterInfoMap[i])
                            .Replace("/", "%2F"),
                            RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
                        continue;
                    }

                    // if marked as body, add to content
                    if (restMethod.BodyParameterInfo != null && restMethod.BodyParameterInfo.Item3 == i)
                    {
                        var streamParam = paramList[i] as Stream;
                        var stringParam = paramList[i] as string;

                        if (paramList[i] is HttpContent httpContentParam)
                        {
                            ret.Content = httpContentParam;
                        }
                        else if (streamParam != null)
                        {
                            ret.Content = new StreamContent(streamParam);
                        }
                        else if (stringParam != null)
                        {
                            ret.Content = new StringContent(stringParam);
                        }
                        else
                        {
                            switch (restMethod.BodyParameterInfo.Item1)
                            {
                            case BodySerializationMethod.UrlEncoded:
                                ret.Content = new FormUrlEncodedContent(new FormValueDictionary(paramList[i]));
                                break;

                            case BodySerializationMethod.Json:
                                var param = paramList[i];
                                switch (restMethod.BodyParameterInfo.Item2)
                                {
                                case false:
                                    ret.Content = new PushStreamContent((stream, _, __) =>
                                    {
                                        using (var writer = new JsonTextWriter(new StreamWriter(stream)))
                                        {
                                            serializer.Serialize(writer, param);
                                        }
                                    },
                                                                        "application/json");
                                    break;

                                case true:
                                    ret.Content = new StringContent(
                                        JsonConvert.SerializeObject(paramList[i], settings.JsonSerializerSettings),
                                        Encoding.UTF8,
                                        "application/json");
                                    break;
                                }

                                break;
                            }
                        }

                        continue;
                    }

                    // if header, add to request headers
                    if (restMethod.HeaderParameterMap.ContainsKey(i))
                    {
                        headersToAdd[restMethod.HeaderParameterMap[i]] = paramList[i]?.ToString();
                        continue;
                    }

                    // ignore nulls
                    if (paramList[i] == null)
                    {
                        continue;
                    }

                    // for anything that fell through to here, if this is not
                    // a multipart method, add the parameter to the query string
                    if (!restMethod.IsMultipart)
                    {
                        var attr = restMethod.ParameterInfoMap[i].GetCustomAttribute <QueryAttribute>() ?? new QueryAttribute();
                        if (DoNotConvertToQueryMap(paramList[i]))
                        {
                            queryParamsToAdd.Add(new KeyValuePair <string, string>(restMethod.QueryParameterMap[i], settings.UrlParameterFormatter.Format(paramList[i], restMethod.ParameterInfoMap[i])));
                        }
                        else
                        {
                            foreach (var kvp in BuildQueryMap(paramList[i], attr.Delimiter))
                            {
                                var path = !string.IsNullOrWhiteSpace(attr.Prefix) ? $"{attr.Prefix}{attr.Delimiter}{kvp.Key}" : kvp.Key;
                                queryParamsToAdd.Add(new KeyValuePair <string, string>(path, settings.UrlParameterFormatter.Format(kvp.Value, restMethod.ParameterInfoMap[i])));
                            }
                        }

                        continue;
                    }

                    // we are in a multipart method, add the part to the content
                    // the parameter name should be either the attachment name or the parameter name (as fallback)
                    string itemName;
                    string parameterName;

                    if (!restMethod.AttachmentNameMap.TryGetValue(i, out var attachment))
                    {
                        itemName = restMethod.QueryParameterMap[i];
                        parameterName = itemName;
                    }
                    else
                    {
                        itemName = attachment.Item1;
                        parameterName = attachment.Item2;
                    }

                    // Check to see if it's an IEnumerable
                    var itemValue = paramList[i];
                    var enumerable = itemValue as IEnumerable <object>;
                    var typeIsCollection = false;

                    if (enumerable != null)
                    {
                        Type tType = null;
                        var eType = enumerable.GetType();
                        if (eType.GetTypeInfo().ContainsGenericParameters)
                        {
                            tType = eType.GenericTypeArguments[0];
                        }
                        else if (eType.IsArray)
                        {
                            tType = eType.GetElementType();
                        }

                        // check to see if it's one of the types we support for multipart:
                        // FileInfo, Stream, string or byte[]
                        if (tType == typeof(Stream) ||
                            tType == typeof(string) ||
                            tType == typeof(byte[]) ||
                            tType.GetTypeInfo().IsSubclassOf(typeof(MultipartItem)) ||
                            tType == typeof(FileInfo)
                            )
                        {
                            typeIsCollection = true;
                        }
                    }

                    if (typeIsCollection)
                    {
                        foreach (var item in enumerable)
                        {
                            AddMultipartItem(multiPartContent, itemName, parameterName, item);
                        }
                    }
                    else
                    {
                        AddMultipartItem(multiPartContent, itemName, parameterName, itemValue);
                    }
                }

                // NB: We defer setting headers until the body has been
                // added so any custom content headers don't get left out.
                foreach (var header in headersToAdd)
                {
                    SetHeader(ret, header.Key, header.Value);
                }

                // NB: The URI methods in .NET are dumb. Also, we do this
                // UriBuilder business so that we preserve any hardcoded query
                // parameters as well as add the parameterized ones.
                var uri = new UriBuilder(new Uri(new Uri("http://api"), urlTarget));
                var query = HttpUtility.ParseQueryString(uri.Query ?? "");
                foreach (var key in query.AllKeys)
                {
                    queryParamsToAdd.Insert(0, new KeyValuePair <string, string>(key, query[key]));
                }

                if (queryParamsToAdd.Any())
                {
                    var pairs = queryParamsToAdd.Select(x => HttpUtility.UrlEncode(x.Key) + "=" + HttpUtility.UrlEncode(x.Value));
                    uri.Query = string.Join("&", pairs);
                }
                else
                {
                    uri.Query = null;
                }

                ret.RequestUri = new Uri(uri.Uri.GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped), UriKind.Relative);
                return ret;
            });
        }
Пример #10
0
        Func <HttpClient, object[], Task <T> > buildTaskFuncForMethod <T>(RestMethodInfo restMethod)
        {
            var ret = buildCancellableTaskFuncForMethod <T>(restMethod);

            return((client, paramList) => ret(client, CancellationToken.None, paramList));
        }
Пример #11
0
 Func <HttpClient, object[], Task> BuildVoidTaskFuncForMethod(RestMethodInfo restMethod)
 {
     return(async(client, paramList) => {
             << << << < HEAD
             var factory = buildRequestFactoryForMethod(restMethod.Name, client.BaseAddress?.AbsolutePath, restMethod.CancellationToken != null);
Пример #12
0
        Func <object[], Task <HttpRequestMessage> > BuildRequestFactoryForMethod(RestMethodInfo restMethod, string basePath, bool paramsContainsCancellationToken)
        {
            return(async paramList =>
            {
                // make sure we strip out any cancelation tokens
                if (paramsContainsCancellationToken)
                {
                    paramList = paramList.Where(o => o == null || o.GetType() != typeof(CancellationToken)).ToArray();
                }

                var ret = new HttpRequestMessage
                {
                    Method = restMethod.HttpMethod
                };

                // set up multipart content
                MultipartFormDataContent multiPartContent = null;
                if (restMethod.IsMultipart)
                {
                    multiPartContent = new MultipartFormDataContent("----MyGreatBoundary");
                    ret.Content = multiPartContent;
                }

                var urlTarget = (basePath == "/" ? string.Empty : basePath) + restMethod.RelativePath;
                var queryParamsToAdd = new List <KeyValuePair <string, string> >();
                var headersToAdd = new Dictionary <string, string>(restMethod.Headers);

                for (var i = 0; i < paramList.Length; i++)
                {
                    // if part of REST resource URL, substitute it in
                    if (restMethod.ParameterMap.ContainsKey(i))
                    {
                        urlTarget = Regex.Replace(
                            urlTarget,
                            "{" + restMethod.ParameterMap[i] + "}",
                            Uri.EscapeDataString(settings.UrlParameterFormatter
                                                 .Format(paramList[i], restMethod.ParameterInfoMap[i]) ?? string.Empty),
                            RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
                        continue;
                    }

                    // if marked as body, add to content
                    if (restMethod.BodyParameterInfo != null && restMethod.BodyParameterInfo.Item3 == i)
                    {
                        if (paramList[i] is HttpContent httpContentParam)
                        {
                            ret.Content = httpContentParam;
                        }
                        else if (paramList[i] is Stream streamParam)
                        {
                            ret.Content = new StreamContent(streamParam);
                        }
                        // Default sends raw strings
                        else if (restMethod.BodyParameterInfo.Item1 == BodySerializationMethod.Default &&
                                 paramList[i] is string stringParam)
                        {
                            ret.Content = new StringContent(stringParam);
                        }
                        else
                        {
                            switch (restMethod.BodyParameterInfo.Item1)
                            {
                            case BodySerializationMethod.UrlEncoded:
                                ret.Content = paramList[i] is string str ? (HttpContent) new StringContent(Uri.EscapeDataString(str), Encoding.UTF8, "application/x-www-form-urlencoded") :  new FormUrlEncodedContent(new FormValueMultimap(paramList[i], settings));
                                break;

                            case BodySerializationMethod.Default:
#pragma warning disable CS0618 // Type or member is obsolete
                            case BodySerializationMethod.Json:
#pragma warning restore CS0618 // Type or member is obsolete
                            case BodySerializationMethod.Serialized:
                                var content = await serializer.SerializeAsync(paramList[i]).ConfigureAwait(false);

                                switch (restMethod.BodyParameterInfo.Item2)
                                {
                                case false:
                                    ret.Content = new PushStreamContent(
                                        async(stream, _, __) => {
                                        using (stream)
                                        {
                                            await content.CopyToAsync(stream).ConfigureAwait(false);
                                        }
                                    }, content.Headers.ContentType);
                                    break;

                                case true:
                                    ret.Content = content;
                                    break;
                                }

                                break;
                            }
                        }

                        continue;
                    }

                    // if header, add to request headers
                    if (restMethod.HeaderParameterMap.ContainsKey(i))
                    {
                        headersToAdd[restMethod.HeaderParameterMap[i]] = paramList[i]?.ToString();
                        continue;
                    }

                    // ignore nulls
                    if (paramList[i] == null)
                    {
                        continue;
                    }

                    // for anything that fell through to here, if this is not
                    // a multipart method, add the parameter to the query string
                    if (!restMethod.IsMultipart)
                    {
                        var attr = restMethod.ParameterInfoMap[i].GetCustomAttribute <QueryAttribute>() ?? new QueryAttribute();
                        if (DoNotConvertToQueryMap(paramList[i]))
                        {
                            if (paramList[i] is IEnumerable paramValues)
                            {
                                switch (attr.CollectionFormat)
                                {
                                case CollectionFormat.Multi:
                                    foreach (var paramValue in paramValues)
                                    {
                                        queryParamsToAdd.Add(new KeyValuePair <string, string>(
                                                                 restMethod.QueryParameterMap[i],
                                                                 settings.UrlParameterFormatter.Format(paramValue, restMethod.ParameterInfoMap[i])));
                                    }
                                    continue;

                                case CollectionFormat.Csv:
                                case CollectionFormat.Ssv:
                                case CollectionFormat.Tsv:
                                case CollectionFormat.Pipes:
                                    var delimiter = attr.CollectionFormat == CollectionFormat.Csv ?  ","
                                            : attr.CollectionFormat == CollectionFormat.Ssv ? " "
                                            : attr.CollectionFormat == CollectionFormat.Tsv ? "\t" : "|";

                                    var formattedValues = paramValues
                                                          .Cast <object>()
                                                          .Select(v => settings.UrlParameterFormatter.Format(v, restMethod.ParameterInfoMap[i]));

                                    queryParamsToAdd.Add(new KeyValuePair <string, string>(
                                                             restMethod.QueryParameterMap[i],
                                                             string.Join(delimiter, formattedValues)));
                                    continue;
                                }
                            }
                            queryParamsToAdd.Add(new KeyValuePair <string, string>(restMethod.QueryParameterMap[i], settings.UrlParameterFormatter.Format(paramList[i], restMethod.ParameterInfoMap[i])));
                        }
                        else
                        {
                            foreach (var kvp in BuildQueryMap(paramList[i], attr.Delimiter))
                            {
                                var path = !string.IsNullOrWhiteSpace(attr.Prefix) ? $"{attr.Prefix}{attr.Delimiter}{kvp.Key}" : kvp.Key;
                                queryParamsToAdd.Add(new KeyValuePair <string, string>(path, settings.UrlParameterFormatter.Format(kvp.Value, restMethod.ParameterInfoMap[i])));
                            }
                        }

                        continue;
                    }

                    // we are in a multipart method, add the part to the content
                    // the parameter name should be either the attachment name or the parameter name (as fallback)
                    string itemName;
                    string parameterName;

                    if (!restMethod.AttachmentNameMap.TryGetValue(i, out var attachment))
                    {
                        itemName = restMethod.QueryParameterMap[i];
                        parameterName = itemName;
                    }
                    else
                    {
                        itemName = attachment.Item1;
                        parameterName = attachment.Item2;
                    }

                    // Check to see if it's an IEnumerable
                    var itemValue = paramList[i];
                    var enumerable = itemValue as IEnumerable <object>;
                    var typeIsCollection = enumerable != null;

                    if (typeIsCollection)
                    {
                        foreach (var item in enumerable)
                        {
                            await AddMultipartItemAsync(multiPartContent, itemName, parameterName, item).ConfigureAwait(false);
                        }
                    }
                    else
                    {
                        await AddMultipartItemAsync(multiPartContent, itemName, parameterName, itemValue).ConfigureAwait(false);
                    }
                }

                // NB: We defer setting headers until the body has been
                // added so any custom content headers don't get left out.
                if (headersToAdd.Count > 0)
                {
                    // We could have content headers, so we need to make
                    // sure we have an HttpContent object to add them to,
                    // provided the HttpClient will allow it for the method
                    if (ret.Content == null && !bodylessMethods.Contains(ret.Method))
                    {
                        ret.Content = new ByteArrayContent(new byte[0]);
                    }

                    foreach (var header in headersToAdd)
                    {
                        SetHeader(ret, header.Key, header.Value);
                    }
                }

                // NB: The URI methods in .NET are dumb. Also, we do this
                // UriBuilder business so that we preserve any hardcoded query
                // parameters as well as add the parameterized ones.
                var uri = new UriBuilder(new Uri(new Uri("http://api"), urlTarget));
                var query = HttpUtility.ParseQueryString(uri.Query ?? "");
                foreach (var key in query.AllKeys)
                {
                    queryParamsToAdd.Insert(0, new KeyValuePair <string, string>(key, query[key]));
                }

                if (queryParamsToAdd.Any())
                {
                    var pairs = queryParamsToAdd.Where(x => x.Key != null && x.Value != null)
                                .Select(x => Uri.EscapeDataString(x.Key) + "=" + Uri.EscapeDataString(x.Value));
                    uri.Query = string.Join("&", pairs);
                }
                else
                {
                    uri.Query = null;
                }

                ret.RequestUri = new Uri(uri.Uri.GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped), UriKind.Relative);
                return ret;
            });
        }
Пример #13
0
        Func <HttpClient, CancellationToken, object[], Task <T> > BuildCancellableTaskFuncForMethod <T, TBody>(RestMethodInfo restMethod)
        {
            return(async(client, ct, paramList) =>
            {
                if (client.BaseAddress == null)
                {
                    throw new InvalidOperationException("BaseAddress must be set on the HttpClient instance");
                }

                var factory = BuildRequestFactoryForMethod(restMethod, client.BaseAddress.AbsolutePath, restMethod.CancellationToken != null);
                var rq = await factory(paramList).ConfigureAwait(false);

                HttpResponseMessage resp = null;
                HttpContent content = null;
                var disposeResponse = true;
                try
                {
                    resp = await client.SendAsync(rq, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);

                    content = resp.Content ?? new StringContent(string.Empty);

                    if (restMethod.SerializedReturnType == typeof(HttpResponseMessage))
                    {
                        disposeResponse = false; // caller has to dispose

                        // NB: This double-casting manual-boxing hate crime is the only way to make
                        // this work without a 'class' generic constraint. It could blow up at runtime
                        // and would be A Bad Idea if we hadn't already vetted the return type.
                        return (T)(object)resp;
                    }

                    if (!resp.IsSuccessStatusCode)
                    {
                        disposeResponse = false;

                        var exception = await ApiException.Create(rq, restMethod.HttpMethod, resp, restMethod.RefitSettings).ConfigureAwait(false);

                        if (restMethod.IsApiResponse)
                        {
                            return ApiResponse.Create <T>(resp, default(T), exception);
                        }

                        throw exception;
                    }

                    var serializedReturnType = restMethod.IsApiResponse ? restMethod.SerializedGenericArgument : restMethod.SerializedReturnType;

                    if (serializedReturnType == typeof(HttpContent))
                    {
                        disposeResponse = false; // caller has to clean up the content
                        if (restMethod.IsApiResponse)
                        {
                            return ApiResponse.Create <T>(resp, content);
                        }
                        return (T)(object)content;
                    }

                    if (serializedReturnType == typeof(Stream))
                    {
                        disposeResponse = false; // caller has to dispose
                        var stream = (object)await content.ReadAsStreamAsync().ConfigureAwait(false);

                        if (restMethod.IsApiResponse)
                        {
                            return ApiResponse.Create <T>(resp, stream);
                        }
                        return (T)stream;
                    }

                    if (serializedReturnType == typeof(string))
                    {
                        using (var stream = await content.ReadAsStreamAsync().ConfigureAwait(false))
                            using (var reader = new StreamReader(stream))
                            {
                                var str = (object)await reader.ReadToEndAsync().ConfigureAwait(false);

                                if (restMethod.IsApiResponse)
                                {
                                    return ApiResponse.Create <T>(resp, str);
                                }
                                return (T)str;
                            }
                    }

                    var body = await serializer.DeserializeAsync <TBody>(content);

                    if (restMethod.IsApiResponse)
                    {
                        return ApiResponse.Create <T>(resp, body);
                    }

                    //  Unfortunate side-effect of having no 'class' or 'T : TBody' constraints.
                    //  However, we know that T must be the same as TBody because IsApiResponse != true so
                    //  this code is safe at runtime.
                    return (T)(object)body;
                }
                finally
                {
                    // Ensure we clean up the request
                    // Especially important if it has open files/streams
                    rq.Dispose();
                    if (disposeResponse)
                    {
                        resp?.Dispose();
                        content?.Dispose();
                    }
                }
            });
        }
Пример #14
0
        Func <HttpClient, CancellationToken, object[], Task <T> > BuildCancellableTaskFuncForMethod <T>(RestMethodInfo restMethod)
        {
            return(async(client, ct, paramList) => {
                var factory = BuildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath, restMethod.CancellationToken != null);
                var rq = factory(paramList);

                var resp = await client.SendAsync(rq, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);

                if (restMethod.SerializedReturnType == typeof(HttpResponseMessage))
                {
                    // NB: This double-casting manual-boxing hate crime is the only way to make
                    // this work without a 'class' generic constraint. It could blow up at runtime
                    // and would be A Bad Idea if we hadn't already vetted the return type.
                    return (T)(object)resp;
                }

                if (!resp.IsSuccessStatusCode)
                {
                    throw await ApiException.Create(rq.RequestUri, restMethod.HttpMethod, resp, restMethod.RefitSettings).ConfigureAwait(false);
                }

                if (restMethod.SerializedReturnType == typeof(HttpContent))
                {
                    return (T)(object)resp.Content;
                }

                if (restMethod.SerializedReturnType == typeof(Stream))
                {
                    return (T)(object)await resp.Content.ReadAsStreamAsync().ConfigureAwait(false);
                }

                using (var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false))
                    using (var reader = new StreamReader(stream)) {
                        if (restMethod.SerializedReturnType == typeof(string))
                        {
                            return (T)(object)await reader.ReadToEndAsync().ConfigureAwait(false);
                        }

                        using (var jsonReader = new JsonTextReader(reader)) {
                            return serializer.Deserialize <T>(jsonReader);
                        }
                    }
            });
        }
Пример #15
0
        Func <HttpClient, CancellationToken, object[], Task <T> > BuildCancellableTaskFuncForMethod <T>(RestMethodInfo restMethod)
        {
            return(async(client, ct, paramList) => {
                var factory = BuildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath, restMethod.CancellationToken != null);
                var rq = factory(paramList);

                var resp = await client.SendAsync(rq, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);

                if (restMethod.SerializedReturnType == typeof(HttpResponseMessage))
                {
                    // NB: This double-casting manual-boxing hate crime is the only way to make
                    // this work without a 'class' generic constraint. It could blow up at runtime
                    // and would be A Bad Idea if we hadn't already vetted the return type.
                    return (T)(object)resp;
                }

                if (!resp.IsSuccessStatusCode)
                {
                    throw await ApiException.Create(rq.RequestUri, restMethod.HttpMethod, resp, restMethod.RefitSettings).ConfigureAwait(false);
                }

                if (restMethod.SerializedReturnType == typeof(HttpContent))
                {
                    return (T)(object)resp.Content;
                }

                var content = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);

                if (restMethod.SerializedReturnType == typeof(string))
                {
                    return (T)(object)content;
                }

                var deserializedObject = JsonConvert.DeserializeObject <T>(content, settings.JsonSerializerSettings);

                // enable message post-processor functionality on resulting object if implements interface,
                // so it is possible to access http response message even when resulting type
                // of the call is something more specific (e.g. to allow processing of response headers)
                if (deserializedObject is IHttpResponseMessagePostProcessor)
                {
                    (deserializedObject as IHttpResponseMessagePostProcessor).PostProcessHttpResponseMessage(resp);
                }

                return deserializedObject;
            });
        }
Пример #16
0
        Func <HttpClient, CancellationToken, object[], Task <T> > buildCancellableTaskFuncForMethod <T>(RestMethodInfo restMethod)
            where T : class
        {
            return(async(client, ct, paramList) => {
                var factory = BuildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath);
                var rq = factory(paramList);

                var resp = await client.SendAsync(rq, HttpCompletionOption.ResponseHeadersRead, ct);

                if (restMethod.SerializedReturnType == typeof(HttpResponseMessage))
                {
                    return resp as T;
                }

                if (!resp.IsSuccessStatusCode)
                {
                    throw await ApiException.Create(resp);
                }

                var ms = new MemoryStream();
                var fromStream = await resp.Content.ReadAsStreamAsync();

                await fromStream.CopyToAsync(ms, 4096, ct);

                var bytes = ms.ToArray();
                var content = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
                if (restMethod.SerializedReturnType == typeof(string))
                {
                    return content as T;
                }

                return JsonConvert.DeserializeObject <T>(content);
            });
        }