chore: 重构网络模块

oneRain 2019-07-31 15:10:39 +08:00
parent fdfec81181
commit d19f389e2a
13 changed files with 241 additions and 202 deletions

View File

@ -1,7 +1,6 @@
using NUnit.Framework; using NUnit.Framework;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using LeanCloud; using LeanCloud;
namespace LeanCloudTests { namespace LeanCloudTests {
@ -16,7 +15,6 @@ namespace LeanCloudTests {
} }
[Test] [Test]
[AsyncStateMachine(typeof(ObjectControllerTests))]
public async Task TestSave() { public async Task TestSave() {
TestContext.Out.WriteLine($"before at {Thread.CurrentThread.ManagedThreadId}"); TestContext.Out.WriteLine($"before at {Thread.CurrentThread.ManagedThreadId}");
var obj = AVObject.Create("Foo"); var obj = AVObject.Create("Foo");
@ -26,7 +24,6 @@ namespace LeanCloudTests {
Assert.NotNull(obj.ObjectId); Assert.NotNull(obj.ObjectId);
Assert.NotNull(obj.CreatedAt); Assert.NotNull(obj.CreatedAt);
Assert.NotNull(obj.UpdatedAt); Assert.NotNull(obj.UpdatedAt);
await Task.Delay(10000);
} }
} }
} }

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Net.Http;
namespace LeanCloud.Storage.Internal namespace LeanCloud.Storage.Internal
{ {
@ -43,7 +44,13 @@ namespace LeanCloud.Storage.Internal
string appId = AVClient.CurrentConfiguration.ApplicationId; string appId = AVClient.CurrentConfiguration.ApplicationId;
string url = string.Format("https://app-router.leancloud.cn/2/route?appId={0}", appId); string url = string.Format("https://app-router.leancloud.cn/2/route?appId={0}", appId);
var ret = await AVClient.HttpGetAsync(new Uri(url)); var request = new HttpRequest {
Uri = new Uri(url),
Method = HttpMethod.Get,
Headers = null,
Data = null
};
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
if (ret.Item1 != HttpStatusCode.OK) { if (ret.Item1 != HttpStatusCode.OK) {
throw new AVException(AVException.ErrorCode.ConnectionFailed, "can not reach router.", null); throw new AVException(AVException.ErrorCode.ConnectionFailed, "can not reach router.", null);
} }

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Text; using System.Text;
using LeanCloud.Storage.Internal; using LeanCloud.Storage.Internal;
using System.Linq; using System.Linq;
using System.Net.Http;
namespace LeanCloud.Storage.Internal namespace LeanCloud.Storage.Internal
{ {
@ -14,105 +15,154 @@ namespace LeanCloud.Storage.Internal
public class AVCommand : HttpRequest public class AVCommand : HttpRequest
{ {
public IDictionary<string, object> DataObject { get; private set; } public IDictionary<string, object> DataObject { get; private set; }
public override Stream Data
{ public object Body {
get get; set;
{
if (base.Data != null)
{
return base.Data;
} }
return base.Data = (DataObject != null public override Stream Data {
? new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(DataObject))) get {
: null); return new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(Body)));
} }
set { base.Data = value; }
} }
public AVCommand(string relativeUri, public AVCommand(string relativeUri,
string method, string method,
string sessionToken = null, string sessionToken = null,
IList<KeyValuePair<string, string>> headers = null, IList<KeyValuePair<string, string>> headers = null,
IDictionary<string, object> data = null) : this(relativeUri: relativeUri, object data = null)
method: method,
sessionToken: sessionToken,
headers: headers,
stream: null,
contentType: data != null ? "application/json" : null)
{
DataObject = data;
}
public AVCommand(string relativeUri,
string method,
string sessionToken = null,
IList<KeyValuePair<string, string>> headers = null,
Stream stream = null,
string contentType = null)
{ {
var state = AVPlugins.Instance.AppRouterController.Get(); var state = AVPlugins.Instance.AppRouterController.Get();
var urlTemplate = "https://{0}/{1}/{2}"; var urlTemplate = "https://{0}/{1}/{2}";
AVClient.Configuration configuration = AVClient.CurrentConfiguration; AVClient.Configuration configuration = AVClient.CurrentConfiguration;
var apiVersion = "1.1"; var apiVersion = "1.1";
if (relativeUri.StartsWith("push") || relativeUri.StartsWith("installations")) if (relativeUri.StartsWith("push") || relativeUri.StartsWith("installations")) {
{
Uri = new Uri(string.Format(urlTemplate, state.PushServer, apiVersion, relativeUri)); Uri = new Uri(string.Format(urlTemplate, state.PushServer, apiVersion, relativeUri));
if (configuration.PushServer != null) if (configuration.PushServer != null) {
{
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.PushServer, apiVersion, relativeUri)); Uri = new Uri(string.Format("{0}{1}/{2}", configuration.PushServer, apiVersion, relativeUri));
} }
} } else if (relativeUri.StartsWith("stats") || relativeUri.StartsWith("always_collect") || relativeUri.StartsWith("statistics")) {
else if (relativeUri.StartsWith("stats") || relativeUri.StartsWith("always_collect") || relativeUri.StartsWith("statistics"))
{
Uri = new Uri(string.Format(urlTemplate, state.StatsServer, apiVersion, relativeUri)); Uri = new Uri(string.Format(urlTemplate, state.StatsServer, apiVersion, relativeUri));
if (configuration.StatsServer != null) if (configuration.StatsServer != null) {
{
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.StatsServer, apiVersion, relativeUri)); Uri = new Uri(string.Format("{0}{1}/{2}", configuration.StatsServer, apiVersion, relativeUri));
} }
} } else if (relativeUri.StartsWith("functions") || relativeUri.StartsWith("call")) {
else if (relativeUri.StartsWith("functions") || relativeUri.StartsWith("call"))
{
Uri = new Uri(string.Format(urlTemplate, state.EngineServer, apiVersion, relativeUri)); Uri = new Uri(string.Format(urlTemplate, state.EngineServer, apiVersion, relativeUri));
if (configuration.EngineServer != null) if (configuration.EngineServer != null) {
{
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.EngineServer, apiVersion, relativeUri)); Uri = new Uri(string.Format("{0}{1}/{2}", configuration.EngineServer, apiVersion, relativeUri));
} }
} } else {
else
{
Uri = new Uri(string.Format(urlTemplate, state.ApiServer, apiVersion, relativeUri)); Uri = new Uri(string.Format(urlTemplate, state.ApiServer, apiVersion, relativeUri));
if (configuration.ApiServer != null) if (configuration.ApiServer != null) {
{
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.ApiServer, apiVersion, relativeUri)); Uri = new Uri(string.Format("{0}{1}/{2}", configuration.ApiServer, apiVersion, relativeUri));
} }
} }
Method = method; switch (method) {
Data = stream; case "GET":
Method = HttpMethod.Get;
break;
case "POST":
Method = HttpMethod.Post;
break;
case "DELETE":
Method = HttpMethod.Delete;
break;
case "PUT":
Method = HttpMethod.Put;
break;
case "HEAD":
Method = HttpMethod.Head;
break;
case "TRACE":
Method = HttpMethod.Trace;
break;
default:
break;
}
Body = data;
Headers = new List<KeyValuePair<string, string>>(headers ?? Enumerable.Empty<KeyValuePair<string, string>>()); Headers = new List<KeyValuePair<string, string>>(headers ?? Enumerable.Empty<KeyValuePair<string, string>>());
string useProduction = AVClient.UseProduction ? "1" : "0"; string useProduction = AVClient.UseProduction ? "1" : "0";
Headers.Add(new KeyValuePair<string, string>("X-LC-Prod", useProduction)); Headers.Add(new KeyValuePair<string, string>("X-LC-Prod", useProduction));
if (!string.IsNullOrEmpty(sessionToken)) if (!string.IsNullOrEmpty(sessionToken)) {
{
Headers.Add(new KeyValuePair<string, string>("X-LC-Session", sessionToken)); Headers.Add(new KeyValuePair<string, string>("X-LC-Session", sessionToken));
} }
if (!string.IsNullOrEmpty(contentType))
{ Headers.Add(new KeyValuePair<string, string>("Content-Type", "application/json"));
Headers.Add(new KeyValuePair<string, string>("Content-Type", contentType));
}
} }
//public AVCommand(string relativeUri,
// string method,
// string sessionToken = null,
// IList<KeyValuePair<string, string>> headers = null,
// Stream stream = null,
// string contentType = null)
//{
// var state = AVPlugins.Instance.AppRouterController.Get();
// var urlTemplate = "https://{0}/{1}/{2}";
// AVClient.Configuration configuration = AVClient.CurrentConfiguration;
// var apiVersion = "1.1";
// if (relativeUri.StartsWith("push") || relativeUri.StartsWith("installations"))
// {
// Uri = new Uri(string.Format(urlTemplate, state.PushServer, apiVersion, relativeUri));
// if (configuration.PushServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.PushServer, apiVersion, relativeUri));
// }
// }
// else if (relativeUri.StartsWith("stats") || relativeUri.StartsWith("always_collect") || relativeUri.StartsWith("statistics"))
// {
// Uri = new Uri(string.Format(urlTemplate, state.StatsServer, apiVersion, relativeUri));
// if (configuration.StatsServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.StatsServer, apiVersion, relativeUri));
// }
// }
// else if (relativeUri.StartsWith("functions") || relativeUri.StartsWith("call"))
// {
// Uri = new Uri(string.Format(urlTemplate, state.EngineServer, apiVersion, relativeUri));
// if (configuration.EngineServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.EngineServer, apiVersion, relativeUri));
// }
// }
// else
// {
// Uri = new Uri(string.Format(urlTemplate, state.ApiServer, apiVersion, relativeUri));
// if (configuration.ApiServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.ApiServer, apiVersion, relativeUri));
// }
// }
// Method = method;
// Data = stream;
// Headers = new List<KeyValuePair<string, string>>(headers ?? Enumerable.Empty<KeyValuePair<string, string>>());
// string useProduction = AVClient.UseProduction ? "1" : "0";
// Headers.Add(new KeyValuePair<string, string>("X-LC-Prod", useProduction));
// if (!string.IsNullOrEmpty(sessionToken))
// {
// Headers.Add(new KeyValuePair<string, string>("X-LC-Session", sessionToken));
// }
// if (!string.IsNullOrEmpty(contentType))
// {
// Headers.Add(new KeyValuePair<string, string>("Content-Type", contentType));
// }
//}
public AVCommand(AVCommand other) public AVCommand(AVCommand other)
{ {
this.Uri = other.Uri; this.Uri = other.Uri;
this.Method = other.Method; this.Method = other.Method;
this.DataObject = other.DataObject; this.DataObject = other.DataObject;
this.Headers = new List<KeyValuePair<string, string>>(other.Headers); this.Headers = new List<KeyValuePair<string, string>>(other.Headers);
this.Data = other.Data; this.Body = other.Data;
} }
} }
} }

View File

@ -35,7 +35,7 @@ namespace LeanCloud.Storage.Internal
/// <param name="downloadProgress"></param> /// <param name="downloadProgress"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
public Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(AVCommand command, public Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(HttpRequest command,
IProgress<AVUploadProgressEventArgs> uploadProgress = null, IProgress<AVUploadProgressEventArgs> uploadProgress = null,
IProgress<AVDownloadProgressEventArgs> downloadProgress = null, IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
@ -99,11 +99,11 @@ namespace LeanCloud.Storage.Internal
} }
private const string revocableSessionTokenTrueValue = "1"; private const string revocableSessionTokenTrueValue = "1";
private Task<AVCommand> PrepareCommand(AVCommand command) private Task<HttpRequest> PrepareCommand(HttpRequest command)
{ {
AVCommand newCommand = new AVCommand(command); HttpRequest newCommand = command;
Task<AVCommand> installationIdTask = installationIdController.GetAsync().ContinueWith(t => Task<HttpRequest> installationIdTask = installationIdController.GetAsync().ContinueWith(t =>
{ {
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-Installation-Id", t.Result.ToString())); newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-Installation-Id", t.Result.ToString()));
return newCommand; return newCommand;

View File

@ -16,7 +16,7 @@ namespace LeanCloud.Storage.Internal
/// <param name="downloadProgress">Download progress callback.</param> /// <param name="downloadProgress">Download progress callback.</param>
/// <param name="cancellationToken">The cancellation token for the request.</param> /// <param name="cancellationToken">The cancellation token for the request.</param>
/// <returns></returns> /// <returns></returns>
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(AVCommand command, Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(HttpRequest command,
IProgress<AVUploadProgressEventArgs> uploadProgress = null, IProgress<AVUploadProgressEventArgs> uploadProgress = null,
IProgress<AVDownloadProgressEventArgs> downloadProgress = null, IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
CancellationToken cancellationToken = default(CancellationToken)); CancellationToken cancellationToken = default(CancellationToken));

View File

@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using LeanCloud.Storage.Internal; using LeanCloud.Storage.Internal;
using System.Net; using System.Net;
using System.Net.Http;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -32,55 +33,36 @@ namespace LeanCloud.Storage.Internal
/// <param name="sessionToken">Session token.</param> /// <param name="sessionToken">Session token.</param>
/// <param name="progress">Progress.</param> /// <param name="progress">Progress.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
public virtual Task<FileState> SaveAsync(FileState state, public virtual async Task<FileState> SaveAsync(FileState state,
Stream dataStream, Stream dataStream,
String sessionToken, String sessionToken,
IProgress<AVUploadProgressEventArgs> progress, IProgress<AVUploadProgressEventArgs> progress,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken)) {
{ if (state.Url != null) {
if (state.Url != null)
{
// !isDirty // !isDirty
return Task<FileState>.FromResult(state); return state;
} }
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested) {
{ return null;
var tcs = new TaskCompletionSource<FileState>();
tcs.TrySetCanceled();
return tcs.Task;
} }
var oldPosition = dataStream.Position; var oldPosition = dataStream.Position;
var command = new AVCommand("files/" + state.Name,
method: "POST",
sessionToken: sessionToken,
contentType: state.MimeType,
stream: dataStream);
return commandRunner.RunCommandAsync(command, var request = new HttpRequest {
uploadProgress: progress, Uri = new Uri("files/" + state.Name),
cancellationToken: cancellationToken).OnSuccess(uploadTask => Method = HttpMethod.Post,
{ Headers = new List<KeyValuePair<string, string>> {
var result = uploadTask.Result; new KeyValuePair<string, string>("Content-Type", state.MimeType)
var jsonData = result.Item2; }
cancellationToken.ThrowIfCancellationRequested(); };
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
return new FileState var jsonData = Json.Parse(ret.Item2) as Dictionary<string, object>;
{ return new FileState {
Name = jsonData["name"] as string, Name = jsonData["name"] as string,
Url = new Uri(jsonData["url"] as string, UriKind.Absolute), Url = new Uri(jsonData["url"] as string, UriKind.Absolute),
MimeType = state.MimeType MimeType = state.MimeType
}; };
}).ContinueWith(t =>
{
// Rewind the stream on failure or cancellation (if possible)
if ((t.IsFaulted || t.IsCanceled) && dataStream.CanSeek)
{
dataStream.Seek(oldPosition, SeekOrigin.Begin);
}
return t;
}).Unwrap();
} }
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken) public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken)
{ {

View File

@ -4,6 +4,7 @@ using System.Threading;
using System.IO; using System.IO;
using LeanCloud.Storage.Internal; using LeanCloud.Storage.Internal;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http;
namespace LeanCloud.Storage.Internal namespace LeanCloud.Storage.Internal
{ {
@ -40,15 +41,18 @@ namespace LeanCloud.Storage.Internal
}); });
} }
internal Task<FileState> PutFile(FileState state, string uploadUrl, Stream dataStream) internal async Task<FileState> PutFile(FileState state, string uploadUrl, Stream dataStream)
{ {
IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>(); IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>();
makeBlockHeaders.Add(new KeyValuePair<string, string>("Content-Type", state.MimeType)); makeBlockHeaders.Add(new KeyValuePair<string, string>("Content-Type", state.MimeType));
var request = new HttpRequest {
return AVClient.RequestAsync(new Uri(uploadUrl), "PUT", makeBlockHeaders, dataStream, state.MimeType, CancellationToken.None).OnSuccess(t => Uri = new Uri(uploadUrl),
{ Method = HttpMethod.Put,
Headers = makeBlockHeaders,
Data = dataStream
};
await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
return state; return state;
});
} }
} }
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -159,7 +160,7 @@ namespace LeanCloud.Storage.Internal
return HexStringFromBytes(hashBytes); return HexStringFromBytes(hashBytes);
} }
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> PostToQCloud( async Task<Tuple<HttpStatusCode, IDictionary<string, object>>> PostToQCloud(
Dictionary<string, object> body, Dictionary<string, object> body,
byte[] sliceFile, byte[] sliceFile,
CancellationToken cancellationToken) CancellationToken cancellationToken)
@ -174,14 +175,15 @@ namespace LeanCloud.Storage.Internal
sliceHeaders.Add(new KeyValuePair<string, string>("Content-Type", contentType)); sliceHeaders.Add(new KeyValuePair<string, string>("Content-Type", contentType));
var rtn = AVClient.RequestAsync(new Uri(this.uploadUrl), "POST", sliceHeaders, tempStream, null, cancellationToken).OnSuccess(_ => var request = new HttpRequest {
{ Uri = new Uri(this.uploadUrl),
var dic = AVClient.ReponseResolve(_.Result, CancellationToken.None); Method = HttpMethod.Post,
Headers = sliceHeaders,
return dic; Data = tempStream
}); };
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
return rtn; var result = new Tuple<HttpStatusCode, IDictionary<string, object>>(ret.Item1, Json.Parse(ret.Item2) as Dictionary<string, object>);
return result;
} }
public static Stream HttpUploadFile(byte[] file, string fileName, out string contentType, out long contentLength, IDictionary<string, object> nvc) public static Stream HttpUploadFile(byte[] file, string fileName, out string contentType, out long contentLength, IDictionary<string, object> nvc)
{ {

View File

@ -6,6 +6,7 @@ using System.Net;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http;
namespace LeanCloud.Storage.Internal namespace LeanCloud.Storage.Internal
{ {
@ -173,20 +174,32 @@ namespace LeanCloud.Storage.Internal
return makeBlockHeaders; return makeBlockHeaders;
} }
Task<Tuple<HttpStatusCode, string>> MakeBlock(FileState state, byte[] firstChunkBinary, long blcokSize = 4194304) async Task<Tuple<HttpStatusCode, string>> MakeBlock(FileState state, byte[] firstChunkBinary, long blcokSize = 4194304) {
{
MemoryStream firstChunkData = new MemoryStream(firstChunkBinary, 0, firstChunkBinary.Length); MemoryStream firstChunkData = new MemoryStream(firstChunkBinary, 0, firstChunkBinary.Length);
return AVClient.RequestAsync(new Uri(new Uri(UP_HOST) + string.Format("mkblk/{0}", blcokSize)), "POST", GetQiniuRequestHeaders(state), firstChunkData, "application/octet-stream", CancellationToken.None); 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)),
Method = HttpMethod.Post,
Headers = headers,
Data = firstChunkData
};
return await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
} }
Task<Tuple<HttpStatusCode, string>> PutChunk(FileState state, byte[] chunkBinary, string LastChunkctx, long currentChunkOffsetInBlock)
{ async Task<Tuple<HttpStatusCode, string>> PutChunk(FileState state, byte[] chunkBinary, string LastChunkctx, long currentChunkOffsetInBlock) {
MemoryStream chunkData = new MemoryStream(chunkBinary, 0, chunkBinary.Length); MemoryStream chunkData = new MemoryStream(chunkBinary, 0, chunkBinary.Length);
return AVClient.RequestAsync(new Uri(new Uri(UP_HOST) + string.Format("bput/{0}/{1}", LastChunkctx, var request = new HttpRequest {
currentChunkOffsetInBlock)), "POST", Uri = new Uri(new Uri(UP_HOST) + string.Format("bput/{0}/{1}", LastChunkctx, currentChunkOffsetInBlock)),
GetQiniuRequestHeaders(state), chunkData, Method = HttpMethod.Post,
"application/octet-stream", CancellationToken.None); Headers = GetQiniuRequestHeaders(state),
Data = chunkData
};
var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
return ret;
} }
internal Task<Tuple<HttpStatusCode, string>> QiniuMakeFile(FileState state, Stream dataStream, string upToken, string key, long fsize, string[] ctxes, CancellationToken cancellationToken)
internal async Task<Tuple<HttpStatusCode, string>> QiniuMakeFile(FileState state, Stream dataStream, string upToken, string key, long fsize, string[] ctxes, CancellationToken cancellationToken)
{ {
StringBuilder urlBuilder = new StringBuilder(); StringBuilder urlBuilder = new StringBuilder();
urlBuilder.AppendFormat("{0}/mkfile/{1}", UP_HOST, fsize); urlBuilder.AppendFormat("{0}/mkfile/{1}", UP_HOST, fsize);
@ -208,6 +221,7 @@ namespace LeanCloud.Storage.Internal
string authHead = "UpToken " + upToken; string authHead = "UpToken " + upToken;
headers.Add(new KeyValuePair<string, string>("Authorization", authHead)); headers.Add(new KeyValuePair<string, string>("Authorization", authHead));
headers.Add(new KeyValuePair<string, string>("Content-Type", "text/plain"));
int proCount = ctxes.Length; int proCount = ctxes.Length;
Stream body = new MemoryStream(); Stream body = new MemoryStream();
@ -221,13 +235,14 @@ namespace LeanCloud.Storage.Internal
} }
} }
body.Seek(0, SeekOrigin.Begin); body.Seek(0, SeekOrigin.Begin);
var request = new HttpRequest {
var rtn = AVClient.RequestAsync(new Uri(urlBuilder.ToString()), "POST", headers, body, "text/plain", cancellationToken).OnSuccess(_ => Uri = new Uri(urlBuilder.ToString()),
{ Method = HttpMethod.Post,
var dic = AVClient.ReponseResolve(_.Result, CancellationToken.None); Headers = headers,
return _.Result; Data = body
}); };
return rtn; var ret = await AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, CancellationToken.None);
return ret;
} }
internal void MergeFromJSON(FileState state, IDictionary<string, object> jsonData) internal void MergeFromJSON(FileState state, IDictionary<string, object> jsonData)
{ {

View File

@ -42,17 +42,16 @@ namespace LeanCloud.Storage.Internal {
IProgress<AVDownloadProgressEventArgs> downloadProgress, IProgress<AVDownloadProgressEventArgs> downloadProgress,
CancellationToken cancellationToken) { CancellationToken cancellationToken) {
HttpMethod httpMethod = new HttpMethod(httpRequest.Method); HttpMethod httpMethod = httpRequest.Method;
HttpRequestMessage message = new HttpRequestMessage(httpMethod, httpRequest.Uri); HttpRequestMessage message = new HttpRequestMessage(httpMethod, httpRequest.Uri);
// Fill in zero-length data if method is post. // Fill in zero-length data if method is post.
Stream data = httpRequest.Data; if (httpRequest.Data == null && httpRequest.Method == HttpMethod.Post) {
if (httpRequest.Data == null && httpRequest.Method.ToLower().Equals("post")) { message.Content = new StreamContent(new MemoryStream(new byte[0]));
data = new MemoryStream(new byte[0]);
} }
if (data != null) { if (httpRequest.Data != null) {
message.Content = new StreamContent(data); message.Content = new StreamContent(httpRequest.Data);
} }
if (httpRequest.Headers != null) { if (httpRequest.Headers != null) {
@ -74,6 +73,7 @@ namespace LeanCloud.Storage.Internal {
uploadProgress?.Report(new AVUploadProgressEventArgs { Progress = 0 }); uploadProgress?.Report(new AVUploadProgressEventArgs { Progress = 0 });
var response = await client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken); var response = await client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
uploadProgress?.Report(new AVUploadProgressEventArgs { Progress = 1 }); uploadProgress?.Report(new AVUploadProgressEventArgs { Progress = 1 });
message.Dispose();
var resultString = await response.Content.ReadAsStringAsync(); var resultString = await response.Content.ReadAsStringAsync();
response.Dispose(); response.Dispose();

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http;
namespace LeanCloud.Storage.Internal namespace LeanCloud.Storage.Internal
{ {
@ -10,16 +11,12 @@ namespace LeanCloud.Storage.Internal
public class HttpRequest public class HttpRequest
{ {
public Uri Uri { get; set; } public Uri Uri { get; set; }
public IList<KeyValuePair<string, string>> Headers { get; set; } public IList<KeyValuePair<string, string>> Headers { get; set; }
/// <summary> // HttpMethod
/// Data stream to be uploaded. public HttpMethod Method { get; set; }
/// </summary>
public virtual Stream Data { get; set; }
/// <summary> public virtual Stream Data { get; set; }
/// HTTP method. One of <c>DELETE</c>, <c>GET</c>, <c>HEAD</c>, <c>POST</c> or <c>PUT</c>
/// </summary>
public string Method { get; set; }
} }
} }

View File

@ -16,7 +16,7 @@ namespace LeanCloud
/// AVClient contains static functions that handle global /// AVClient contains static functions that handle global
/// configuration for the LeanCloud library. /// configuration for the LeanCloud library.
/// </summary> /// </summary>
public static partial class AVClient public static class AVClient
{ {
public static readonly string[] DateFormatStrings = { public static readonly string[] DateFormatStrings = {
// Official ISO format // Official ISO format
@ -367,37 +367,37 @@ namespace LeanCloud
return Json.Encode(jsonData); return Json.Encode(jsonData);
} }
public static Task<Tuple<HttpStatusCode, string>> HttpGetAsync(Uri uri) //public static Task<Tuple<HttpStatusCode, string>> HttpGetAsync(Uri uri)
{ //{
return RequestAsync(uri, "GET", null, body: null, contentType: null, cancellationToken: CancellationToken.None); // return RequestAsync(uri, "GET", null, body: null, contentType: null, cancellationToken: CancellationToken.None);
} //}
public static Task<Tuple<HttpStatusCode, string>> RequestAsync(Uri uri, string method, IList<KeyValuePair<string, string>> headers, IDictionary<string, object> body, string contentType, CancellationToken cancellationToken) //public static Task<Tuple<HttpStatusCode, string>> RequestAsync(Uri uri, string method, IList<KeyValuePair<string, string>> headers, IDictionary<string, object> body, string contentType, CancellationToken cancellationToken)
{ //{
var dataStream = body != null ? new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(body))) : null; // var dataStream = body != null ? new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(body))) : null;
return AVClient.RequestAsync(uri, method, headers, dataStream, contentType, cancellationToken); // return AVClient.RequestAsync(uri, method, headers, dataStream, contentType, cancellationToken);
//return AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, cancellationToken); // //return AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, cancellationToken);
} //}
public static Task<Tuple<HttpStatusCode, string>> RequestAsync(Uri uri, string method, IList<KeyValuePair<string, string>> headers, Stream data, string contentType, CancellationToken cancellationToken) //public static Task<Tuple<HttpStatusCode, string>> RequestAsync(Uri uri, string method, IList<KeyValuePair<string, string>> headers, Stream data, string contentType, CancellationToken cancellationToken)
{ //{
HttpRequest request = new HttpRequest() // HttpRequest request = new HttpRequest()
{ // {
Data = data != null ? data : null, // Data = data != null ? data : null,
Headers = headers, // Headers = headers,
Method = method, // Method = method,
Uri = uri // Uri = uri
}; // };
return AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, cancellationToken).OnSuccess(t => // return AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, cancellationToken).OnSuccess(t =>
{ // {
var response = t.Result; // var response = t.Result;
var contentString = response.Item2; // var contentString = response.Item2;
int responseCode = (int)response.Item1; // int responseCode = (int)response.Item1;
var responseLog = responseCode + ";" + contentString; // var responseLog = responseCode + ";" + contentString;
PrintLog(responseLog); // PrintLog(responseLog);
return response; // return response;
}); // });
} //}
internal static Tuple<HttpStatusCode, IDictionary<string, object>> ReponseResolve(Tuple<HttpStatusCode, string> response, CancellationToken cancellationToken) internal static Tuple<HttpStatusCode, IDictionary<string, object>> ReponseResolve(Tuple<HttpStatusCode, string> response, CancellationToken cancellationToken)
{ {
@ -477,22 +477,9 @@ namespace LeanCloud
try try
{ {
if (request is AVCommand) var bodyLog = "Body:" + Json.Encode(request.Data);
{
var command = (AVCommand)request;
if (command.DataObject != null)
{
var bodyLog = "Body:" + Json.Encode(command.DataObject);
sb.AppendLine(bodyLog); sb.AppendLine(bodyLog);
} }
}
else
{
StreamReader reader = new StreamReader(request.Data);
string bodyLog = reader.ReadToEnd();
sb.AppendLine(bodyLog);
}
}
catch (Exception) catch (Exception)
{ {

View File

@ -211,9 +211,7 @@ namespace LeanCloud {
if (overwrite) { if (overwrite) {
path = string.Format("{0}?overwrite=1", path); path = string.Format("{0}?overwrite=1", path);
} }
var dataStr = Json.Encode(data); var command = new AVCommand(path, "POST", user.SessionToken, data: data);
var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(dataStr));
var command = new AVCommand(path, "POST", contentType: "application/json", sessionToken: user.SessionToken, stream: dataStream);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
try { try {
List<AVStatistic> statisticList = new List<AVStatistic>(); List<AVStatistic> statisticList = new List<AVStatistic>();