chore: 删除 HttpClient,使用 .Net 标准库中的 HttpClient
parent
f2ec04a331
commit
148f51967e
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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}");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue