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 { /// /// AVClient contains static functions that handle global /// configuration for the LeanCloud library. /// 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'", }; /// /// Represents the configuration of the LeanCloud SDK. /// public struct Configuration { /// /// 与 SDK 通讯的云端节点 /// public enum AVRegion { /// /// 默认值,LeanCloud 华北节点,同 Public_North_China /// [Obsolete("please use Configuration.AVRegion.Public_North_China")] Public_CN = 0, /// /// 默认值,华北公有云节点,同 Public_CN /// Public_North_China = 0, /// /// LeanCloud 北美区公有云节点,同 Public_North_America /// [Obsolete("please use Configuration.AVRegion.Public_North_America")] Public_US = 1, /// /// LeanCloud 北美区公有云节点,同 Public_US /// Public_North_America = 1, /// /// 华东公有云节点,同 Public_East_China /// [Obsolete("please use Configuration.AVRegion.Public_East_China")] Vendor_Tencent = 2, /// /// 华东公有云节点,同 Vendor_Tencent /// Public_East_China = 2, } /// /// 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. /// public struct VersionInformation { /// /// The build number of your app. /// public string BuildVersion { get; set; } /// /// The human friendly version number of your happ. /// public string DisplayVersion { get; set; } /// /// The operating system version of the platform the SDK is operating in.. /// public string OSVersion { get; set; } } /// /// The LeanCloud application ID of your app. /// public string ApplicationId { get; set; } /// /// LeanCloud C# SDK 支持的服务节点,目前支持华北,华东和北美公有云节点和私有节点,以及专属节点 /// public AVRegion Region { get; set; } internal int RegionValue { get { return (int)Region; } } /// /// The LeanCloud application key for your app. /// public string ApplicationKey { get; set; } /// /// The LeanCloud master key for your app. /// /// The master key. public string MasterKey { get; set; } /// /// Gets or sets additional HTTP headers to be sent with network requests from the SDK. /// public IDictionary AdditionalHTTPHeaders { get; set; } /// /// The version information of your application environment. /// public VersionInformation VersionInfo { get; set; } /// /// 存储服务器地址 /// /// The API server. public string ApiServer { get; set; } /// /// 云引擎服务器地址 /// /// The engine server uri. public string EngineServer { get; set; } /// /// 即时通信服务器地址 /// /// The RTMR outer. public string RTMServer { get; set; } /// /// 直连即时通信服务器地址 /// /// The realtime server. 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(); } /// /// The current configuration that LeanCloud has been initialized with. /// 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; /// /// 当前 SDK 版本号 /// public static string VersionString { get { return versionString; } } /// /// 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 AVClient.Initialize in your /// Application startup. /// /// The Application ID provided in the LeanCloud dashboard. /// /// The .NET API Key provided in the LeanCloud dashboard. /// public static void Initialize(string applicationId, string applicationKey) { Initialize(new Configuration { ApplicationId = applicationId, ApplicationKey = applicationKey }); } internal static Action LogTracker { get; private set; } /// /// 启动日志打印 /// /// public static void HttpLog(Action trace) { LogTracker = trace; } /// /// 打印 HTTP 访问日志 /// /// public static void PrintLog(string log) { if (AVClient.LogTracker != null) { AVClient.LogTracker(log); } } static bool useProduction = true; /// /// Gets or sets a value indicating whether send the request to production server or staging server. /// /// true if use production; otherwise, false. public static bool UseProduction { get { return useProduction; } set { useProduction = value; } } static bool useMasterKey = false; public static bool UseMasterKey { get { return useMasterKey; } set { useMasterKey = value; } } /// /// 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 AVClient.Initialize in your /// Application startup. /// /// The configuration to initialize LeanCloud with. /// public static void Initialize(Configuration configuration) { Config(configuration); AVObject.RegisterSubclass(); AVObject.RegisterSubclass(); AVObject.RegisterSubclass(); } 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(); } /// /// Switch app. /// /// Configuration. 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 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 DecodeQueryString(string queryString) { var dict = new Dictionary(); 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 DeserializeJsonString(string jsonData) { return Json.Parse(jsonData) as IDictionary; } internal static string SerializeJsonString(IDictionary jsonData) { return Json.Encode(jsonData); } public static Task> HttpGetAsync(Uri uri) { return RequestAsync(uri, "GET", null, body: null, contentType: null, cancellationToken: CancellationToken.None); } public static Task> RequestAsync(Uri uri, string method, IList> headers, IDictionary 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> RequestAsync(Uri uri, string method, IList> 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> ReponseResolve(Tuple response, CancellationToken cancellationToken) { Tuple result = response; HttpStatusCode code = result.Item1; string item2 = result.Item2; if (item2 == null) { cancellationToken.ThrowIfCancellationRequested(); return new Tuple>(code, null); } IDictionary strs = null; try { strs = (!item2.StartsWith("[", StringComparison.Ordinal) ? AVClient.DeserializeJsonString(item2) : new Dictionary() { { "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>(code, strs); } internal static Task>> RequestAsync(string method, Uri relativeUri, string sessionToken, IDictionary 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>> 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(); } } }