Func <HttpClient, object[], Task> BuildVoidTaskFuncForMethod(RestMethodInfo restMethod) { return(async(client, 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); 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); } } }); }
Func <HttpClient, object[], Task <T> > buildTaskFuncForMethod <T>(RestMethodInfo restMethod) where T : class { return(async(client, paramList) => { var factory = BuildRequestFactoryForMethod(restMethod.Name, client.BaseAddress.AbsolutePath); var rq = factory(paramList); var resp = await client.SendAsync(rq); if (restMethod.SerializedReturnType == typeof(HttpResponseMessage)) { return resp as T; } if (!resp.IsSuccessStatusCode) { throw await ApiException.Create(resp); } var content = await resp.Content.ReadAsStringAsync(); if (restMethod.SerializedReturnType == typeof(string)) { return content as T; } return JsonConvert.DeserializeObject <T>(content); }); }
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); }); }
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); } }); }
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); 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); } } }); }
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; }); }
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, 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; } var isApiResponse = restMethod.SerializedReturnType.GetTypeInfo().IsGenericType&& restMethod.SerializedReturnType.GetGenericTypeDefinition() == typeof(ApiResponse <>); if (!resp.IsSuccessStatusCode && !isApiResponse) { disposeResponse = false; throw await ApiException.Create(rq, restMethod.HttpMethod, resp, restMethod.RefitSettings).ConfigureAwait(false); } var serializedReturnType = (isApiResponse) ? restMethod.SerializedGenericArgument : restMethod.SerializedReturnType; if (serializedReturnType == typeof(HttpContent)) { disposeResponse = false; // caller has to clean up the content if (isApiResponse) { return ApiResponse.Create <T>(resp, resp.Content); } return (T)(object)resp.Content; } if (serializedReturnType == typeof(Stream)) { disposeResponse = false; // caller has to dispose var stream = (object)await resp.Content.ReadAsStreamAsync().ConfigureAwait(false); if (isApiResponse) { return ApiResponse.Create <T>(resp, stream); } return (T)stream; } using (var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false)) using (var reader = new StreamReader(stream)) { if (serializedReturnType == typeof(string)) { var str = (object)await reader.ReadToEndAsync().ConfigureAwait(false); if (isApiResponse) { return ApiResponse.Create <T>(resp, str); } return (T)str; } using (var jsonReader = new JsonTextReader(reader)) { var json = serializer.Deserialize(jsonReader, serializedReturnType); if (isApiResponse) { return ApiResponse.Create <T>(resp, json); } return (T)json; } } } finally { // Ensure we clean up the request // Especially important if it has open files/streams rq.Dispose(); if (disposeResponse) { resp?.Dispose(); } } }); }
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(); } } }); }