chore: 删除 HttpClient,使用 .Net 标准库中的 HttpClient

oneRain 2019-08-09 14:32:28 +08:00
parent f2ec04a331
commit 148f51967e
9 changed files with 93 additions and 217 deletions

View File

@ -16,7 +16,6 @@ namespace LeanCloud.Storage.Internal {
#region Server Controllers
private HttpClient httpClient;
private AppRouterController appRouterController;
private AVCommandRunner commandRunner;
private StorageController storageController;
@ -40,7 +39,6 @@ namespace LeanCloud.Storage.Internal {
public void Reset() {
lock (mutex) {
HttpClient = null;
AppRouterController = null;
CommandRunner = null;
StorageController = null;
@ -57,20 +55,6 @@ namespace LeanCloud.Storage.Internal {
}
}
public HttpClient HttpClient {
get {
lock (mutex) {
httpClient = httpClient ?? new HttpClient();
return httpClient;
}
}
set {
lock (mutex) {
httpClient = value;
}
}
}
public AppRouterController AppRouterController {
get {
lock (mutex) {

View File

@ -36,18 +36,23 @@ namespace LeanCloud.Storage.Internal {
string appId = AVClient.CurrentConfiguration.ApplicationId;
string url = string.Format("https://app-router.leancloud.cn/2/route?appId={0}", appId);
var request = new HttpRequest {
Uri = new Uri(url),
Method = HttpMethod.Get,
Headers = null,
Data = null
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage {
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
if (ret.Item1 != HttpStatusCode.OK) {
throw new AVException(AVException.ErrorCode.ConnectionFailed, "can not reach router.", null);
}
try {
HttpResponseMessage response = await client.SendAsync(request);
client.Dispose();
request.Dispose();
return await JsonUtils.DeserializeObjectAsync<AppRouterState>(ret.Item2);
string content = await response.Content.ReadAsStringAsync();
response.Dispose();
return await JsonUtils.DeserializeObjectAsync<AppRouterState>(content);
} catch (Exception e) {
throw new AVException(AVException.ErrorCode.ConnectionFailed, "can not reach router.", e);
}
}
public void Clear() {

View File

@ -18,10 +18,10 @@ namespace LeanCloud.Storage.Internal {
const string USE_PRODUCTION = "1";
const string USE_DEVELOPMENT = "0";
private readonly System.Net.Http.HttpClient httpClient;
private readonly HttpClient httpClient;
public AVCommandRunner() {
httpClient = new System.Net.Http.HttpClient();
httpClient = new HttpClient();
ProductHeaderValue product = new ProductHeaderValue(AVClient.Name, AVClient.Version);
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(product));
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(APPLICATION_JSON));
@ -48,15 +48,9 @@ namespace LeanCloud.Storage.Internal {
///
/// </summary>
/// <param name="command"></param>
/// <param name="uploadProgress"></param>
/// <param name="downloadProgress"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<Tuple<HttpStatusCode, T>> RunCommandAsync<T>(AVCommand command,
IProgress<AVUploadProgressEventArgs> uploadProgress = null,
IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
CancellationToken cancellationToken = default) {
public async Task<Tuple<HttpStatusCode, T>> RunCommandAsync<T>(AVCommand command,CancellationToken cancellationToken = default) {
string content = JsonConvert.SerializeObject(command.Content);
var request = new HttpRequestMessage {
RequestUri = command.Uri,
@ -115,7 +109,7 @@ namespace LeanCloud.Storage.Internal {
return new Tuple<HttpStatusCode, T>(responseCode, default);
}
static void PrintRequest(System.Net.Http.HttpClient client, HttpRequestMessage request, string content) {
static void PrintRequest(HttpClient client, HttpRequestMessage request, string content) {
StringBuilder sb = new StringBuilder();
sb.AppendLine("=== HTTP Request Start ===");
sb.AppendLine($"URL: {request.RequestUri}");

View File

@ -6,53 +6,15 @@ using System.Net;
using System.Net.Http;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal {
/// <summary>
/// AVF ile controller.
/// </summary>
public class AVFileController {
/// <summary>
/// Saves the async.
/// </summary>
/// <returns>The async.</returns>
/// <param name="state">State.</param>
/// <param name="dataStream">Data stream.</param>
/// <param name="sessionToken">Session token.</param>
/// <param name="progress">Progress.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public virtual async Task<FileState> SaveAsync(FileState state,
public abstract class AVFileController {
public abstract Task<FileState> SaveAsync(FileState state,
Stream dataStream,
String sessionToken,
IProgress<AVUploadProgressEventArgs> progress,
CancellationToken cancellationToken = default(CancellationToken)) {
if (state.Url != null) {
// !isDirty
return state;
}
CancellationToken cancellationToken = default(CancellationToken));
if (cancellationToken.IsCancellationRequested) {
return null;
}
var oldPosition = dataStream.Position;
var request = new HttpRequest {
Uri = new Uri("files/" + state.Name),
Method = HttpMethod.Post,
Headers = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("Content-Type", state.MimeType)
}
};
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
var jsonData = JsonConvert.DeserializeObject<Dictionary<string, object>>(ret.Item2, new LeanCloudJsonConverter());
return new FileState {
Name = jsonData["name"] as string,
Url = new Uri(jsonData["url"] as string, UriKind.Absolute),
MimeType = state.MimeType
};
}
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand {
Path = $"files/{state.ObjectId}",
@ -60,6 +22,7 @@ namespace LeanCloud.Storage.Internal {
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
}
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken) {
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
string currentSessionToken = AVUser.CurrentSessionToken;
@ -78,6 +41,7 @@ namespace LeanCloud.Storage.Internal {
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
}
public Task<FileState> GetAsync(string objectId, string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand {
Path = $"files/{objectId}",
@ -94,6 +58,7 @@ namespace LeanCloud.Storage.Internal {
};
});
}
internal static string GetUniqueName(FileState fileState) {
string key = Random(12);
string extension = Path.GetExtension(fileState.Name);
@ -101,12 +66,14 @@ namespace LeanCloud.Storage.Internal {
fileState.CloudName = key;
return key;
}
internal static string Random(int length) {
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
var random = new Random();
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
internal static double CalcProgress(double already, double total) {
var pv = (1.0 * already / total);
return Math.Round(pv, 3);

View File

@ -2,7 +2,6 @@
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using LeanCloud.Storage.Internal;
using System.Collections.Generic;
using System.Net.Http;
@ -29,13 +28,19 @@ namespace LeanCloud.Storage.Internal {
internal async Task<FileState> PutFile(FileState state, string uploadUrl, Stream dataStream) {
IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>();
makeBlockHeaders.Add(new KeyValuePair<string, string>("Content-Type", state.MimeType));
var request = new HttpRequest {
Uri = new Uri(uploadUrl),
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage {
RequestUri = new Uri(uploadUrl),
Method = HttpMethod.Put,
Headers = makeBlockHeaders,
Data = dataStream
Content = new StreamContent(dataStream)
};
await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
foreach (var header in makeBlockHeaders) {
request.Headers.Add(header.Key, header.Value);
}
await client.SendAsync(request);
client.Dispose();
request.Dispose();
return state;
}
}

View File

@ -20,7 +20,7 @@ namespace LeanCloud.Storage.Internal {
bool done;
private long sliceSize = (long)CommonSize.KB512;
public Task<FileState> SaveAsync(FileState state,
public override Task<FileState> SaveAsync(FileState state,
Stream dataStream,
string sessionToken,
IProgress<AVUploadProgressEventArgs> progress,
@ -132,6 +132,7 @@ namespace LeanCloud.Storage.Internal {
return HexStringFromBytes(hashBytes);
}
async Task<Tuple<HttpStatusCode, IDictionary<string, object>>> PostToQCloud(
Dictionary<string, object> body,
byte[] sliceFile,
@ -146,16 +147,22 @@ namespace LeanCloud.Storage.Internal {
sliceHeaders.Add(new KeyValuePair<string, string>("Content-Type", contentType));
var request = new HttpRequest {
Uri = new Uri(this.uploadUrl),
var client = new HttpClient();
var request = new HttpRequestMessage {
RequestUri = new Uri(uploadUrl),
Method = HttpMethod.Post,
Headers = sliceHeaders,
Data = tempStream
Content = new StreamContent(tempStream)
};
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
var result = new Tuple<HttpStatusCode, IDictionary<string, object>>(ret.Item1,
JsonConvert.DeserializeObject<Dictionary<string, object>>(ret.Item2, new LeanCloudJsonConverter()));
return result;
foreach (var header in sliceHeaders) {
request.Headers.Add(header.Key, header.Value);
}
var response = await client.SendAsync(request);
client.Dispose();
request.Dispose();
var content = await response.Content.ReadAsStringAsync();
response.Dispose();
// TODO 修改反序列化返回
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, IDictionary<string, object>>>(content);
}
public static Stream HttpUploadFile(byte[] file, string fileName, out string contentType, out long contentLength, IDictionary<string, object> nvc) {
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");

View File

@ -175,25 +175,41 @@ namespace LeanCloud.Storage.Internal
MemoryStream firstChunkData = new MemoryStream(firstChunkBinary, 0, firstChunkBinary.Length);
var headers = GetQiniuRequestHeaders(state);
headers.Add(new KeyValuePair<string, string>("Content-Type", "application/octet-stream"));
var request = new HttpRequest {
Uri = new Uri(new Uri(UP_HOST) + string.Format("mkblk/{0}", blcokSize)),
var client = new HttpClient();
var request = new HttpRequestMessage {
RequestUri = new Uri($"{UP_HOST}/mkblk/{blcokSize}"),
Method = HttpMethod.Post,
Headers = headers,
Data = firstChunkData
Content = new StreamContent(firstChunkData)
};
return await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
foreach (var header in headers) {
request.Headers.Add(header.Key, header.Value);
}
var response = await client.SendAsync(request);
client.Dispose();
request.Dispose();
var content = await response.Content.ReadAsStringAsync();
response.Dispose();
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
}
async Task<Tuple<HttpStatusCode, string>> PutChunk(FileState state, byte[] chunkBinary, string LastChunkctx, long currentChunkOffsetInBlock) {
MemoryStream chunkData = new MemoryStream(chunkBinary, 0, chunkBinary.Length);
var request = new HttpRequest {
Uri = new Uri(new Uri(UP_HOST) + string.Format("bput/{0}/{1}", LastChunkctx, currentChunkOffsetInBlock)),
var client = new HttpClient();
var request = new HttpRequestMessage {
RequestUri = new Uri($"{UP_HOST}/bput/{LastChunkctx}/{currentChunkOffsetInBlock}"),
Method = HttpMethod.Post,
Headers = GetQiniuRequestHeaders(state),
Data = chunkData
Content = new StreamContent(chunkData)
};
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
return ret;
var headers = GetQiniuRequestHeaders(state);
foreach (var header in headers) {
request.Headers.Add(header.Key, header.Value);
}
var response = await client.SendAsync(request);
client.Dispose();
request.Dispose();
var content = await response.Content.ReadAsStringAsync();
response.Dispose();
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
}
internal async Task<Tuple<HttpStatusCode, string>> QiniuMakeFile(FileState state, Stream dataStream, string upToken, string key, long fsize, string[] ctxes, CancellationToken cancellationToken)
@ -232,15 +248,21 @@ namespace LeanCloud.Storage.Internal
}
}
body.Seek(0, SeekOrigin.Begin);
var request = new HttpRequest {
Uri = new Uri(urlBuilder.ToString()),
var client = new HttpClient();
var request = new HttpRequestMessage {
RequestUri = new Uri(urlBuilder.ToString()),
Method = HttpMethod.Post,
Headers = headers,
Data = body
Content = new StreamContent(body)
};
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
return ret;
var response = await client.SendAsync(request);
client.Dispose();
request.Dispose();
var content = await response.Content.ReadAsStringAsync();
response.Dispose();
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
}
internal void MergeFromJSON(FileState state, IDictionary<string, object> jsonData)
{
lock (this.mutex)

View File

@ -1,86 +0,0 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using System.IO;
using NetHttpClient = System.Net.Http.HttpClient;
namespace LeanCloud.Storage.Internal {
public class HttpClient {
static readonly HashSet<string> HttpContentHeaders = new HashSet<string> {
{ "Allow" },
{ "Content-Disposition" },
{ "Content-Encoding" },
{ "Content-Language" },
{ "Content-Length" },
{ "Content-Location" },
{ "Content-MD5" },
{ "Content-Range" },
{ "Content-Type" },
{ "Expires" },
{ "Last-Modified" }
};
readonly NetHttpClient client;
public HttpClient() {
client = new NetHttpClient();
// 设置版本号
client.DefaultRequestHeaders.Add("User-Agent", $"LeanCloud-csharp-sdk-{AVClient.Version}");
}
public HttpClient(NetHttpClient client) {
this.client = client;
}
public async Task<Tuple<HttpStatusCode, string>> ExecuteAsync(HttpRequest httpRequest,
IProgress<AVUploadProgressEventArgs> uploadProgress,
IProgress<AVDownloadProgressEventArgs> downloadProgress,
CancellationToken cancellationToken) {
HttpMethod httpMethod = httpRequest.Method;
HttpRequestMessage message = new HttpRequestMessage(httpMethod, httpRequest.Uri);
// Fill in zero-length data if method is post.
if (httpRequest.Data == null && httpRequest.Method == HttpMethod.Post) {
message.Content = new StreamContent(new MemoryStream(new byte[0]));
}
if (httpRequest.Data != null) {
message.Content = new StreamContent(httpRequest.Data);
}
if (httpRequest.Headers != null) {
foreach (var header in httpRequest.Headers) {
if (!string.IsNullOrEmpty(header.Value)) {
if (HttpContentHeaders.Contains(header.Key)) {
message.Content.Headers.Add(header.Key, header.Value);
} else {
message.Headers.Add(header.Key, header.Value);
}
}
}
}
// Avoid aggressive caching on Windows Phone 8.1.
message.Headers.Add("Cache-Control", "no-cache");
message.Headers.IfModifiedSince = DateTimeOffset.UtcNow;
uploadProgress?.Report(new AVUploadProgressEventArgs { Progress = 0 });
var response = await client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
uploadProgress?.Report(new AVUploadProgressEventArgs { Progress = 1 });
message.Dispose();
var resultString = await response.Content.ReadAsStringAsync();
response.Dispose();
downloadProgress?.Report(new AVDownloadProgressEventArgs { Progress = 1 });
return new Tuple<HttpStatusCode, string>(response.StatusCode, resultString);
}
}
}

View File

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
namespace LeanCloud.Storage.Internal
{
/// <summary>
/// <code>IHttpRequest</code> is an interface that provides an API to execute HTTP request data.
/// </summary>
public class HttpRequest
{
public Uri Uri { get; set; }
public IList<KeyValuePair<string, string>> Headers { get; set; }
// HttpMethod
public HttpMethod Method { get; set; }
public virtual Stream Data { get; set; }
}
}