csharp-sdk-upm/Storage/Source/Public/AVClient.cs

507 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using LeanCloud.Storage.Internal;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace LeanCloud
{
/// <summary>
/// AVClient contains static functions that handle global
/// configuration for the LeanCloud library.
/// </summary>
public static partial class AVClient
{
public static readonly string[] DateFormatStrings = {
// Official ISO format
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'",
// It's possible that the string converter server-side may trim trailing zeroes,
// so these two formats cover ourselves from that.
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ff'Z'",
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'f'Z'",
};
/// <summary>
/// Represents the configuration of the LeanCloud SDK.
/// </summary>
public struct Configuration
{
/// <summary>
/// 与 SDK 通讯的云端节点
/// </summary>
public enum AVRegion
{
/// <summary>
/// 默认值LeanCloud 华北节点,同 Public_North_China
/// </summary>
[Obsolete("please use Configuration.AVRegion.Public_North_China")]
Public_CN = 0,
/// <summary>
/// 默认值,华北公有云节点,同 Public_CN
/// </summary>
Public_North_China = 0,
/// <summary>
/// LeanCloud 北美区公有云节点,同 Public_North_America
/// </summary>
[Obsolete("please use Configuration.AVRegion.Public_North_America")]
Public_US = 1,
/// <summary>
/// LeanCloud 北美区公有云节点,同 Public_US
/// </summary>
Public_North_America = 1,
/// <summary>
/// 华东公有云节点,同 Public_East_China
/// </summary>
[Obsolete("please use Configuration.AVRegion.Public_East_China")]
Vendor_Tencent = 2,
/// <summary>
/// 华东公有云节点,同 Vendor_Tencent
/// </summary>
Public_East_China = 2,
}
/// <summary>
/// In the event that you would like to use the LeanCloud SDK
/// from a completely portable project, with no platform-specific library required,
/// to get full access to all of our features available on LeanCloud.com
/// (A/B testing, slow queries, etc.), you must set the values of this struct
/// to be appropriate for your platform.
///
/// Any values set here will overwrite those that are automatically configured by
/// any platform-specific migration library your app includes.
/// </summary>
public struct VersionInformation
{
/// <summary>
/// The build number of your app.
/// </summary>
public String BuildVersion { get; set; }
/// <summary>
/// The human friendly version number of your happ.
/// </summary>
public String DisplayVersion { get; set; }
/// <summary>
/// The operating system version of the platform the SDK is operating in..
/// </summary>
public String OSVersion { get; set; }
}
/// <summary>
/// The LeanCloud application ID of your app.
/// </summary>
public string ApplicationId { get; set; }
/// <summary>
/// LeanCloud C# SDK 支持的服务节点,目前支持华北,华东和北美公有云节点和私有节点,以及专属节点
/// </summary>
public AVRegion Region { get; set; }
internal int RegionValue
{
get
{
return (int)Region;
}
}
/// <summary>
/// The LeanCloud application key for your app.
/// </summary>
public string ApplicationKey { get; set; }
/// <summary>
/// The LeanCloud master key for your app.
/// </summary>
/// <value>The master key.</value>
public string MasterKey { get; set; }
/// <summary>
/// Gets or sets additional HTTP headers to be sent with network requests from the SDK.
/// </summary>
public IDictionary<string, string> AdditionalHTTPHeaders { get; set; }
/// <summary>
/// The version information of your application environment.
/// </summary>
public VersionInformation VersionInfo { get; set; }
/// <summary>
/// 存储服务器地址
/// </summary>
/// <value>The API server.</value>
public string ApiServer { get; set; }
/// <summary>
/// 云引擎服务器地址
/// </summary>
/// <value>The engine server uri.</value>
public string EngineServer { get; set; }
/// <summary>
/// 即时通信服务器地址
/// </summary>
/// <value>The RTMR outer.</value>
public string RTMServer { get; set; }
/// <summary>
/// 直连即时通信服务器地址
/// </summary>
/// <value>The realtime server.</value>
public string RealtimeServer { get; set; }
public Uri PushServer { get; set; }
public Uri StatsServer { get; set; }
}
private static readonly object mutex = new object();
static AVClient()
{
versionString = "net-portable-" + Version;
//AVModuleController.Instance.ScanForModules();
}
/// <summary>
/// The current configuration that LeanCloud has been initialized with.
/// </summary>
public static Configuration CurrentConfiguration { get; internal set; }
internal static Version Version
{
get
{
var assemblyName = new AssemblyName(typeof(AVClient).GetTypeInfo().Assembly.FullName);
return assemblyName.Version;
}
}
private static readonly string versionString;
/// <summary>
/// 当前 SDK 版本号
/// </summary>
public static string VersionString
{
get
{
return versionString;
}
}
/// <summary>
/// Authenticates this client as belonging to your application. This must be
/// called before your application can use the LeanCloud library. The recommended
/// way is to put a call to <c>AVClient.Initialize</c> in your
/// Application startup.
/// </summary>
/// <param name="applicationId">The Application ID provided in the LeanCloud dashboard.
/// </param>
/// <param name="applicationKey">The .NET API Key provided in the LeanCloud dashboard.
/// </param>
public static void Initialize(string applicationId, string applicationKey)
{
Initialize(new Configuration
{
ApplicationId = applicationId,
ApplicationKey = applicationKey
});
}
internal static Action<string> LogTracker { get; private set; }
/// <summary>
/// 启动日志打印
/// </summary>
/// <param name="trace"></param>
public static void HttpLog(Action<string> trace)
{
LogTracker = trace;
}
/// <summary>
/// 打印 HTTP 访问日志
/// </summary>
/// <param name="log"></param>
public static void PrintLog(string log)
{
if (AVClient.LogTracker != null)
{
AVClient.LogTracker(log);
}
}
static bool useProduction = true;
/// <summary>
/// Gets or sets a value indicating whether send the request to production server or staging server.
/// </summary>
/// <value><c>true</c> if use production; otherwise, <c>false</c>.</value>
public static bool UseProduction
{
get
{
return useProduction;
}
set
{
useProduction = value;
}
}
static bool useMasterKey = false;
public static bool UseMasterKey
{
get
{
return useMasterKey;
}
set
{
useMasterKey = value;
}
}
/// <summary>
/// Authenticates this client as belonging to your application. This must be
/// called before your application can use the LeanCloud library. The recommended
/// way is to put a call to <c>AVClient.Initialize</c> in your
/// Application startup.
/// </summary>
/// <param name="configuration">The configuration to initialize LeanCloud with.
/// </param>
public static void Initialize(Configuration configuration)
{
Config(configuration);
AVObject.RegisterSubclass<AVUser>();
AVObject.RegisterSubclass<AVRole>();
AVObject.RegisterSubclass<AVSession>();
}
internal static void Config(Configuration configuration)
{
lock (mutex)
{
var nodeHash = configuration.ApplicationId.Split('-');
if (nodeHash.Length > 1)
{
if (nodeHash[1].Trim() == "9Nh9j0Va")
{
configuration.Region = Configuration.AVRegion.Public_East_China;
}
}
CurrentConfiguration = configuration;
}
}
internal static void Clear()
{
AVPlugins.Instance.AppRouterController.Clear();
AVPlugins.Instance.Reset();
AVUser.ClearInMemoryUser();
}
/// <summary>
/// Switch app.
/// </summary>
/// <param name="configuration">Configuration.</param>
public static void Switch(Configuration configuration)
{
Clear();
Initialize(configuration);
}
public static void Switch(string applicationId, string applicationKey, Configuration.AVRegion region = Configuration.AVRegion.Public_North_China)
{
var configuration = new Configuration
{
ApplicationId = applicationId,
ApplicationKey = applicationKey,
Region = region
};
Switch(configuration);
}
public static string BuildQueryString(IDictionary<string, object> parameters)
{
return string.Join("&", (from pair in parameters
let valueString = pair.Value as string
select string.Format("{0}={1}",
Uri.EscapeDataString(pair.Key),
Uri.EscapeDataString(string.IsNullOrEmpty(valueString) ?
Json.Encode(pair.Value) : valueString)))
.ToArray());
}
internal static IDictionary<string, string> DecodeQueryString(string queryString)
{
var dict = new Dictionary<string, string>();
foreach (var pair in queryString.Split('&'))
{
var parts = pair.Split(new char[] { '=' }, 2);
dict[parts[0]] = parts.Length == 2 ? Uri.UnescapeDataString(parts[1].Replace("+", " ")) : null;
}
return dict;
}
internal static IDictionary<string, object> DeserializeJsonString(string jsonData)
{
return Json.Parse(jsonData) as IDictionary<string, object>;
}
internal static string SerializeJsonString(IDictionary<string, object> jsonData)
{
return Json.Encode(jsonData);
}
public static Task<Tuple<HttpStatusCode, string>> HttpGetAsync(Uri uri)
{
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)
{
var dataStream = body != null ? new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(body))) : null;
return AVClient.RequestAsync(uri, method, headers, dataStream, contentType, 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)
{
HttpRequest request = new HttpRequest()
{
Data = data != null ? data : null,
Headers = headers,
Method = method,
Uri = uri
};
return AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, cancellationToken).OnSuccess(t =>
{
var response = t.Result;
var contentString = response.Item2;
int responseCode = (int)response.Item1;
var responseLog = responseCode + ";" + contentString;
PrintLog(responseLog);
return response;
});
}
internal static Tuple<HttpStatusCode, IDictionary<string, object>> ReponseResolve(Tuple<HttpStatusCode, string> response, CancellationToken cancellationToken)
{
Tuple<HttpStatusCode, string> result = response;
HttpStatusCode code = result.Item1;
string item2 = result.Item2;
if (item2 == null)
{
cancellationToken.ThrowIfCancellationRequested();
return new Tuple<HttpStatusCode, IDictionary<string, object>>(code, null);
}
IDictionary<string, object> strs = null;
try
{
strs = (!item2.StartsWith("[", StringComparison.Ordinal) ? AVClient.DeserializeJsonString(item2) : new Dictionary<string, object>()
{
{ "results", Json.Parse(item2) }
});
}
catch (Exception exception)
{
throw new AVException(AVException.ErrorCode.OtherCause, "Invalid response from server", exception);
}
var codeValue = (int)code;
if (codeValue > 203 || codeValue < 200)
{
throw new AVException((AVException.ErrorCode)((int)((strs.ContainsKey("code") ? (long)strs["code"] : (long)-1))), (strs.ContainsKey("error") ? strs["error"] as string : item2), null);
}
cancellationToken.ThrowIfCancellationRequested();
return new Tuple<HttpStatusCode, IDictionary<string, object>>(code, strs);
}
internal static Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RequestAsync(string method, Uri relativeUri, string sessionToken, IDictionary<string, object> data, CancellationToken cancellationToken)
{
var command = new AVCommand(relativeUri.ToString(),
method: method,
sessionToken: sessionToken,
data: data);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken);
}
internal static Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(AVCommand command)
{
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command);
}
internal static bool IsSuccessStatusCode(HttpStatusCode responseStatus)
{
var codeValue = (int)responseStatus;
return (codeValue > 199) && (codeValue < 204);
}
public static string ToLog(this HttpRequest request)
{
StringBuilder sb = new StringBuilder();
var start = "===HTTP Request Start===";
sb.AppendLine(start);
var urlLog = "Url: " + request.Uri;
sb.AppendLine(urlLog);
var methodLog = "Method: " + request.Method;
sb.AppendLine(methodLog);
try
{
var headers = request.Headers.ToDictionary(x => x.Key, x => x.Value as object);
var headersLog = "Headers: " + Json.Encode(headers);
sb.AppendLine(headersLog);
}
catch (Exception)
{
}
try
{
if (request is AVCommand)
{
var command = (AVCommand)request;
if (command.DataObject != null)
{
var bodyLog = "Body:" + Json.Encode(command.DataObject);
sb.AppendLine(bodyLog);
}
}
else
{
StreamReader reader = new StreamReader(request.Data);
string bodyLog = reader.ReadToEnd();
sb.AppendLine(bodyLog);
}
}
catch (Exception)
{
}
var end = "===HTTP Request End===";
sb.AppendLine(end);
return sb.ToString();
}
}
}