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();
}
}
}