chore: 同步修复华东节点上传文件的 bug

oneRain 2019-08-13 16:08:10 +08:00
parent ba39a81eb5
commit 991446940a
12 changed files with 109 additions and 548 deletions

View File

@ -23,7 +23,6 @@ namespace LeanCloud.Storage.Internal {
private AVFileController fileController;
private AVObjectController objectController;
private AVQueryController queryController;
private AVSessionController sessionController;
private AVUserController userController;
private ObjectSubclassingController subclassingController;
@ -43,7 +42,6 @@ namespace LeanCloud.Storage.Internal {
CloudCodeController = null;
FileController = null;
ObjectController = null;
SessionController = null;
UserController = null;
SubclassingController = null;
@ -98,20 +96,8 @@ namespace LeanCloud.Storage.Internal {
if (fileController != null) {
return fileController;
}
lock (mutex) {
switch (AVClient.CurrentConfiguration.RegionValue) {
case 0:
fileController = new QiniuFileController();
break;
case 2:
fileController = new QCloudCosFileController();
break;
case 1:
fileController = new AWSS3FileController();
break;
}
return fileController;
}
fileController = new AVFileController();
return fileController;
}
set {
lock (mutex) {
@ -150,20 +136,6 @@ namespace LeanCloud.Storage.Internal {
}
}
public AVSessionController SessionController {
get {
lock (mutex) {
sessionController = sessionController ?? new AVSessionController();
return sessionController;
}
}
set {
lock (mutex) {
sessionController = value;
}
}
}
public AVUserController UserController {
get {
lock (mutex) {

View File

@ -8,12 +8,40 @@ using System.Collections.Generic;
using System.Linq;
namespace LeanCloud.Storage.Internal {
public abstract class AVFileController {
public abstract Task<FileState> SaveAsync(FileState state,
public interface IFileUploader {
Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress,
CancellationToken cancellationToken);
}
public class AVFileController {
const string QCloud = "qcloud";
const string AWS = "s3";
public Task<FileState> SaveAsync(FileState state,
Stream dataStream,
String sessionToken,
IProgress<AVUploadProgressEventArgs> progress,
CancellationToken cancellationToken = default(CancellationToken));
CancellationToken cancellationToken = default(CancellationToken)) {
if (state.Url != null) {
// !isDirty
return Task<FileState>.FromResult(state);
}
return GetFileToken(state, cancellationToken).OnSuccess(t => {
// 根据 provider 区分 cdn
var ret = t.Result;
var fileToken = ret.Item2;
var provider = fileToken["provider"] as string;
switch (provider) {
case QCloud:
return new QCloudUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
case AWS:
return new AWSUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
default:
return new QiniuUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
}
}).Unwrap();
}
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand {
@ -24,7 +52,6 @@ namespace LeanCloud.Storage.Internal {
}
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken) {
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
string currentSessionToken = AVUser.CurrentSessionToken;
string str = fileState.Name;
IDictionary<string, object> parameters = new Dictionary<string, object>();

View File

@ -6,21 +6,14 @@ using System.Collections.Generic;
using System.Net.Http;
namespace LeanCloud.Storage.Internal {
internal class AWSS3FileController : AVFileController {
public override Task<FileState> SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken = default(System.Threading.CancellationToken)) {
if (state.Url != null) {
return Task.FromResult(state);
}
return GetFileToken(state, cancellationToken).OnSuccess(t => {
var fileToken = t.Result.Item2;
var uploadUrl = fileToken["upload_url"].ToString();
state.ObjectId = fileToken["objectId"].ToString();
string url = fileToken["url"] as string;
state.Url = new Uri(url, UriKind.Absolute);
return PutFile(state, uploadUrl, dataStream);
}).Unwrap().OnSuccess(s => {
internal class AWSUploader : IFileUploader {
public Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress,
CancellationToken cancellationToken) {
var uploadUrl = fileToken["upload_url"].ToString();
state.ObjectId = fileToken["objectId"].ToString();
string url = fileToken["url"] as string;
state.Url = new Uri(url, UriKind.Absolute);
return PutFile(state, uploadUrl, dataStream).OnSuccess(s => {
return s.Result;
});
}

View File

@ -6,10 +6,9 @@ using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal {
internal class QCloudCosFileController : AVFileController {
internal class QCloudUploader : IFileUploader {
private object mutex = new object();
FileState fileState;
@ -20,33 +19,22 @@ namespace LeanCloud.Storage.Internal {
bool done;
private long sliceSize = (long)CommonSize.KB512;
public override Task<FileState> SaveAsync(FileState state,
Stream dataStream,
string sessionToken,
IProgress<AVUploadProgressEventArgs> progress,
CancellationToken cancellationToken) {
if (state.Url != null) {
return Task<FileState>.FromResult(state);
}
public Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken) {
fileState = state;
data = dataStream;
return GetFileToken(fileState, cancellationToken).OnSuccess(_ => {
var fileToken = _.Result.Item2;
uploadUrl = fileToken["upload_url"].ToString();
token = fileToken["token"].ToString();
fileState.ObjectId = fileToken["objectId"].ToString();
bucket = fileToken["bucket"].ToString();
return FileSlice(cancellationToken).OnSuccess(t => {
if (done) return Task<FileState>.FromResult(state);
var response = t.Result.Item2;
var resumeData = response["data"] as IDictionary<string, object>;
if (resumeData.ContainsKey("access_url")) return Task<FileState>.FromResult(state);
var sliceSession = resumeData["session"].ToString();
var sliceOffset = long.Parse(resumeData["offset"].ToString());
return UploadSlice(sliceSession, sliceOffset, dataStream, progress, cancellationToken);
}).Unwrap();
uploadUrl = fileToken["upload_url"].ToString();
token = fileToken["token"].ToString();
fileState.ObjectId = fileToken["objectId"].ToString();
bucket = fileToken["bucket"].ToString();
return FileSlice(cancellationToken).OnSuccess(t => {
if (done) return Task<FileState>.FromResult(state);
var response = t.Result.Item2;
var resumeData = response["data"] as IDictionary<string, object>;
if (resumeData.ContainsKey("access_url")) return Task<FileState>.FromResult(state);
var sliceSession = resumeData["session"].ToString();
var sliceOffset = long.Parse(resumeData["offset"].ToString());
return UploadSlice(sliceSession, sliceOffset, dataStream, progress, cancellationToken);
}).Unwrap();
}

View File

@ -9,104 +9,75 @@ using System.Threading.Tasks;
using System.Net.Http;
using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal
{
internal enum CommonSize : long
{
namespace LeanCloud.Storage.Internal {
internal enum CommonSize : long {
MB4 = 1024 * 1024 * 4,
MB1 = 1024 * 1024,
KB512 = 1024 * 1024 / 2,
KB256 = 1024 * 1024 / 4
}
internal class QiniuFileController : AVFileController
{
internal class QiniuUploader : IFileUploader {
private static int BLOCKSIZE = 1024 * 1024 * 4;
private const int blockMashk = (1 << blockBits) - 1;
private const int blockBits = 22;
private int CalcBlockCount(long fsize)
{
private int CalcBlockCount(long fsize) {
return (int)((fsize + blockMashk) >> blockBits);
}
internal static string UP_HOST = "https://up.qbox.me";
private object mutex = new object();
public override Task<FileState> SaveAsync(FileState state,
Stream dataStream,
String sessionToken,
IProgress<AVUploadProgressEventArgs> progress,
CancellationToken cancellationToken)
{
if (state.Url != null)
{
return Task<FileState>.FromResult(state);
}
public Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken) {
state.frozenData = dataStream;
state.CloudName = GetUniqueName(state);
return GetQiniuToken(state, CancellationToken.None).ContinueWith(t =>
{
MergeFromJSON(state, t.Result.Item2);
return UploadNextChunk(state, dataStream, string.Empty, 0, progress);
}).Unwrap().OnSuccess<FileState>(s =>
{
MergeFromJSON(state, fileToken);
return UploadNextChunk(state, dataStream, string.Empty, 0, progress).OnSuccess(_ => {
return state;
});
}
Task UploadNextChunk(FileState state, Stream dataStream, string context, long offset, IProgress<AVUploadProgressEventArgs> progress)
{
Task UploadNextChunk(FileState state, Stream dataStream, string context, long offset, IProgress<AVUploadProgressEventArgs> progress) {
var totalSize = dataStream.Length;
var remainingSize = totalSize - state.completed;
if (progress != null)
{
lock (mutex)
{
progress.Report(new AVUploadProgressEventArgs()
{
if (progress != null) {
lock (mutex) {
progress.Report(new AVUploadProgressEventArgs() {
Progress = AVFileController.CalcProgress(state.completed, totalSize)
});
}
}
if (state.completed == totalSize)
{
if (state.completed == totalSize) {
return QiniuMakeFile(state, state.frozenData, state.token, state.CloudName, totalSize, state.block_ctxes.ToArray(), CancellationToken.None);
}
else if (state.completed % BLOCKSIZE == 0)
{
} else if (state.completed % BLOCKSIZE == 0) {
var firstChunkBinary = GetChunkBinary(state.completed, dataStream);
var blockSize = remainingSize > BLOCKSIZE ? BLOCKSIZE : remainingSize;
return MakeBlock(state, firstChunkBinary, blockSize).ContinueWith(t =>
{
return MakeBlock(state, firstChunkBinary, blockSize).ContinueWith(t => {
var dict = JsonConvert.DeserializeObject<IDictionary<string, object>>(t.Result.Item2, new LeanCloudJsonConverter());
var ctx = dict["ctx"].ToString();
offset = long.Parse(dict["offset"].ToString());
var host = dict["host"].ToString();
state.completed += firstChunkBinary.Length;
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize)
{
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize) {
state.block_ctxes.Add(ctx);
}
return UploadNextChunk(state, dataStream, ctx, offset, progress);
}).Unwrap();
}
else
{
} else {
var chunkBinary = GetChunkBinary(state.completed, dataStream);
return PutChunk(state, chunkBinary, context, offset).ContinueWith(t =>
{
return PutChunk(state, chunkBinary, context, offset).ContinueWith(t => {
var dict = JsonConvert.DeserializeObject<IDictionary<string, object>>(t.Result.Item2, new LeanCloudJsonConverter());
var ctx = dict["ctx"].ToString();
offset = long.Parse(dict["offset"].ToString());
var host = dict["host"].ToString();
state.completed += chunkBinary.Length;
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize)
{
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize) {
state.block_ctxes.Add(ctx);
}
//if (AVClient.fileUploaderDebugLog)
@ -119,11 +90,9 @@ namespace LeanCloud.Storage.Internal
}
}
byte[] GetChunkBinary(long completed, Stream dataStream)
{
byte[] GetChunkBinary(long completed, Stream dataStream) {
long chunkSize = (long)CommonSize.MB1;
if (completed + chunkSize > dataStream.Length)
{
if (completed + chunkSize > dataStream.Length) {
chunkSize = dataStream.Length - completed;
}
byte[] chunkBinary = new byte[chunkSize];
@ -132,16 +101,13 @@ namespace LeanCloud.Storage.Internal
return chunkBinary;
}
internal string GetUniqueName(FileState state)
{
internal string GetUniqueName(FileState state) {
string key = Guid.NewGuid().ToString();//file Key in Qiniu.
string extension = Path.GetExtension(state.Name);
key += extension;
return key;
}
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetQiniuToken(FileState state, CancellationToken cancellationToken)
{
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetQiniuToken(FileState state, CancellationToken cancellationToken) {
string currentSessionToken = AVUser.CurrentSessionToken;
string str = state.Name;
@ -162,8 +128,7 @@ namespace LeanCloud.Storage.Internal
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
}
IList<KeyValuePair<string, string>> GetQiniuRequestHeaders(FileState state)
{
IList<KeyValuePair<string, string>> GetQiniuRequestHeaders(FileState state) {
IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>();
string authHead = "UpToken " + state.token;
@ -212,19 +177,16 @@ namespace LeanCloud.Storage.Internal
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)
{
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();
urlBuilder.AppendFormat("{0}/mkfile/{1}", UP_HOST, fsize);
if (key != null)
{
if (key != null) {
urlBuilder.AppendFormat("/key/{0}", ToBase64URLSafe(key));
}
var metaData = GetMetaData(state, dataStream);
StringBuilder sb = new StringBuilder();
foreach (string _key in metaData.Keys)
{
foreach (string _key in metaData.Keys) {
sb.AppendFormat("/{0}/{1}", _key, ToBase64URLSafe(metaData[_key].ToString()));
}
urlBuilder.Append(sb.ToString());
@ -238,12 +200,10 @@ namespace LeanCloud.Storage.Internal
int proCount = ctxes.Length;
Stream body = new MemoryStream();
for (int i = 0; i < proCount; i++)
{
for (int i = 0; i < proCount; i++) {
byte[] bctx = StringToAscii(ctxes[i]);
body.Write(bctx, 0, bctx.Length);
if (i != proCount - 1)
{
if (i != proCount - 1) {
body.WriteByte((byte)',');
}
}
@ -263,10 +223,8 @@ namespace LeanCloud.Storage.Internal
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
}
internal void MergeFromJSON(FileState state, IDictionary<string, object> jsonData)
{
lock (this.mutex)
{
internal void MergeFromJSON(FileState state, IDictionary<string, object> jsonData) {
lock (this.mutex) {
string url = jsonData["url"] as string;
state.Url = new Uri(url, UriKind.Absolute);
state.bucketId = FetchBucketId(url);
@ -276,17 +234,14 @@ namespace LeanCloud.Storage.Internal
}
}
string FetchBucketId(string url)
{
string FetchBucketId(string url) {
var elements = url.Split('/');
return elements[elements.Length - 1];
}
public static byte[] StringToAscii(string s)
{
public static byte[] StringToAscii(string s) {
byte[] retval = new byte[s.Length];
for (int ix = 0; ix < s.Length; ++ix)
{
for (int ix = 0; ix < s.Length; ++ix) {
char ch = s[ix];
if (ch <= 0x7f)
retval[ix] = (byte)ch;
@ -295,20 +250,17 @@ namespace LeanCloud.Storage.Internal
}
return retval;
}
public static string ToBase64URLSafe(string str)
{
public static string ToBase64URLSafe(string str) {
return Encode(str);
}
public static string Encode(byte[] bs)
{
public static string Encode(byte[] bs) {
if (bs == null || bs.Length == 0)
return "";
string encodedStr = Convert.ToBase64String(bs);
encodedStr = encodedStr.Replace('+', '-').Replace('/', '_');
return encodedStr;
}
public static string Encode(string text)
{
public static string Encode(string text) {
if (String.IsNullOrEmpty(text))
return "";
byte[] bs = Encoding.UTF8.GetBytes(text);
@ -317,28 +269,23 @@ namespace LeanCloud.Storage.Internal
return encodedStr;
}
internal static string GetMD5Code(Stream data)
{
internal static string GetMD5Code(Stream data) {
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(data);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
for (int i = 0; i < retVal.Length; i++) {
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
internal IDictionary<string, object> GetMetaData(FileState state, Stream data)
{
internal IDictionary<string, object> GetMetaData(FileState state, Stream data) {
IDictionary<string, object> rtn = new Dictionary<string, object>();
if (state.MetaData != null)
{
foreach (var meta in state.MetaData)
{
if (state.MetaData != null) {
foreach (var meta in state.MetaData) {
rtn.Add(meta.Key, meta.Value);
}
}
@ -351,14 +298,10 @@ namespace LeanCloud.Storage.Internal
return rtn;
}
internal void MergeDic(IDictionary<string, object> dic, string key, object value)
{
if (dic.ContainsKey(key))
{
internal void MergeDic(IDictionary<string, object> dic, string key, object value) {
if (dic.ContainsKey(key)) {
dic[key] = value;
}
else
{
} else {
dic.Add(key, value);
}
}

View File

@ -97,9 +97,7 @@ namespace LeanCloud.Storage.Internal {
registerActions.TryGetValue(className, out toPerform);
mutex.ExitReadLock();
if (toPerform != null) {
toPerform();
}
toPerform?.Invoke();
}
public void UnregisterSubclass(Type type) {

View File

@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
namespace LeanCloud.Storage.Internal {
public class AVSessionController {
public Task<IObjectState> GetSessionAsync(string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand {
Path = "sessions/me",
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
});
}
public Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand {
Path = "logout",
Method = HttpMethod.Post
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
}
public Task<IObjectState> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand {
Path = "upgradeToRevocableSession",
Method = HttpMethod.Post,
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
});
}
public bool IsRevocableSessionToken(string sessionToken) {
return sessionToken.Contains("r:");
}
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LeanCloud.Storage.Internal {
/// <summary>
/// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc.
///
/// These cannot be 'internal' anymore if we are fully modularizing things out, because
/// they are no longer a part of the same library, especially as we create things like
/// Installation inside push library.
///
/// So this class contains a bunch of extension methods that can live inside another
/// namespace, which 'wrap' the intenral APIs that already exist.
/// </summary>
public static class AVSessionExtensions {
public static Task<string> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) {
return AVSession.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken);
}
public static Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) {
return AVSession.RevokeAsync(sessionToken, cancellationToken);
}
}
}

View File

@ -251,7 +251,6 @@ namespace LeanCloud {
AVObject.RegisterSubclass<AVUser>();
AVObject.RegisterSubclass<AVRole>();
AVObject.RegisterSubclass<AVSession>();
}
internal static void Config(Configuration configuration) {

View File

@ -350,129 +350,4 @@ namespace LeanCloud {
}
}
/// <summary>
/// AVRPCC loud function base.
/// </summary>
public class AVRPCCloudFunctionBase<P, R> {
/// <summary>
/// AVRPCD eserialize.
/// </summary>
public delegate R AVRPCDeserialize<R>(IDictionary<string, object> result);
/// <summary>
/// AVRPCS erialize.
/// </summary>
public delegate IDictionary<string, object> AVRPCSerialize<P>(P parameters);
public AVRPCCloudFunctionBase()
: this(true) {
}
public AVRPCCloudFunctionBase(bool noneParameters) {
if (noneParameters) {
this.Encode = n => {
return null;
};
}
}
private AVRPCDeserialize<R> _decode;
public AVRPCDeserialize<R> Decode {
get {
return _decode;
}
set {
_decode = value;
}
}
private AVRPCSerialize<P> _encode;
public AVRPCSerialize<P> Encode {
get {
if (_encode == null) {
_encode = n => {
if (n != null) {
return JsonConvert.DeserializeObject<Dictionary<string, object>>(n.ToString(), new LeanCloudJsonConverter());
}
return null;
};
}
return _encode;
}
set {
_encode = value;
}
}
public string FunctionName { get; set; }
public Task<R> ExecuteAsync(P parameters) {
var encodedParameters = Encode(parameters);
var command = new EngineCommand {
Path = $"call/{Uri.EscapeUriString(FunctionName)}",
Method = HttpMethod.Post,
Content = encodedParameters
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command).OnSuccess(s => {
var responseBody = s.Result.Item2;
if (!responseBody.ContainsKey("result")) {
return default(R);
}
return Decode(responseBody);
});
}
}
public class AVObjectRPCCloudFunction : AVObjectRPCCloudFunction<AVObject> {
}
public class AVObjectListRPCCloudFunction : AVObjectListRPCCloudFunction<AVObject> {
}
public class AVObjectListRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, IList<R>> where R : AVObject {
public AVObjectListRPCCloudFunction()
: base(true) {
this.Decode = this.AVObjectListDeserializer();
}
public AVRPCDeserialize<IList<R>> AVObjectListDeserializer() {
AVRPCDeserialize<IList<R>> del = data => {
var items = data["result"] as IList<object>;
return items.Select(item => {
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
return AVObject.FromState<AVObject>(state, state.ClassName);
}).ToList() as IList<R>;
};
return del;
}
}
public class AVObjectRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, R> where R : AVObject {
public AVObjectRPCCloudFunction()
: base(true) {
this.Decode = this.AVObjectDeserializer();
}
public AVRPCDeserialize<R> AVObjectDeserializer() {
AVRPCDeserialize<R> del = data => {
var item = data["result"] as object;
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
return AVObject.FromState<R>(state, state.ClassName);
};
return del;
}
}
}

View File

@ -1,106 +0,0 @@
using LeanCloud.Storage.Internal;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LeanCloud
{
/// <summary>
/// Represents a session of a user for a LeanCloud application.
/// </summary>
[AVClassName("_Session")]
public class AVSession : AVObject
{
private static readonly HashSet<string> readOnlyKeys = new HashSet<string> {
"sessionToken", "createdWith", "restricted", "user", "expiresAt", "installationId"
};
protected override bool IsKeyMutable(string key)
{
return !readOnlyKeys.Contains(key);
}
/// <summary>
/// Gets the session token for a user, if they are logged in.
/// </summary>
[AVFieldName("sessionToken")]
public string SessionToken
{
get { return GetProperty<string>(null, "SessionToken"); }
}
/// <summary>
/// Constructs a <see cref="AVQuery{AVSession}"/> for AVSession.
/// </summary>
public static AVQuery<AVSession> Query
{
get
{
return new AVQuery<AVSession>();
}
}
internal static AVSessionController SessionController
{
get
{
return AVPlugins.Instance.SessionController;
}
}
/// <summary>
/// Gets the current <see cref="AVSession"/> object related to the current user.
/// </summary>
public static Task<AVSession> GetCurrentSessionAsync()
{
return GetCurrentSessionAsync(CancellationToken.None);
}
/// <summary>
/// Gets the current <see cref="AVSession"/> object related to the current user.
/// </summary>
/// <param name="cancellationToken">The cancellation token</param>
public static Task<AVSession> GetCurrentSessionAsync(CancellationToken cancellationToken)
{
AVUser user = AVUser.CurrentUser;
if (user == null) {
return Task<AVSession>.FromResult((AVSession)null);
}
string sessionToken = user.SessionToken;
if (sessionToken == null) {
return Task<AVSession>.FromResult((AVSession)null);
}
return SessionController.GetSessionAsync(sessionToken, cancellationToken).OnSuccess(t =>
{
AVSession session = AVObject.FromState<AVSession>(t.Result, "_Session");
return session;
});
}
internal static Task RevokeAsync(string sessionToken, CancellationToken cancellationToken)
{
if (sessionToken == null || !SessionController.IsRevocableSessionToken(sessionToken))
{
return Task.FromResult(0);
}
return SessionController.RevokeAsync(sessionToken, cancellationToken);
}
internal static Task<string> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken)
{
if (sessionToken == null || SessionController.IsRevocableSessionToken(sessionToken))
{
return Task<string>.FromResult(sessionToken);
}
return SessionController.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken).OnSuccess(t =>
{
AVSession session = AVObject.FromState<AVSession>(t.Result, "_Session");
return session.SessionToken;
});
}
}
}

View File

@ -343,47 +343,12 @@ namespace LeanCloud {
/// Typically, you should use <see cref="LogOutAsync()"/>, unless you are managing your own threading.
/// </remarks>
public static void LogOut() {
CurrentUser = null;
// TODO (hallucinogen): this will without a doubt fail in Unity. But what else can we do?
//LogOutAsync().Wait();
}
/// <summary>
/// Logs out the currently logged in user session. This will remove the session from disk, log out of
/// linked services, and future calls to <see cref="CurrentUser"/> will return <c>null</c>.
/// </summary>
/// <remarks>
/// This is preferable to using <see cref="LogOut()"/>, unless your code is already running from a
/// background thread.
/// </remarks>
public static void LogOutAsync() {
//return LogOutAsync(CancellationToken.None);
}
/// <summary>
/// Logs out the currently logged in user session. This will remove the session from disk, log out of
/// linked services, and future calls to <see cref="CurrentUser"/> will return <c>null</c>.
///
/// This is preferable to using <see cref="LogOut()"/>, unless your code is already running from a
/// background thread.
/// </summary>
public static void LogOutAsync(CancellationToken cancellationToken) {
CurrentUser = null;
}
//internal Task LogOutAsync(Task toAwait, CancellationToken cancellationToken) {
// string oldSessionToken = SessionToken;
// if (oldSessionToken == null) {
// return Task.FromResult(0);
// }
// // Cleanup in-memory session.
// MutateState(mutableClone => {
// mutableClone.ServerData.Remove("sessionToken");
// });
// var revokeSessionTask = AVSession.RevokeAsync(oldSessionToken, cancellationToken);
// return Task.WhenAll(revokeSessionTask, CurrentUserController.LogOutAsync(cancellationToken));
//}
private static void LogOutWithProviders() {
foreach (var provider in authProviders.Values) {
provider.Deauthenticate();
@ -391,8 +356,8 @@ namespace LeanCloud {
}
public static AVUser CurrentUser {
// TODO 线程问题
get; internal set;
get;
internal set;
}
/// <summary>
@ -404,27 +369,6 @@ namespace LeanCloud {
}
}
#region Legacy / Revocable Session Tokens
private static readonly object isRevocableSessionEnabledMutex = new object();
private static bool isRevocableSessionEnabled;
internal static void DisableRevocableSession() {
lock (isRevocableSessionEnabledMutex) {
isRevocableSessionEnabled = false;
}
}
internal static bool IsRevocableSessionEnabled {
get {
lock (isRevocableSessionEnabledMutex) {
return isRevocableSessionEnabled;
}
}
}
#endregion
/// <summary>
/// Requests a password reset email to be sent to the specified email address associated with the
/// user account. This email allows the user to securely reset their password on the LeanCloud site.
@ -803,7 +747,7 @@ namespace LeanCloud {
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber) {
return AVUser.RequestPasswordResetBySmsCode(mobilePhoneNumber, null, CancellationToken.None);
return RequestPasswordResetBySmsCode(mobilePhoneNumber, null, CancellationToken.None);
}
/// <summary>
@ -823,7 +767,7 @@ namespace LeanCloud {
/// <param name="validateToken">Validate token.</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken) {
return AVUser.RequestPasswordResetBySmsCode(mobilePhoneNumber, validateToken, CancellationToken.None);
return RequestPasswordResetBySmsCode(mobilePhoneNumber, validateToken, CancellationToken.None);
}
/// <summary>
@ -834,9 +778,7 @@ namespace LeanCloud {
/// <param name="cancellationToken">cancellationToken</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
string currentSessionToken = AVUser.CurrentSessionToken;
Dictionary<string, object> strs = new Dictionary<string, object>()
{
Dictionary<string, object> strs = new Dictionary<string, object> {
{ "mobilePhoneNumber", mobilePhoneNumber },
};
if (String.IsNullOrEmpty(validateToken)) {
@ -868,9 +810,7 @@ namespace LeanCloud {
/// <param name="cancellationToken">cancellationToken</param>
/// <returns></returns>
public static Task ResetPasswordBySmsCodeAsync(string newPassword, string smsCode, CancellationToken cancellationToken) {
string currentSessionToken = AVUser.CurrentSessionToken;
Dictionary<string, object> strs = new Dictionary<string, object>()
{
Dictionary<string, object> strs = new Dictionary<string, object> {
{ "password", newPassword }
};
var command = new AVCommand {
@ -918,7 +858,6 @@ namespace LeanCloud {
/// <param name="cancellationToken">CancellationToken</param>
/// <returns></returns>
public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
string currentSessionToken = AVUser.CurrentSessionToken;
Dictionary<string, object> strs = new Dictionary<string, object> {
{ "mobilePhoneNumber", mobilePhoneNumber }
};