4665 changed files with 567985 additions and 0 deletions
@ -0,0 +1,144 @@
|
||||
# ---> Unity |
||||
/[Ll]ibrary/ |
||||
/[Tt]emp/ |
||||
/[Oo]bj/ |
||||
/[Bb]uild/ |
||||
/Assets/StreamingAssets/AssetBundles/ |
||||
/Assets/StreamingAssets/Systems/ |
||||
# Autogenerated VS/MD solution and project files |
||||
*.csproj |
||||
*.unityproj |
||||
*.sln |
||||
*.suo |
||||
*.tmp |
||||
*.user |
||||
*.userprefs |
||||
*.pidb |
||||
*.booproj |
||||
|
||||
# Unity3D generated meta files |
||||
|
||||
# Unity3D Generated File On Crash Reports |
||||
sysinfo.txt |
||||
|
||||
# ---> C Sharp |
||||
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) |
||||
[Bb]in/ |
||||
[Oo]bj/ |
||||
|
||||
# mstest test results |
||||
TestResults |
||||
|
||||
## Ignore Visual Studio temporary files, build results, and |
||||
## files generated by popular Visual Studio add-ons. |
||||
|
||||
# User-specific files |
||||
*.suo |
||||
*.user |
||||
*.sln.docstates |
||||
|
||||
# Build results |
||||
[Dd]ebug/ |
||||
[Rr]elease/ |
||||
x64/ |
||||
*_i.c |
||||
*_p.c |
||||
*.ilk |
||||
*.obj |
||||
*.pch |
||||
*.pdb |
||||
*.pgc |
||||
*.pgd |
||||
*.rsp |
||||
*.sbr |
||||
*.tlb |
||||
*.tli |
||||
*.tlh |
||||
*.tmp |
||||
*.log |
||||
*.vspscc |
||||
*.vssscc |
||||
.builds |
||||
|
||||
# Visual C++ cache files |
||||
ipch/ |
||||
*.aps |
||||
*.ncb |
||||
*.opensdf |
||||
*.sdf |
||||
|
||||
# Visual Studio profiler |
||||
*.psess |
||||
*.vsp |
||||
*.vspx |
||||
|
||||
# Guidance Automation Toolkit |
||||
*.gpState |
||||
|
||||
# ReSharper is a .NET coding add-in |
||||
_ReSharper* |
||||
|
||||
# NCrunch |
||||
*.ncrunch* |
||||
.*crunch*.local.xml |
||||
|
||||
# Installshield output folder |
||||
[Ee]xpress |
||||
|
||||
# DocProject is a documentation generator add-in |
||||
DocProject/buildhelp/ |
||||
DocProject/Help/*.HxT |
||||
DocProject/Help/*.HxC |
||||
DocProject/Help/*.hhc |
||||
DocProject/Help/*.hhk |
||||
DocProject/Help/*.hhp |
||||
DocProject/Help/Html2 |
||||
DocProject/Help/html |
||||
|
||||
# Click-Once directory |
||||
publish |
||||
|
||||
# Publish Web Output |
||||
*.Publish.xml |
||||
|
||||
# NuGet Packages Directory |
||||
packages |
||||
|
||||
# Windows Azure Build Output |
||||
csx |
||||
*.build.csdef |
||||
|
||||
# Windows Store app package directory |
||||
AppPackages/ |
||||
|
||||
# Others |
||||
[Bb]in |
||||
[Oo]bj |
||||
sql |
||||
TestResults |
||||
[Tt]est[Rr]esult* |
||||
*.Cache |
||||
ClientBin |
||||
[Ss]tyle[Cc]op.* |
||||
~$* |
||||
*.dbmdl |
||||
Generated_Code #added for RIA/Silverlight projects |
||||
|
||||
# Backup & report files from converting an old project file to a newer |
||||
# Visual Studio version. Backup files are not needed, because we have git ;-) |
||||
_UpgradeReport_Files/ |
||||
Backup*/ |
||||
UpgradeLog*.XML |
||||
|
||||
/Logs |
||||
/.vs |
||||
/Assets/StreamingAssets/AssetBundles.meta |
||||
/Assets/StreamingAssets/ScenePics |
||||
.vsconfig |
||||
UserSettings |
||||
/UserSettings |
||||
UserSettings |
||||
/UserSettings/EditorUserSettings.asset |
||||
/Assets/Plugins/tripolygon/PastelTown-BuiltInRP-UModeler/Textures/debug.log.meta |
||||
/Assets/StreamingAssets/model |
||||
/Assets/StreamingAssets/model.meta |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: f106eb2547e82e34a992bd3612baf073 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 57597f74dcc2b134d8b4affb37eb371a |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 1ce3e085b670ef7408bf0db1d6ad9437 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
Binary file not shown.
@ -0,0 +1,76 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 6a3c684705042f345975d924f6983e36 |
||||
timeCreated: 1466788352 |
||||
licenseType: Store |
||||
PluginImporter: |
||||
serializedVersion: 1 |
||||
iconMap: {} |
||||
executionOrder: {} |
||||
isPreloaded: 0 |
||||
platformData: |
||||
Android: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
Any: |
||||
enabled: 0 |
||||
settings: {} |
||||
Editor: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
DefaultValueInitialized: true |
||||
OS: AnyOS |
||||
Linux: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: x86 |
||||
Linux64: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: x86_64 |
||||
OSXIntel: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
OSXIntel64: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
SamsungTV: |
||||
enabled: 1 |
||||
settings: |
||||
STV_MODEL: STANDARD_13 |
||||
Tizen: |
||||
enabled: 1 |
||||
settings: {} |
||||
WebGL: |
||||
enabled: 1 |
||||
settings: {} |
||||
Win: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
Win64: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
WindowsStoreApps: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
DontProcess: False |
||||
PlaceholderPath: Assets/JsonDotNet/Assemblies/Standalone/Newtonsoft.Json.dll |
||||
SDK: AnySDK |
||||
ScriptingBackend: Il2Cpp |
||||
iOS: |
||||
enabled: 1 |
||||
settings: |
||||
CompileFlags: |
||||
FrameworkDependencies: |
||||
tvOS: |
||||
enabled: 1 |
||||
settings: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 17f5d20e192afae459aa673c7d64a7b6 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
Binary file not shown.
@ -0,0 +1,75 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 17aef65a15b471f468b5fbeb4ff0c6a1 |
||||
timeCreated: 1466788349 |
||||
licenseType: Store |
||||
PluginImporter: |
||||
serializedVersion: 1 |
||||
iconMap: {} |
||||
executionOrder: {} |
||||
isPreloaded: 0 |
||||
platformData: |
||||
Android: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
Any: |
||||
enabled: 0 |
||||
settings: {} |
||||
Editor: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
DefaultValueInitialized: true |
||||
OS: AnyOS |
||||
Linux: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: x86 |
||||
Linux64: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: x86_64 |
||||
LinuxUniversal: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
OSXIntel: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
OSXIntel64: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
OSXUniversal: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
SamsungTV: |
||||
enabled: 0 |
||||
settings: |
||||
STV_MODEL: STANDARD_13 |
||||
Win: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
Win64: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
WindowsStoreApps: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
DontProcess: False |
||||
PlaceholderPath: |
||||
SDK: AnySDK |
||||
ScriptingBackend: Il2Cpp |
||||
iOS: |
||||
enabled: 0 |
||||
settings: |
||||
CompileFlags: |
||||
FrameworkDependencies: |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: b47e3c4b434c79946ad8624d894c7aee |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
Binary file not shown.
@ -0,0 +1,67 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 9b6ba260dada0ea4a871a42011f8b87d |
||||
timeCreated: 1466788355 |
||||
licenseType: Store |
||||
PluginImporter: |
||||
serializedVersion: 1 |
||||
iconMap: {} |
||||
executionOrder: {} |
||||
isPreloaded: 0 |
||||
platformData: |
||||
Android: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
Any: |
||||
enabled: 0 |
||||
settings: {} |
||||
Editor: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
DefaultValueInitialized: true |
||||
OS: AnyOS |
||||
Linux: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: x86 |
||||
Linux64: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: x86_64 |
||||
OSXIntel: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
OSXIntel64: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
SamsungTV: |
||||
enabled: 0 |
||||
settings: |
||||
STV_MODEL: STANDARD_13 |
||||
Win: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
Win64: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
WindowsStoreApps: |
||||
enabled: 1 |
||||
settings: |
||||
CPU: AnyCPU |
||||
DontProcess: False |
||||
PlaceholderPath: Assets/JsonDotNet/Assemblies/Standalone/Newtonsoft.Json.dll |
||||
SDK: AnySDK |
||||
ScriptingBackend: DotNet |
||||
iOS: |
||||
enabled: 0 |
||||
settings: |
||||
CompileFlags: |
||||
FrameworkDependencies: |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 9b865acf4b76d314089bd3c5e07bec1d |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 1540279ecf377fe4fb07e4f0358f15c1 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 7afc95d5f9cc73d4e887a85e7ac1c9b2 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: fa3387f79caf3814e8ecb6bbdeb9c73e |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,209 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
using BestHTTP; |
||||
|
||||
namespace BestHTTP.Examples.HTTP |
||||
{ |
||||
public sealed class AssetBundleSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[Tooltip("The url of the resource to download")] |
||||
[SerializeField] |
||||
private string _path = "/AssetBundles/WebGL/demobundle.assetbundle"; |
||||
|
||||
[SerializeField] |
||||
private string _assetnameInBundle = "9443182_orig"; |
||||
|
||||
[SerializeField] |
||||
private Text _statusText; |
||||
|
||||
[SerializeField] |
||||
private RawImage _rawImage; |
||||
|
||||
[SerializeField] |
||||
private Button _downloadButton; |
||||
|
||||
#pragma warning restore |
||||
|
||||
#region Private Fields |
||||
|
||||
/// <summary> |
||||
/// Reference to the request to be able to call Abort on it. |
||||
/// </summary> |
||||
HTTPRequest request; |
||||
|
||||
/// <summary> |
||||
/// The downloaded and cached AssetBundle |
||||
/// </summary> |
||||
AssetBundle cachedBundle; |
||||
|
||||
#endregion |
||||
|
||||
#region Unity Events |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
this._statusText.text = "Waiting for user interaction"; |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
if (this.request != null) |
||||
this.request.Abort(); |
||||
this.request = null; |
||||
|
||||
UnloadBundle(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// GUI button callback |
||||
/// </summary> |
||||
public void OnStartDownloadButton() |
||||
{ |
||||
this._downloadButton.enabled = false; |
||||
UnloadBundle(); |
||||
|
||||
StartCoroutine(DownloadAssetBundle()); |
||||
} |
||||
|
||||
#endregion |
||||
|
||||
#region Private Helper Functions |
||||
|
||||
IEnumerator DownloadAssetBundle() |
||||
{ |
||||
// Create and send our request |
||||
request = new HTTPRequest(new Uri(this.sampleSelector.BaseURL + this._path)).Send(); |
||||
|
||||
this._statusText.text = "Download started"; |
||||
|
||||
// Wait while it's finishes and add some fancy dots to display something while the user waits for it. |
||||
// A simple "yield return StartCoroutine(request);" would do the job too. |
||||
while (request.State < HTTPRequestStates.Finished) |
||||
{ |
||||
yield return new WaitForSeconds(0.1f); |
||||
|
||||
this._statusText.text += "."; |
||||
} |
||||
|
||||
// Check the outcome of our request. |
||||
switch (request.State) |
||||
{ |
||||
// The request finished without any problem. |
||||
case HTTPRequestStates.Finished: |
||||
|
||||
if (request.Response.IsSuccess) |
||||
{ |
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
if (request.Response.IsFromCache) |
||||
this._statusText.text = "Loaded from local cache!"; |
||||
else |
||||
this._statusText.text = "Downloaded!"; |
||||
#else |
||||
this._statusText.text = "Downloaded!"; |
||||
#endif |
||||
|
||||
// Start creating the downloaded asset bundle |
||||
AssetBundleCreateRequest async = |
||||
#if UNITY_5_3_OR_NEWER |
||||
AssetBundle.LoadFromMemoryAsync(request.Response.Data); |
||||
#else |
||||
AssetBundle.CreateFromMemory(request.Response.Data); |
||||
#endif |
||||
|
||||
// wait for it |
||||
yield return async; |
||||
|
||||
BestHTTP.PlatformSupport.Memory.BufferPool.Release(request.Response.Data); |
||||
|
||||
// And process the bundle |
||||
yield return StartCoroutine(ProcessAssetBundle(async.assetBundle)); |
||||
} |
||||
else |
||||
{ |
||||
this._statusText.text = string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", |
||||
request.Response.StatusCode, |
||||
request.Response.Message, |
||||
request.Response.DataAsText); |
||||
Debug.LogWarning(this._statusText.text); |
||||
} |
||||
|
||||
break; |
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error. |
||||
case HTTPRequestStates.Error: |
||||
this._statusText.text = "Request Finished with Error! " + (request.Exception != null ? (request.Exception.Message + "\n" + request.Exception.StackTrace) : "No Exception"); |
||||
Debug.LogError(this._statusText.text); |
||||
break; |
||||
|
||||
// The request aborted, initiated by the user. |
||||
case HTTPRequestStates.Aborted: |
||||
this._statusText.text = "Request Aborted!"; |
||||
Debug.LogWarning(this._statusText.text); |
||||
break; |
||||
|
||||
// Connecting to the server is timed out. |
||||
case HTTPRequestStates.ConnectionTimedOut: |
||||
this._statusText.text = "Connection Timed Out!"; |
||||
Debug.LogError(this._statusText.text); |
||||
break; |
||||
|
||||
// The request didn't finished in the given time. |
||||
case HTTPRequestStates.TimedOut: |
||||
this._statusText.text = "Processing the request Timed Out!"; |
||||
Debug.LogError(this._statusText.text); |
||||
break; |
||||
} |
||||
|
||||
this._downloadButton.enabled = true; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// In this function we can do whatever we want with the freshly downloaded bundle. |
||||
/// In this example we will cache it for later use, and we will load a texture from it. |
||||
/// </summary> |
||||
IEnumerator ProcessAssetBundle(AssetBundle bundle) |
||||
{ |
||||
if (bundle == null) |
||||
yield break; |
||||
|
||||
// Save the bundle for future use |
||||
cachedBundle = bundle; |
||||
|
||||
// Start loading the asset from the bundle |
||||
var asyncAsset = |
||||
#if UNITY_5_1 || UNITY_5_2 || UNITY_5_3_OR_NEWER |
||||
cachedBundle.LoadAssetAsync(this._assetnameInBundle, typeof(Texture2D)); |
||||
#else |
||||
|
||||
cachedBundle.LoadAsync(this._assetnameInBundle, typeof(Texture2D)); |
||||
#endif |
||||
|
||||
// wait til load |
||||
yield return asyncAsset; |
||||
|
||||
// get the texture |
||||
this._rawImage.texture = asyncAsset.asset as Texture2D; |
||||
} |
||||
|
||||
void UnloadBundle() |
||||
{ |
||||
this._rawImage.texture = null; |
||||
|
||||
if (cachedBundle != null) |
||||
{ |
||||
cachedBundle.Unload(true); |
||||
cachedBundle = null; |
||||
} |
||||
} |
||||
|
||||
#endregion |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 03eab9c6191c5cd4c9613084e817bd29 |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,197 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using BestHTTP.Extensions; |
||||
using BestHTTP.PlatformSupport.Memory; |
||||
|
||||
namespace BestHTTP |
||||
{ |
||||
/// <summary> |
||||
/// Stream based implementation of the multipart/form-data Content-Type. Using this class reading a whole file into memory can be avoided. |
||||
/// This implementation expects that all streams has a final, accessible Length. |
||||
/// </summary> |
||||
public sealed class MultipartFormDataStream : System.IO.Stream |
||||
{ |
||||
public override bool CanRead { get { return true; } } |
||||
|
||||
public override bool CanSeek { get { return false; } } |
||||
|
||||
public override bool CanWrite { get { return false; } } |
||||
|
||||
public override long Length |
||||
{ |
||||
get |
||||
{ |
||||
// multipart/form-data requires a leading boundary that we can add when all streams are added. |
||||
// This final preparation could be user initiated, but we can do it automatically too when the HTTPRequest |
||||
// first access the Length property. |
||||
if (!this.prepared) |
||||
{ |
||||
this.prepared = true; |
||||
this.Prepare(); |
||||
} |
||||
|
||||
return this._length; |
||||
} |
||||
} |
||||
private long _length; |
||||
|
||||
public override long Position { get; set; } |
||||
/// <summary> |
||||
/// A random boundary generated in the constructor. |
||||
/// </summary> |
||||
private string boundary; |
||||
|
||||
private Queue<StreamList> fields = new Queue<StreamList>(1); |
||||
private StreamList currentField; |
||||
private bool prepared; |
||||
|
||||
public MultipartFormDataStream(HTTPRequest request) |
||||
{ |
||||
this.boundary = "BestHTTP_MultipartFormDataStream_" + this.GetHashCode().ToString("X2"); |
||||
|
||||
request.SetHeader("Content-Type", "multipart/form-data; boundary=" + boundary); |
||||
request.UploadStream = this; |
||||
request.UseUploadStreamLength = true; |
||||
} |
||||
|
||||
public void AddField(string fieldName, string value) |
||||
{ |
||||
AddField(fieldName, value, System.Text.Encoding.UTF8); |
||||
} |
||||
|
||||
public void AddField(string fieldName, string value, System.Text.Encoding encoding) |
||||
{ |
||||
var enc = encoding ?? System.Text.Encoding.UTF8; |
||||
var byteCount = enc.GetByteCount(value); |
||||
var buffer = BufferPool.Get(byteCount, true); |
||||
var stream = new BufferPoolMemoryStream(); |
||||
|
||||
enc.GetBytes(value, 0, value.Length, buffer, 0); |
||||
|
||||
stream.Write(buffer, 0, byteCount); |
||||
|
||||
stream.Position = 0; |
||||
|
||||
string mime = encoding != null ? "text/plain; charset=" + encoding.WebName : null; |
||||
AddStreamField(stream, fieldName, null, mime); |
||||
} |
||||
|
||||
public void AddStreamField(System.IO.Stream stream, string fieldName) |
||||
{ |
||||
AddStreamField(stream, fieldName, null, null); |
||||
} |
||||
|
||||
public void AddStreamField(System.IO.Stream stream, string fieldName, string fileName) |
||||
{ |
||||
AddStreamField(stream, fieldName, fileName, null); |
||||
} |
||||
|
||||
public void AddStreamField(System.IO.Stream stream, string fieldName, string fileName, string mimeType) |
||||
{ |
||||
var header = new BufferPoolMemoryStream(); |
||||
header.WriteLine("--" + this.boundary); |
||||
header.WriteLine("Content-Disposition: form-data; name=\"" + fieldName + "\"" + (!string.IsNullOrEmpty(fileName) ? "; filename=\"" + fileName + "\"" : string.Empty)); |
||||
// Set up Content-Type head for the form. |
||||
if (!string.IsNullOrEmpty(mimeType)) |
||||
header.WriteLine("Content-Type: " + mimeType); |
||||
//header.WriteLine("Content-Length: " + stream.Length.ToString()); |
||||
header.WriteLine(); |
||||
header.Position = 0; |
||||
|
||||
var footer = new BufferPoolMemoryStream(); |
||||
footer.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length); |
||||
footer.Position = 0; |
||||
|
||||
// all wrapped streams going to be disposed by the StreamList wrapper. |
||||
var wrapper = new StreamList(header, stream, footer); |
||||
|
||||
try |
||||
{ |
||||
if (this._length >= 0) |
||||
this._length += wrapper.Length; |
||||
} |
||||
catch |
||||
{ |
||||
this._length = -1; |
||||
} |
||||
|
||||
this.fields.Enqueue(wrapper); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Adds the final boundary. |
||||
/// </summary> |
||||
private void Prepare() |
||||
{ |
||||
var boundaryStream = new BufferPoolMemoryStream(); |
||||
boundaryStream.WriteLine("--" + this.boundary + "--"); |
||||
boundaryStream.Position = 0; |
||||
|
||||
this.fields.Enqueue(new StreamList(boundaryStream)); |
||||
|
||||
if (this._length >= 0) |
||||
this._length += boundaryStream.Length; |
||||
} |
||||
|
||||
public override int Read(byte[] buffer, int offset, int length) |
||||
{ |
||||
if (this.currentField == null && this.fields.Count == 0) |
||||
return -1; |
||||
|
||||
if (this.currentField == null && this.fields.Count > 0) |
||||
this.currentField = this.fields.Dequeue(); |
||||
|
||||
int readCount = 0; |
||||
|
||||
do |
||||
{ |
||||
// read from the current stream |
||||
int count = this.currentField.Read(buffer, offset + readCount, length - readCount); |
||||
|
||||
if (count > 0) |
||||
readCount += count; |
||||
else |
||||
{ |
||||
// if the current field's stream is empty, go for the next one. |
||||
|
||||
// dispose the current one first |
||||
try |
||||
{ |
||||
this.currentField.Dispose(); |
||||
} |
||||
catch |
||||
{ } |
||||
|
||||
// no more fields/streams? exit |
||||
if (this.fields.Count == 0) |
||||
break; |
||||
|
||||
// grab the next one |
||||
this.currentField = this.fields.Dequeue(); |
||||
} |
||||
|
||||
// exit when we reach the length goal, or there's no more streams to read from |
||||
} while (readCount < length && this.fields.Count > 0); |
||||
|
||||
return readCount; |
||||
} |
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public override void SetLength(long value) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public override void Flush() { } |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2 |
||||
guid: d54866f088b8c154db3325a2771b58af |
||||
timeCreated: 1582886645 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,96 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
using BestHTTP; |
||||
|
||||
namespace BestHTTP.Examples.HTTP |
||||
{ |
||||
public sealed class ResumableStreamingSample : StreamingSample |
||||
{ |
||||
const string ProcessedBytesKey = "ProcessedBytes"; |
||||
const string DownloadLengthKey = "DownloadLength"; |
||||
|
||||
/// <summary> |
||||
/// Expected content length |
||||
/// </summary> |
||||
protected override long DownloadLength { get { return PlayerPrefs.GetInt(this._downloadPath + DownloadLengthKey); } set { PlayerPrefs.SetInt(this._downloadPath + DownloadLengthKey, (int)value); } } |
||||
|
||||
/// <summary> |
||||
/// Total processed bytes |
||||
/// </summary> |
||||
protected override long ProcessedBytes { get { return PlayerPrefs.GetInt(this._downloadPath + ProcessedBytesKey, 0); } set { PlayerPrefs.SetInt(this._downloadPath + ProcessedBytesKey, (int)value); } } |
||||
|
||||
private long downloadStartedAt = 0; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
// If we have a non-finished download, set the progress to the value where we left it |
||||
float progress = GetSavedProgress(); |
||||
if (progress > 0.0f) |
||||
{ |
||||
this._downloadProgressSlider.value = progress; |
||||
base._statusText.text = progress.ToString("F2"); |
||||
} |
||||
} |
||||
|
||||
protected override void SetupRequest() |
||||
{ |
||||
base.SetupRequest(); |
||||
|
||||
// Are there any progress, that we can continue? |
||||
this.downloadStartedAt = this.ProcessedBytes; |
||||
|
||||
if (this.downloadStartedAt > 0) |
||||
{ |
||||
// Set the range header |
||||
request.SetRangeHeader(this.downloadStartedAt); |
||||
} |
||||
else |
||||
// This is a new request |
||||
DeleteKeys(); |
||||
} |
||||
|
||||
protected override void OnRequestFinished(HTTPRequest req, HTTPResponse resp) |
||||
{ |
||||
base.OnRequestFinished(req, resp); |
||||
|
||||
if (req.State == HTTPRequestStates.Finished && resp.IsSuccess) |
||||
DeleteKeys(); |
||||
} |
||||
|
||||
protected override void OnDownloadProgress(HTTPRequest originalRequest, long downloaded, long downloadLength) |
||||
{ |
||||
double downloadPercent = ((this.downloadStartedAt + downloaded) / (double)this.DownloadLength) * 100; |
||||
|
||||
this._downloadProgressSlider.value = (float)downloadPercent; |
||||
this._downloadProgressText.text = string.Format("{0:F1}%", downloadPercent); |
||||
} |
||||
|
||||
protected override void ResetProcessedValues() |
||||
{ |
||||
SetDataProcessedUI(this.ProcessedBytes, this.DownloadLength); |
||||
} |
||||
|
||||
private float GetSavedProgress() |
||||
{ |
||||
long down = this.ProcessedBytes; |
||||
long length = this.DownloadLength; |
||||
|
||||
if (down > 0 && length > 0) |
||||
return (down / (float)length) * 100f; |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
private void DeleteKeys() |
||||
{ |
||||
PlayerPrefs.DeleteKey(this._downloadPath + ProcessedBytesKey); |
||||
PlayerPrefs.DeleteKey(this._downloadPath + DownloadLengthKey); |
||||
PlayerPrefs.Save(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 972fc2301f87c9e46bfb5523f2bc5090 |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,272 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
using BestHTTP; |
||||
|
||||
namespace BestHTTP.Examples.HTTP |
||||
{ |
||||
public class StreamingSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[Tooltip("The url of the resource to download")] |
||||
[SerializeField] |
||||
protected string _downloadPath = "/test100mb.dat"; |
||||
|
||||
[Header("Streaming Setup")] |
||||
|
||||
[SerializeField] |
||||
protected RectTransform _streamingSetupRoot; |
||||
|
||||
[SerializeField] |
||||
protected Slider _fragmentSizeSlider; |
||||
|
||||
[SerializeField] |
||||
protected Text _fragmentSizeText; |
||||
|
||||
[SerializeField] |
||||
protected Toggle _disableCacheToggle; |
||||
|
||||
[Header("Reporting")] |
||||
|
||||
[SerializeField] |
||||
protected RectTransform _reportingRoot; |
||||
|
||||
[SerializeField] |
||||
protected Slider _downloadProgressSlider; |
||||
|
||||
[SerializeField] |
||||
protected Text _downloadProgressText; |
||||
|
||||
[SerializeField] |
||||
protected Slider _processedDataSlider; |
||||
|
||||
[SerializeField] |
||||
protected Text _processedDataText; |
||||
|
||||
[SerializeField] |
||||
protected Text _statusText; |
||||
|
||||
[SerializeField] |
||||
protected Button _startDownload; |
||||
|
||||
[SerializeField] |
||||
protected Button _cancelDownload; |
||||
|
||||
#pragma warning restore |
||||
|
||||
/// <summary> |
||||
/// Cached request to be able to abort it |
||||
/// </summary> |
||||
protected HTTPRequest request; |
||||
|
||||
/// <summary> |
||||
/// Download(processing) progress. Its range is between [0..1] |
||||
/// </summary> |
||||
protected float progress; |
||||
|
||||
/// <summary> |
||||
/// The fragment size that we will set to the request |
||||
/// </summary> |
||||
protected int fragmentSize = HTTPResponse.MinReadBufferSize; |
||||
|
||||
protected virtual long DownloadLength { get; set; } |
||||
|
||||
protected virtual long ProcessedBytes { get; set; } |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
this._streamingSetupRoot.gameObject.SetActive(true); |
||||
this._reportingRoot.gameObject.SetActive(false); |
||||
|
||||
this._startDownload.interactable = true; |
||||
this._cancelDownload.interactable = false; |
||||
|
||||
this._fragmentSizeSlider.value = (1024 * 1024 - HTTPResponse.MinReadBufferSize) / 1024; |
||||
this._fragmentSizeText.text = GUIHelper.GetBytesStr(1024 * 1024, 1); |
||||
} |
||||
|
||||
protected void OnDestroy() |
||||
{ |
||||
// Stop the download if we are leaving this example |
||||
if (request != null && request.State < HTTPRequestStates.Finished) |
||||
{ |
||||
request.OnDownloadProgress = null; |
||||
request.Callback = null; |
||||
request.Abort(); |
||||
} |
||||
} |
||||
|
||||
public void OnFragmentSizeSliderChanged(float value) |
||||
{ |
||||
this.fragmentSize = HTTPResponse.MinReadBufferSize + (int)value * 1024; |
||||
this._fragmentSizeText.text = GUIHelper.GetBytesStr(this.fragmentSize, 1); |
||||
} |
||||
|
||||
public void Cancel() |
||||
{ |
||||
if (this.request != null) |
||||
this.request.Abort(); |
||||
} |
||||
|
||||
protected virtual void SetupRequest() |
||||
{ |
||||
request = new HTTPRequest(new Uri(base.sampleSelector.BaseURL + this._downloadPath), OnRequestFinished); |
||||
|
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
// If we are writing our own file set it to true(disable), so don't duplicate it on the file-system |
||||
request.DisableCache = this._disableCacheToggle.isOn; |
||||
#endif |
||||
|
||||
request.StreamFragmentSize = fragmentSize; |
||||
|
||||
request.Tag = DateTime.Now; |
||||
|
||||
request.OnHeadersReceived += OnHeadersReceived; |
||||
request.OnDownloadProgress += OnDownloadProgress; |
||||
request.OnStreamingData += OnDataDownloaded; |
||||
} |
||||
|
||||
public virtual void StartStreaming() |
||||
{ |
||||
SetupRequest(); |
||||
|
||||
// Start Processing the request |
||||
request.Send(); |
||||
|
||||
this._statusText.text = "Download started!"; |
||||
|
||||
// UI |
||||
this._streamingSetupRoot.gameObject.SetActive(false); |
||||
this._reportingRoot.gameObject.SetActive(true); |
||||
|
||||
this._startDownload.interactable = false; |
||||
this._cancelDownload.interactable = true; |
||||
|
||||
ResetProcessedValues(); |
||||
} |
||||
|
||||
private void OnHeadersReceived(HTTPRequest req, HTTPResponse resp, Dictionary<string, List<string>> newHeaders) |
||||
{ |
||||
var range = resp.GetRange(); |
||||
if (range != null) |
||||
this.DownloadLength = range.ContentLength; |
||||
else |
||||
{ |
||||
var contentLength = resp.GetFirstHeaderValue("content-length"); |
||||
if (contentLength != null) |
||||
{ |
||||
long length = 0; |
||||
if (long.TryParse(contentLength, out length)) |
||||
this.DownloadLength = length; |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected virtual void OnRequestFinished(HTTPRequest req, HTTPResponse resp) |
||||
{ |
||||
switch (req.State) |
||||
{ |
||||
// The request finished without any problem. |
||||
case HTTPRequestStates.Finished: |
||||
if (resp.IsSuccess) |
||||
{ |
||||
DateTime downloadStarted = (DateTime)req.Tag; |
||||
TimeSpan diff = DateTime.Now - downloadStarted; |
||||
|
||||
this._statusText.text = string.Format("Streaming finished in {0:N0}ms", diff.TotalMilliseconds); |
||||
} |
||||
else |
||||
{ |
||||
this._statusText.text = string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", |
||||
resp.StatusCode, |
||||
resp.Message, |
||||
resp.DataAsText); |
||||
Debug.LogWarning(this._statusText.text); |
||||
|
||||
request = null; |
||||
} |
||||
break; |
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error. |
||||
case HTTPRequestStates.Error: |
||||
this._statusText.text = "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"); |
||||
Debug.LogError(this._statusText.text); |
||||
|
||||
request = null; |
||||
break; |
||||
|
||||
// The request aborted, initiated by the user. |
||||
case HTTPRequestStates.Aborted: |
||||
this._statusText.text = "Request Aborted!"; |
||||
Debug.LogWarning(this._statusText.text); |
||||
|
||||
request = null; |
||||
break; |
||||
|
||||
// Connecting to the server is timed out. |
||||
case HTTPRequestStates.ConnectionTimedOut: |
||||
this._statusText.text = "Connection Timed Out!"; |
||||
Debug.LogError(this._statusText.text); |
||||
|
||||
request = null; |
||||
break; |
||||
|
||||
// The request didn't finished in the given time. |
||||
case HTTPRequestStates.TimedOut: |
||||
this._statusText.text = "Processing the request Timed Out!"; |
||||
Debug.LogError(this._statusText.text); |
||||
|
||||
request = null; |
||||
break; |
||||
} |
||||
|
||||
// UI |
||||
|
||||
this._streamingSetupRoot.gameObject.SetActive(true); |
||||
this._reportingRoot.gameObject.SetActive(false); |
||||
|
||||
this._startDownload.interactable = true; |
||||
this._cancelDownload.interactable = false; |
||||
request = null; |
||||
} |
||||
|
||||
protected virtual void OnDownloadProgress(HTTPRequest originalRequest, long downloaded, long downloadLength) |
||||
{ |
||||
double downloadPercent = (downloaded / (double)downloadLength) * 100; |
||||
this._downloadProgressSlider.value = (float)downloadPercent; |
||||
this._downloadProgressText.text = string.Format("{0:F1}%", downloadPercent); |
||||
} |
||||
|
||||
protected virtual bool OnDataDownloaded(HTTPRequest request, HTTPResponse response, byte[] dataFragment, int dataFragmentLength) |
||||
{ |
||||
this.ProcessedBytes += dataFragmentLength; |
||||
SetDataProcessedUI(this.ProcessedBytes, this.DownloadLength); |
||||
|
||||
// Use downloaded data |
||||
|
||||
// Return true if dataFrament is processed so the plugin can recycle the byte[] |
||||
return true; |
||||
} |
||||
|
||||
protected void SetDataProcessedUI(long processed, long length) |
||||
{ |
||||
float processedPercent = (processed / (float)length) * 100f; |
||||
|
||||
this._processedDataSlider.value = processedPercent; |
||||
this._processedDataText.text = GUIHelper.GetBytesStr(processed, 0); |
||||
} |
||||
|
||||
protected virtual void ResetProcessedValues() |
||||
{ |
||||
this.ProcessedBytes = 0; |
||||
this.DownloadLength = 0; |
||||
|
||||
SetDataProcessedUI(this.ProcessedBytes, this.DownloadLength); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 7608ce95dba469c42b49baa85e3c300c |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,159 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
using BestHTTP; |
||||
|
||||
namespace BestHTTP.Examples.HTTP |
||||
{ |
||||
public sealed class TextureDownloadSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
[Header("Texture Download Example")] |
||||
|
||||
[Tooltip("The URL of the server that will serve the image resources")] |
||||
[SerializeField] |
||||
private string _path = "/images/Demo/"; |
||||
|
||||
[Tooltip("The downloadable images")] |
||||
[SerializeField] |
||||
private string[] _imageNames = new string[9] { "One.png", "Two.png", "Three.png", "Four.png", "Five.png", "Six.png", "Seven.png", "Eight.png", "Nine.png" }; |
||||
|
||||
[SerializeField] |
||||
private RawImage[] _images = new RawImage[0]; |
||||
|
||||
[SerializeField] |
||||
private Text _maxConnectionPerServerLabel; |
||||
|
||||
[SerializeField] |
||||
private Text _cacheLabel; |
||||
|
||||
#pragma warning restore |
||||
|
||||
private byte savedMaxConnectionPerServer; |
||||
|
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
private bool allDownloadedFromLocalCache; |
||||
#endif |
||||
|
||||
private List<HTTPRequest> activeRequests = new List<HTTPRequest>(); |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
this.savedMaxConnectionPerServer = HTTPManager.MaxConnectionPerServer; |
||||
|
||||
// Set a well observable value |
||||
// This is how many concurrent requests can be made to a server |
||||
HTTPManager.MaxConnectionPerServer = 1; |
||||
|
||||
this._maxConnectionPerServerLabel.text = HTTPManager.MaxConnectionPerServer.ToString(); |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
// Set back to its defualt value. |
||||
HTTPManager.MaxConnectionPerServer = this.savedMaxConnectionPerServer; |
||||
foreach (var request in this.activeRequests) |
||||
request.Abort(); |
||||
this.activeRequests.Clear(); |
||||
} |
||||
|
||||
public void OnMaxConnectionPerServerChanged(float value) |
||||
{ |
||||
HTTPManager.MaxConnectionPerServer = (byte)Mathf.RoundToInt(value); |
||||
this._maxConnectionPerServerLabel.text = HTTPManager.MaxConnectionPerServer.ToString(); |
||||
} |
||||
|
||||
public void DownloadImages() |
||||
{ |
||||
// Set these metadatas to its initial values |
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
allDownloadedFromLocalCache = true; |
||||
#endif |
||||
|
||||
for (int i = 0; i < _imageNames.Length; ++i) |
||||
{ |
||||
// Set a blank placeholder texture, overriding previously downloaded texture |
||||
this._images[i].texture = null; |
||||
|
||||
// Construct the request |
||||
var request = new HTTPRequest(new Uri(this.sampleSelector.BaseURL + this._path + this._imageNames[i]), ImageDownloaded); |
||||
|
||||
// Set the Tag property, we can use it as a general storage bound to the request |
||||
request.Tag = this._images[i]; |
||||
|
||||
// Send out the request |
||||
request.Send(); |
||||
|
||||
this.activeRequests.Add(request); |
||||
} |
||||
|
||||
this._cacheLabel.text = string.Empty; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Callback function of the image download http requests |
||||
/// </summary> |
||||
void ImageDownloaded(HTTPRequest req, HTTPResponse resp) |
||||
{ |
||||
switch (req.State) |
||||
{ |
||||
// The request finished without any problem. |
||||
case HTTPRequestStates.Finished: |
||||
if (resp.IsSuccess) |
||||
{ |
||||
// The target RawImage reference is stored in the Tag property |
||||
RawImage rawImage = req.Tag as RawImage; |
||||
rawImage.texture = resp.DataAsTexture2D; |
||||
|
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
// Update the cache-info variable |
||||
allDownloadedFromLocalCache = allDownloadedFromLocalCache && resp.IsFromCache; |
||||
#endif |
||||
} |
||||
else |
||||
{ |
||||
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", |
||||
resp.StatusCode, |
||||
resp.Message, |
||||
resp.DataAsText)); |
||||
} |
||||
break; |
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error. |
||||
case HTTPRequestStates.Error: |
||||
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception")); |
||||
break; |
||||
|
||||
// The request aborted, initiated by the user. |
||||
case HTTPRequestStates.Aborted: |
||||
Debug.LogWarning("Request Aborted!"); |
||||
break; |
||||
|
||||
// Connecting to the server is timed out. |
||||
case HTTPRequestStates.ConnectionTimedOut: |
||||
Debug.LogError("Connection Timed Out!"); |
||||
break; |
||||
|
||||
// The request didn't finished in the given time. |
||||
case HTTPRequestStates.TimedOut: |
||||
Debug.LogError("Processing the request Timed Out!"); |
||||
break; |
||||
} |
||||
|
||||
this.activeRequests.Remove(req); |
||||
if (this.activeRequests.Count == 0) |
||||
{ |
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
if (this.allDownloadedFromLocalCache) |
||||
this._cacheLabel.text = "All images loaded from local cache!"; |
||||
else |
||||
#endif |
||||
this._cacheLabel.text = string.Empty; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: af6ffe2bbf96d2b49ba3ef0713511d45 |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,222 @@
|
||||
using BestHTTP; |
||||
using System; |
||||
using System.IO; |
||||
using System.Threading; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
public sealed class UploadStream : Stream |
||||
{ |
||||
#region Private Fields |
||||
|
||||
/// <summary> |
||||
/// Buffer for reads |
||||
/// </summary> |
||||
MemoryStream ReadBuffer = new MemoryStream(); |
||||
|
||||
/// <summary> |
||||
/// Buffer for writes |
||||
/// </summary> |
||||
MemoryStream WriteBuffer = new MemoryStream(); |
||||
|
||||
/// <summary> |
||||
/// Indicates that we will not write more data to this stream |
||||
/// </summary> |
||||
bool noMoreData; |
||||
|
||||
/// <summary> |
||||
/// For thread synchronization |
||||
/// </summary> |
||||
AutoResetEvent ARE = new AutoResetEvent(false); |
||||
|
||||
/// <summary> |
||||
/// For thread synchronization |
||||
/// </summary> |
||||
object locker = new object(); |
||||
|
||||
#endregion |
||||
|
||||
#region Properties |
||||
|
||||
/// <summary> |
||||
/// Name of this stream for easier debugging |
||||
/// </summary> |
||||
public string Name { get; private set; } |
||||
|
||||
/// <summary> |
||||
/// true if we are read all data from the read buffer |
||||
/// </summary> |
||||
private bool IsReadBufferEmpty { get { lock (locker) return ReadBuffer.Position == ReadBuffer.Length; } } |
||||
|
||||
#endregion |
||||
|
||||
#region Constructors |
||||
|
||||
public UploadStream(string name) |
||||
: this() |
||||
{ |
||||
this.Name = name; |
||||
} |
||||
|
||||
public UploadStream() |
||||
{ |
||||
this.ReadBuffer = new MemoryStream(); |
||||
this.WriteBuffer = new MemoryStream(); |
||||
this.Name = string.Empty; |
||||
} |
||||
|
||||
#endregion |
||||
|
||||
#region Stream Implementation |
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) |
||||
{ |
||||
// We will not push more data to the write buffer |
||||
if (noMoreData) |
||||
{ |
||||
// No data left in the read buffer |
||||
if (ReadBuffer.Position == ReadBuffer.Length) |
||||
{ |
||||
// Is there any data in the write buffer? If so, switch the buffers |
||||
if (WriteBuffer.Length > 0) |
||||
SwitchBuffers(); |
||||
else |
||||
{ |
||||
HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Read - End Of Stream", this.Name)); |
||||
return -1; |
||||
} |
||||
} |
||||
else |
||||
return ReadBuffer.Read(buffer, offset, count); |
||||
} |
||||
|
||||
// There are no more data in the read buffer? Wait for it. |
||||
if (IsReadBufferEmpty) |
||||
{ |
||||
ARE.WaitOne(); |
||||
|
||||
lock (locker) |
||||
if (IsReadBufferEmpty && WriteBuffer.Length > 0) |
||||
SwitchBuffers(); |
||||
} |
||||
|
||||
int read = -1; |
||||
|
||||
lock (locker) |
||||
read = ReadBuffer.Read(buffer, offset, count); |
||||
|
||||
return read; |
||||
} |
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) |
||||
{ |
||||
if (noMoreData) |
||||
throw new System.ArgumentException("noMoreData already set!"); |
||||
|
||||
lock (locker) |
||||
{ |
||||
WriteBuffer.Write(buffer, offset, count); |
||||
|
||||
SwitchBuffers(); |
||||
} |
||||
|
||||
ARE.Set(); |
||||
} |
||||
|
||||
public override void Flush() |
||||
{ |
||||
Finish(); |
||||
} |
||||
|
||||
#endregion |
||||
|
||||
#region Dispose Implementation |
||||
|
||||
protected override void Dispose(bool disposing) |
||||
{ |
||||
if (disposing) |
||||
{ |
||||
HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Dispose", this.Name)); |
||||
|
||||
ReadBuffer.Dispose(); |
||||
ReadBuffer = null; |
||||
|
||||
WriteBuffer.Dispose(); |
||||
WriteBuffer = null; |
||||
|
||||
#if NETFX_CORE |
||||
ARE.Dispose(); |
||||
#else |
||||
ARE.Close(); |
||||
#endif |
||||
ARE = null; |
||||
} |
||||
|
||||
base.Dispose(disposing); |
||||
} |
||||
|
||||
#endregion |
||||
|
||||
#region Helper Functions |
||||
|
||||
public void Finish() |
||||
{ |
||||
if (noMoreData) |
||||
throw new System.ArgumentException("noMoreData already set!"); |
||||
|
||||
HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Finish", this.Name)); |
||||
|
||||
noMoreData = true; |
||||
|
||||
ARE.Set(); |
||||
} |
||||
|
||||
private bool SwitchBuffers() |
||||
{ |
||||
// Switch the buffers only when all data are consumed from our read buffer |
||||
lock (locker) |
||||
{ |
||||
if (ReadBuffer.Position == ReadBuffer.Length) |
||||
{ |
||||
// This buffer will be the read buffer, we need to seek back to the beginning |
||||
WriteBuffer.Seek(0, SeekOrigin.Begin); |
||||
|
||||
// This will be the write buffer, set the length to zero |
||||
ReadBuffer.SetLength(0); |
||||
|
||||
// switch the two buffers |
||||
MemoryStream tmp = WriteBuffer; |
||||
WriteBuffer = ReadBuffer; |
||||
ReadBuffer = tmp; |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
#endregion |
||||
|
||||
#region Not Implemented Functions and Properties |
||||
|
||||
public override bool CanRead { get { throw new NotImplementedException(); } } |
||||
public override bool CanSeek { get { throw new NotImplementedException(); } } |
||||
public override bool CanWrite { get { throw new NotImplementedException(); } } |
||||
|
||||
public override long Length { get { throw new NotImplementedException(); } } |
||||
public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } |
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public override void SetLength(long value) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
#endregion |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: a2ab021301a7baf45a80b5a46275a140 |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: e17235d56e1e78642af36ef4d6dcee27 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 514e2a6c85b1e3644b534eb9c892fff9 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,57 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using BestHTTP.Core; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.Helpers.Components |
||||
{ |
||||
public class Cache : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649, 0169 |
||||
[SerializeField] |
||||
private Text _count; |
||||
|
||||
[SerializeField] |
||||
private Text _size; |
||||
|
||||
[SerializeField] |
||||
private Button _clear; |
||||
#pragma warning restore |
||||
|
||||
private void Start() |
||||
{ |
||||
PluginEventHelper.OnEvent += OnPluginEvent; |
||||
UpdateLabels(); |
||||
} |
||||
|
||||
private void OnDestroy() |
||||
{ |
||||
PluginEventHelper.OnEvent -= OnPluginEvent; |
||||
} |
||||
|
||||
private void OnPluginEvent(PluginEventInfo @event) |
||||
{ |
||||
if (@event.Event == PluginEvents.SaveCacheLibrary) |
||||
UpdateLabels(); |
||||
} |
||||
|
||||
private void UpdateLabels() |
||||
{ |
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
this._count.text = BestHTTP.Caching.HTTPCacheService.GetCacheEntityCount().ToString("N0"); |
||||
this._size.text = BestHTTP.Caching.HTTPCacheService.GetCacheSize().ToString("N0"); |
||||
#else |
||||
this._count.text = "0"; |
||||
this._size.text = "0"; |
||||
#endif |
||||
} |
||||
|
||||
public void OnClearButtonClicked() |
||||
{ |
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
BestHTTP.Caching.HTTPCacheService.BeginClear(); |
||||
#endif |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: b640e13d843caef4885814c6d13d8e3d |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,63 @@
|
||||
using BestHTTP.Core; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.Helpers.Components |
||||
{ |
||||
public class Cookies : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649, 0169 |
||||
[SerializeField] |
||||
private Text _count; |
||||
|
||||
[SerializeField] |
||||
private Text _size; |
||||
|
||||
[SerializeField] |
||||
private Button _clear; |
||||
#pragma warning restore |
||||
|
||||
private void Start() |
||||
{ |
||||
PluginEventHelper.OnEvent += OnPluginEvent; |
||||
UpdateLabels(); |
||||
} |
||||
|
||||
private void OnDestroy() |
||||
{ |
||||
PluginEventHelper.OnEvent -= OnPluginEvent; |
||||
} |
||||
|
||||
private void OnPluginEvent(PluginEventInfo @event) |
||||
{ |
||||
#if !BESTHTTP_DISABLE_COOKIES |
||||
if (@event.Event == PluginEvents.SaveCookieLibrary) |
||||
UpdateLabels(); |
||||
#endif |
||||
} |
||||
|
||||
private void UpdateLabels() |
||||
{ |
||||
#if !BESTHTTP_DISABLE_COOKIES |
||||
var cookies = BestHTTP.Cookies.CookieJar.GetAll(); |
||||
var size = cookies.Sum(c => c.GuessSize()); |
||||
|
||||
this._count.text = cookies.Count.ToString("N0"); |
||||
this._size.text = size.ToString("N0"); |
||||
#else |
||||
this._count.text = "0"; |
||||
this._size.text = "0"; |
||||
#endif |
||||
} |
||||
|
||||
public void OnClearButtonClicked() |
||||
{ |
||||
#if !BESTHTTP_DISABLE_COOKIES |
||||
BestHTTP.Cookies.CookieJar.Clear(); |
||||
#endif |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 5b9db2d0fa31b054b835fb38d688f211 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,60 @@
|
||||
using BestHTTP.Examples.Helpers; |
||||
using System; |
||||
using System.Collections; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
public static class GUIHelper |
||||
{ |
||||
// https://en.wikipedia.org/wiki/Binary_prefix |
||||
private static string[] prefixes = new string[] { " B", " KiB", " MiB", " GiB", " TiB" }; |
||||
public static string GetBytesStr(double bytes, byte precision) |
||||
{ |
||||
int prefixIdx = 0; |
||||
while (bytes >= 1024) |
||||
{ |
||||
bytes = bytes / 1024; |
||||
prefixIdx++; |
||||
} |
||||
|
||||
return bytes.ToString("F" + precision) + prefixes[prefixIdx]; |
||||
} |
||||
|
||||
public static void RemoveChildren(RectTransform transform, int maxChildCount) |
||||
{ |
||||
while (transform.childCount > maxChildCount) |
||||
{ |
||||
var child = transform.GetChild(0); |
||||
child.SetParent(null); |
||||
|
||||
GameObject.Destroy(child.gameObject); |
||||
} |
||||
} |
||||
|
||||
public static TextListItem AddText(TextListItem prefab, RectTransform contentRoot, string text, int maxEntries, ScrollRect scrollRect) |
||||
{ |
||||
if (contentRoot == null) |
||||
return null; |
||||
|
||||
var listItem = GameObject.Instantiate<TextListItem>(prefab, contentRoot, false); |
||||
listItem.SetText(text); |
||||
|
||||
GUIHelper.RemoveChildren(contentRoot, maxEntries); |
||||
|
||||
if (scrollRect != null && scrollRect.isActiveAndEnabled) |
||||
scrollRect.StartCoroutine(ScrollToBottom(scrollRect)); |
||||
|
||||
return listItem; |
||||
} |
||||
|
||||
public static IEnumerator ScrollToBottom(ScrollRect scrollRect) |
||||
{ |
||||
yield return null; |
||||
|
||||
if (scrollRect != null && scrollRect.isActiveAndEnabled) |
||||
scrollRect.normalizedPosition = new Vector2(0, 0); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: fd560ff6f0fa60844a43a82db1ad03be |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,36 @@
|
||||
using System.Runtime.InteropServices; |
||||
using UnityEngine; |
||||
using UnityEngine.EventSystems; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
public class Link : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler |
||||
{ |
||||
public string url; |
||||
public Texture2D linkSelectCursor; |
||||
|
||||
void IPointerDownHandler.OnPointerDown(PointerEventData eventData) |
||||
{ |
||||
#if UNITY_WEBGL && !UNITY_EDITOR |
||||
openWindow(this.url); |
||||
#else |
||||
Application.OpenURL(this.url); |
||||
#endif |
||||
} |
||||
|
||||
void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData) |
||||
{ |
||||
Cursor.SetCursor(this.linkSelectCursor, Vector2.zero, CursorMode.Auto); |
||||
} |
||||
|
||||
void IPointerExitHandler.OnPointerExit(PointerEventData eventData) |
||||
{ |
||||
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto); |
||||
} |
||||
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR |
||||
[DllImport("__Internal")] |
||||
private static extern void openWindow(string url); |
||||
#endif |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 3ab0e37de384a5b4d8320cdd8644b44c |
||||
timeCreated: 1572211254 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,23 @@
|
||||
using UnityEngine; |
||||
|
||||
namespace BestHTTP.Examples.Helpers |
||||
{ |
||||
public abstract class SampleBase : MonoBehaviour |
||||
{ |
||||
[Header("Common Properties")] |
||||
public string Category; |
||||
public string DisplayName; |
||||
|
||||
[TextArea] |
||||
public string Description; |
||||
|
||||
public RuntimePlatform[] BannedPlatforms = new RuntimePlatform[0]; |
||||
|
||||
protected SampleRoot sampleSelector; |
||||
|
||||
protected virtual void Start() |
||||
{ |
||||
this.sampleSelector = FindObjectOfType<SampleRoot>(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 9f8792c52d9520447a1c9d7139925f57 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: beedbf79e87c74949aa50c9220fdf55c |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,22 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.Helpers.SelectorUI |
||||
{ |
||||
public sealed class Category : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[SerializeField] |
||||
private Text _text; |
||||
|
||||
#pragma warning restore |
||||
|
||||
public void SetLabel(string category) |
||||
{ |
||||
this._text.text = category; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 1101c9e1e1276414d8062a6a7ca4db45 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,37 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.Helpers.SelectorUI |
||||
{ |
||||
public sealed class ExampleInfo : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649 |
||||
[SerializeField] |
||||
private Text _header; |
||||
|
||||
[SerializeField] |
||||
private Text _description; |
||||
|
||||
#pragma warning restore |
||||
|
||||
private SampleSelectorUI _parentUI; |
||||
|
||||
private SampleBase _example; |
||||
|
||||
public void Setup(SampleSelectorUI parentUI, SampleBase example) |
||||
{ |
||||
this._parentUI = parentUI; |
||||
this._example = example; |
||||
|
||||
this._header.text = this._example.name; |
||||
this._description.text = this._example.Description; |
||||
} |
||||
|
||||
public void OnExecuteExample() |
||||
{ |
||||
this._parentUI.ExecuteExample(this._example); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: fd424cc4259865c4ba2fb4d9d68d0272 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,32 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.Helpers.SelectorUI |
||||
{ |
||||
public sealed class ExampleListItem : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649 |
||||
[SerializeField] |
||||
private Text _text; |
||||
#pragma warning restore |
||||
|
||||
public SampleSelectorUI ParentUI { get; private set; } |
||||
|
||||
public SampleBase ExamplePrefab { get; private set; } |
||||
|
||||
public void Setup(SampleSelectorUI parentUI, SampleBase prefab) |
||||
{ |
||||
this.ParentUI = parentUI; |
||||
this.ExamplePrefab = prefab; |
||||
|
||||
this._text.text = prefab.DisplayName; |
||||
} |
||||
|
||||
public void OnButton() |
||||
{ |
||||
this.ParentUI.SelectSample(this); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 376fb4eb0776e41479fb60f520aaa1f3 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,101 @@
|
||||
using System; |
||||
using System.Linq; |
||||
using System.Collections.Generic; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.Helpers.SelectorUI |
||||
{ |
||||
public class SampleSelectorUI : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649, 0169 |
||||
|
||||
[SerializeField] |
||||
private Category _categoryListItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private ExampleListItem _exampleListItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private ExampleInfo _exampleInfoPrefab; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _listRoot; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _dyncamicContentRoot; |
||||
|
||||
private SampleRoot sampleSelector; |
||||
private ExampleListItem selectedSample; |
||||
private GameObject dynamicContent; |
||||
|
||||
#pragma warning restore |
||||
|
||||
private void Start() |
||||
{ |
||||
this.sampleSelector = FindObjectOfType<SampleRoot>(); |
||||
DisplayExamples(); |
||||
} |
||||
|
||||
private void DisplayExamples() |
||||
{ |
||||
// Sort examples by category |
||||
this.sampleSelector.samples.Sort((a, b) => { |
||||
if (a == null || b == null) |
||||
return 0; |
||||
|
||||
int result = a.Category.CompareTo(b.Category); |
||||
if (result == 0) |
||||
result = a.DisplayName.CompareTo(b.DisplayName); |
||||
return result; |
||||
}); |
||||
|
||||
string currentCategory = null; |
||||
|
||||
for (int i = 0; i < this.sampleSelector.samples.Count; ++i) |
||||
{ |
||||
var examplePrefab = this.sampleSelector.samples[i]; |
||||
|
||||
if (examplePrefab == null) |
||||
continue; |
||||
|
||||
if (examplePrefab.BannedPlatforms.Contains(UnityEngine.Application.platform)) |
||||
continue; |
||||
|
||||
if (currentCategory != examplePrefab.Category) |
||||
{ |
||||
var category = Instantiate<Category>(this._categoryListItemPrefab, this._listRoot, false); |
||||
category.SetLabel(examplePrefab.Category); |
||||
|
||||
currentCategory = examplePrefab.Category; |
||||
} |
||||
|
||||
var listItem = Instantiate<ExampleListItem>(this._exampleListItemPrefab, this._listRoot, false); |
||||
listItem.Setup(this, examplePrefab); |
||||
|
||||
if (this.sampleSelector.selectedExamplePrefab == null) |
||||
{ |
||||
SelectSample(listItem); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void SelectSample(ExampleListItem item) |
||||
{ |
||||
this.sampleSelector.selectedExamplePrefab = item.ExamplePrefab; |
||||
if (this.dynamicContent != null) |
||||
Destroy(this.dynamicContent); |
||||
|
||||
var example = Instantiate<ExampleInfo>(this._exampleInfoPrefab, this._dyncamicContentRoot, false); |
||||
example.Setup(this, item.ExamplePrefab); |
||||
this.dynamicContent = example.gameObject; |
||||
} |
||||
|
||||
public void ExecuteExample(SampleBase example) |
||||
{ |
||||
if (this.dynamicContent != null) |
||||
Destroy(this.dynamicContent); |
||||
this.dynamicContent = Instantiate(example, this._dyncamicContentRoot, false).gameObject; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: bd639b2a784de314984c606b7e8329e4 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,26 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.Helpers |
||||
{ |
||||
public class TextListItem : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649 |
||||
[SerializeField] |
||||
private Text _text; |
||||
#pragma warning restore |
||||
|
||||
public void SetText(string text) |
||||
{ |
||||
this._text.text = text; |
||||
} |
||||
|
||||
public void AddLeftPadding(int padding) |
||||
{ |
||||
this.GetComponent<LayoutGroup>().padding.left += padding; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 05975660b0231b84f849693106b207d1 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 255df445982551d4cbbcf8238aee20f7 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,57 @@
|
||||
#if CSHARP_7_OR_LATER |
||||
using System; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
|
||||
namespace BestHTTP |
||||
{ |
||||
public static class AsyncExtensions |
||||
{ |
||||
public static Task<T> GetFromJsonResultAsync<T>(this HTTPRequest request, CancellationToken token = default) |
||||
{ |
||||
return HTTPRequestAsyncExtensions.CreateTask<T>(request, token, (req, resp, tcs) => |
||||
{ |
||||
switch (req.State) |
||||
{ |
||||
// The request finished without any problem. |
||||
case HTTPRequestStates.Finished: |
||||
if (resp.IsSuccess) |
||||
tcs.TrySetResult(BestHTTP.JSON.LitJson.JsonMapper.ToObject<T>(resp.DataAsText)); |
||||
else |
||||
tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("Request finished Successfully, but the server sent an error.", resp)); |
||||
break; |
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error. |
||||
case HTTPRequestStates.Error: |
||||
HTTPRequestAsyncExtensions.VerboseLogging(request, "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception")); |
||||
|
||||
tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("No Exception", null, req.Exception)); |
||||
break; |
||||
|
||||
// The request aborted, initiated by the user. |
||||
case HTTPRequestStates.Aborted: |
||||
HTTPRequestAsyncExtensions.VerboseLogging(request, "Request Aborted!"); |
||||
|
||||
tcs.TrySetCanceled(); |
||||
break; |
||||
|
||||
// Connecting to the server is timed out. |
||||
case HTTPRequestStates.ConnectionTimedOut: |
||||
HTTPRequestAsyncExtensions.VerboseLogging(request, "Connection Timed Out!"); |
||||
|
||||
tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("Connection Timed Out!")); |
||||
break; |
||||
|
||||
// The request didn't finished in the given time. |
||||
case HTTPRequestStates.TimedOut: |
||||
HTTPRequestAsyncExtensions.VerboseLogging(request, "Processing the request Timed Out!"); |
||||
|
||||
tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("Processing the request Timed Out!")); |
||||
break; |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 7f0aefe97b2a43844850d2d8b234e819 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,103 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
using BestHTTP.Examples.Helpers; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
public class SampleRoot : MonoBehaviour |
||||
{ |
||||
#pragma warning disable 0649, 0169 |
||||
[Header("Common Properties")] |
||||
public string BaseURL = "https://besthttpwebgldemo.azurewebsites.net"; |
||||
|
||||
[Header("References")] |
||||
|
||||
[SerializeField] |
||||
private Text _pluginVersion; |
||||
|
||||
[SerializeField] |
||||
private Dropdown _logLevelDropdown; |
||||
|
||||
[SerializeField] |
||||
private Text _proxyLabel; |
||||
|
||||
[SerializeField] |
||||
private InputField _proxyInputField; |
||||
|
||||
#pragma warning restore |
||||
|
||||
[SerializeField] |
||||
public List<SampleBase> samples = new List<SampleBase>(); |
||||
|
||||
[HideInInspector] |
||||
public SampleBase selectedExamplePrefab; |
||||
|
||||
private void Start() |
||||
{ |
||||
Application.runInBackground = true; |
||||
|
||||
this._pluginVersion.text = "Version: " + HTTPManager.UserAgent; |
||||
|
||||
int logLevel = PlayerPrefs.GetInt("BestHTTP.HTTPManager.Logger.Level", (int)HTTPManager.Logger.Level); |
||||
this._logLevelDropdown.value = logLevel; |
||||
HTTPManager.Logger.Level = (BestHTTP.Logger.Loglevels)logLevel; |
||||
|
||||
#if (UNITY_WEBGL && !UNITY_EDITOR) || BESTHTTP_DISABLE_PROXY |
||||
this._proxyLabel.gameObject.SetActive(false); |
||||
this._proxyInputField.gameObject.SetActive(false); |
||||
#else |
||||
string proxyURL = PlayerPrefs.GetString("BestHTTP.HTTPManager.Proxy", null); |
||||
if (!string.IsNullOrEmpty(proxyURL)) |
||||
{ |
||||
try |
||||
{ |
||||
HTTPManager.Proxy = new HTTPProxy(new Uri(proxyURL), null, true); |
||||
#if UNITY_2019_1_OR_NEWER |
||||
this._proxyInputField.SetTextWithoutNotify(proxyURL); |
||||
#else |
||||
this._proxyInputField.onEndEdit.RemoveAllListeners(); |
||||
this._proxyInputField.text = proxyURL; |
||||
this._proxyInputField.onEndEdit.AddListener(this.OnProxyEditEnd); |
||||
#endif |
||||
} |
||||
catch |
||||
{ } |
||||
} |
||||
else |
||||
HTTPManager.Proxy = null; |
||||
#endif |
||||
|
||||
#if !BESTHTTP_DISABLE_CACHING |
||||
// Remove too old cache entries. |
||||
BestHTTP.Caching.HTTPCacheService.BeginMaintainence(new BestHTTP.Caching.HTTPCacheMaintananceParams(TimeSpan.FromDays(30), ulong.MaxValue)); |
||||
#endif |
||||
} |
||||
|
||||
public void OnLogLevelChanged(int idx) |
||||
{ |
||||
HTTPManager.Logger.Level = (BestHTTP.Logger.Loglevels)idx; |
||||
PlayerPrefs.SetInt("BestHTTP.HTTPManager.Logger.Level", idx); |
||||
} |
||||
|
||||
public void OnProxyEditEnd(string proxyURL) |
||||
{ |
||||
#if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_PROXY |
||||
try |
||||
{ |
||||
if (string.IsNullOrEmpty(this._proxyInputField.text)) |
||||
HTTPManager.Proxy = null; |
||||
else |
||||
HTTPManager.Proxy = new HTTPProxy(new Uri(this._proxyInputField.text), null, true); |
||||
|
||||
PlayerPrefs.SetString("BestHTTP.HTTPManager.Proxy", this._proxyInputField.text); |
||||
} |
||||
catch |
||||
{ } |
||||
#endif |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 0eb115c0377f90041a9ecfde21658f92 |
||||
timeCreated: 1571213708 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: b86f7bd69606df2419df60d808328c1b |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,156 @@
|
||||
#if !BESTHTTP_DISABLE_SERVERSENT_EVENTS |
||||
|
||||
using System; |
||||
using BestHTTP.Examples.Helpers; |
||||
using BestHTTP.ServerSentEvents; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples.ServerSentEvents |
||||
{ |
||||
public class SimpleSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[Tooltip("The url of the resource to use.")] |
||||
[SerializeField] |
||||
private string _path = "/sse"; |
||||
|
||||
[SerializeField] |
||||
private ScrollRect _scrollRect; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _contentRoot; |
||||
|
||||
[SerializeField] |
||||
private TextListItem _listItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private int _maxListItemEntries = 100; |
||||
|
||||
[SerializeField] |
||||
private Button _startButton; |
||||
|
||||
[SerializeField] |
||||
private Button _closeButton; |
||||
|
||||
#pragma warning restore |
||||
|
||||
private EventSource eventSource; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
if (this.eventSource != null) |
||||
{ |
||||
this.eventSource.Close(); |
||||
this.eventSource = null; |
||||
} |
||||
} |
||||
|
||||
public void OnStartButton() |
||||
{ |
||||
GUIHelper.RemoveChildren(this._contentRoot, 0); |
||||
|
||||
// Create the EventSource instance |
||||
this.eventSource = new EventSource(new Uri(base.sampleSelector.BaseURL + this._path)); |
||||
|
||||
// Subscribe to generic events |
||||
this.eventSource.OnOpen += OnOpen; |
||||
this.eventSource.OnClosed += OnClosed; |
||||
this.eventSource.OnError += OnError; |
||||
this.eventSource.OnStateChanged += this.OnStateChanged; |
||||
this.eventSource.OnMessage += OnMessage; |
||||
|
||||
// Subscribe to an application specific event |
||||
this.eventSource.On("datetime", OnDateTime); |
||||
|
||||
// Start to connect to the server |
||||
this.eventSource.Open(); |
||||
|
||||
AddText("Opening Server-Sent Events..."); |
||||
|
||||
SetButtons(false, true); |
||||
} |
||||
|
||||
public void OnCloseButton() |
||||
{ |
||||
SetButtons(false, false); |
||||
this.eventSource.Close(); |
||||
} |
||||
|
||||
private void OnOpen(EventSource eventSource) |
||||
{ |
||||
AddText("Open"); |
||||
} |
||||
|
||||
private void OnClosed(EventSource eventSource) |
||||
{ |
||||
AddText("Closed"); |
||||
|
||||
this.eventSource = null; |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
private void OnError(EventSource eventSource, string error) |
||||
{ |
||||
AddText(string.Format("Error: <color=red>{0}</color>", error)); |
||||
} |
||||
|
||||
private void OnStateChanged(EventSource eventSource, States oldState, States newState) |
||||
{ |
||||
AddText(string.Format("State Changed {0} => {1}", oldState, newState)); |
||||
} |
||||
|
||||
private void OnMessage(EventSource eventSource, Message message) |
||||
{ |
||||
AddText(string.Format("Message: <color=yellow>{0}</color>", message)); |
||||
} |
||||
|
||||
private void OnDateTime(EventSource eventSource, Message message) |
||||
{ |
||||
DateTimeData dtData = BestHTTP.JSON.LitJson.JsonMapper.ToObject<DateTimeData>(message.Data); |
||||
|
||||
AddText(string.Format("OnDateTime: <color=yellow>{0}</color>", dtData.ToString())); |
||||
} |
||||
|
||||
private void SetButtons(bool start, bool close) |
||||
{ |
||||
if (this._startButton != null) |
||||
this._startButton.interactable = start; |
||||
|
||||
if (this._closeButton != null) |
||||
this._closeButton.interactable = close; |
||||
} |
||||
|
||||
private void AddText(string text) |
||||
{ |
||||
GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); |
||||
} |
||||
} |
||||
|
||||
[PlatformSupport.IL2CPP.Preserve] |
||||
sealed class DateTimeData |
||||
{ |
||||
#pragma warning disable 0649 |
||||
[PlatformSupport.IL2CPP.Preserve] |
||||
public int eventid; |
||||
|
||||
[PlatformSupport.IL2CPP.Preserve] |
||||
public string datetime; |
||||
#pragma warning restore |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("[DateTimeData EventId: {0}, DateTime: {1}]", this.eventid, this.datetime); |
||||
} |
||||
} |
||||
} |
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 730e176dfa30cb24c93efcaabb4f8688 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 56e6fe2f6b3c19848aec5ffe33186e61 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 8894a3d3eb906a64a8173b7f1016fed5 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,135 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR |
||||
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR) |
||||
|
||||
using System; |
||||
|
||||
using BestHTTP.Cookies; |
||||
using BestHTTP.SignalR.Transports; |
||||
|
||||
namespace BestHTTP.SignalR.Authentication |
||||
{ |
||||
public sealed class SampleCookieAuthentication : IAuthenticationProvider |
||||
{ |
||||
#region Public Properties |
||||
|
||||
public Uri AuthUri { get; private set; } |
||||
public string UserName { get; private set; } |
||||
public string Password { get; private set; } |
||||
public string UserRoles { get; private set; } |
||||
|
||||
#endregion |
||||
|
||||
#region IAuthenticationProvider properties |
||||
|
||||
public bool IsPreAuthRequired { get; private set; } |
||||
|
||||
public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; |
||||
public event OnAuthenticationFailedDelegate OnAuthenticationFailed; |
||||
|
||||
#endregion |
||||
|
||||
#region Privates |
||||
|
||||
private HTTPRequest AuthRequest; |
||||
private Cookie Cookie; |
||||
|
||||
#endregion |
||||
|
||||
public SampleCookieAuthentication(Uri authUri, string user, string passwd, string roles) |
||||
{ |
||||
this.AuthUri = authUri; |
||||
this.UserName = user; |
||||
this.Password = passwd; |
||||
this.UserRoles = roles; |
||||
this.IsPreAuthRequired = true; |
||||
} |
||||
|
||||
#region IAuthenticationProvider Implementation |
||||
|
||||
public void StartAuthentication() |
||||
{ |
||||
AuthRequest = new HTTPRequest(AuthUri, HTTPMethods.Post, OnAuthRequestFinished); |
||||
|
||||
// Setup the form |
||||
AuthRequest.AddField("userName", UserName); |
||||
AuthRequest.AddField("Password", Password); // not used in the sample |
||||
AuthRequest.AddField("roles", UserRoles); |
||||
|
||||
AuthRequest.Send(); |
||||
} |
||||
|
||||
public void PrepareRequest(HTTPRequest request, RequestTypes type) |
||||
{ |
||||
// Adding the cookie to the request is not required, as it's managed by the plugin automatically, |
||||
// but for now, we want to be really sure that it's added |
||||
request.Cookies.Add(Cookie); |
||||
} |
||||
|
||||
#endregion |
||||
|
||||
#region Request Handler |
||||
|
||||
void OnAuthRequestFinished(HTTPRequest req, HTTPResponse resp) |
||||
{ |
||||
AuthRequest = null; |
||||
string failReason = string.Empty; |
||||
|
||||
switch (req.State) |
||||
{ |
||||
// The request finished without any problem. |
||||
case HTTPRequestStates.Finished: |
||||
if (resp.IsSuccess) |
||||
{ |
||||
Cookie = resp.Cookies != null ? resp.Cookies.Find(c => c.Name.Equals(".ASPXAUTH")) : null; |
||||
|
||||
if (Cookie != null) |
||||
{ |
||||
HTTPManager.Logger.Information("CookieAuthentication", "Auth. Cookie found!"); |
||||
|
||||
if (OnAuthenticationSucceded != null) |
||||
OnAuthenticationSucceded(this); |
||||
|
||||
// return now, all other paths are authentication failures |
||||
return; |
||||
} |
||||
else |
||||
HTTPManager.Logger.Warning("CookieAuthentication", failReason = "Auth. Cookie NOT found!"); |
||||
} |
||||
else |
||||
HTTPManager.Logger.Warning("CookieAuthentication", failReason = string.Format("Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", |
||||
resp.StatusCode, |
||||
resp.Message, |
||||
resp.DataAsText)); |
||||
break; |
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error. |
||||
case HTTPRequestStates.Error: |
||||
HTTPManager.Logger.Warning("CookieAuthentication", failReason = "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception")); |
||||
break; |
||||
|
||||
// The request aborted, initiated by the user. |
||||
case HTTPRequestStates.Aborted: |
||||
HTTPManager.Logger.Warning("CookieAuthentication", failReason = "Request Aborted!"); |
||||
break; |
||||
|
||||
// Connecting to the server is timed out. |
||||
case HTTPRequestStates.ConnectionTimedOut: |
||||
HTTPManager.Logger.Error("CookieAuthentication", failReason = "Connection Timed Out!"); |
||||
break; |
||||
|
||||
// The request didn't finished in the given time. |
||||
case HTTPRequestStates.TimedOut: |
||||
HTTPManager.Logger.Error("CookieAuthentication", failReason = "Processing the request Timed Out!"); |
||||
break; |
||||
} |
||||
|
||||
if (OnAuthenticationFailed != null) |
||||
OnAuthenticationFailed(this, failReason); |
||||
} |
||||
|
||||
#endregion |
||||
} |
||||
} |
||||
|
||||
#endif |
||||
#endif |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 48a74a50eeb07bb4ea649a902e9d487a |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,97 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR |
||||
|
||||
namespace BestHTTP.SignalR.Authentication |
||||
{ |
||||
/// <summary> |
||||
/// Custom http-header based authenticator. |
||||
/// <example> |
||||
/// <code> |
||||
/// // Server side implementation of the Header-based authenticator |
||||
/// // Use it by adding the app.Use(typeof(HeaderBasedAuthenticationMiddleware)); line to the Startup class' Configuration function. |
||||
/// private class HeaderBasedAuthenticationMiddleware : OwinMiddleware |
||||
/// { |
||||
/// public HeaderBasedAuthenticationMiddleware(OwinMiddleware next) |
||||
/// : base(next) |
||||
/// { |
||||
/// } |
||||
/// |
||||
/// public override Task Invoke(IOwinContext context) |
||||
/// { |
||||
/// string username = context.Request.Headers.Get("username"); |
||||
/// string roles = context.Request.Headers.Get("roles"); |
||||
/// |
||||
/// if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(roles)) |
||||
/// { |
||||
/// var identity = new System.Security.Principal.GenericIdentity(username); |
||||
/// |
||||
/// var principal = new System.Security.Principal.GenericPrincipal(identity, SplitString(roles)); |
||||
/// |
||||
/// context.Request.User = principal; |
||||
/// } |
||||
/// |
||||
/// return Next.Invoke(context); |
||||
/// } |
||||
/// |
||||
/// private static string[] SplitString(string original) |
||||
/// { |
||||
/// if (String.IsNullOrEmpty(original)) |
||||
/// return new string[0]; |
||||
/// |
||||
/// var split = from piece in original.Split(',') let trimmed = piece.Trim() where !String.IsNullOrEmpty(trimmed) select trimmed; |
||||
/// |
||||
/// return split.ToArray(); |
||||
/// } |
||||
/// } |
||||
/// </code> |
||||
/// </example> |
||||
/// </summary> |
||||
class HeaderAuthenticator : IAuthenticationProvider |
||||
{ |
||||
public string User { get; private set; } |
||||
public string Roles { get; private set; } |
||||
|
||||
/// <summary> |
||||
/// No pre-auth step required for this type of authentication |
||||
/// </summary> |
||||
public bool IsPreAuthRequired { get { return false; } } |
||||
|
||||
#pragma warning disable 0067 |
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; |
||||
|
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationFailedDelegate OnAuthenticationFailed; |
||||
|
||||
#pragma warning restore 0067 |
||||
|
||||
/// <summary> |
||||
/// Constructor to initialise the authenticator with username and roles. |
||||
/// </summary> |
||||
public HeaderAuthenticator(string user, string roles) |
||||
{ |
||||
this.User = user; |
||||
this.Roles = roles; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Not used as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public void StartAuthentication() |
||||
{ } |
||||
|
||||
/// <summary> |
||||
/// Prepares the request by adding two headers to it |
||||
/// </summary> |
||||
public void PrepareRequest(BestHTTP.HTTPRequest request, RequestTypes type) |
||||
{ |
||||
request.SetHeader("username", this.User); |
||||
request.SetHeader("roles", this.Roles); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 5ebd4ce02d369a6498f9be6bb7141ac3 |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 1a071702f6b0d014890a15a301be0af9 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,23 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR && BESTHTTP_SIGNALR_WITH_JSONDOTNET |
||||
|
||||
using System.Collections.Generic; |
||||
|
||||
using Newtonsoft.Json; |
||||
|
||||
namespace BestHTTP.SignalR.JsonEncoders |
||||
{ |
||||
public sealed class JSonDotnetEncoder : IJsonEncoder |
||||
{ |
||||
public string Encode(object obj) |
||||
{ |
||||
return JsonConvert.SerializeObject(obj); |
||||
} |
||||
|
||||
public IDictionary<string, object> DecodeMessage(string json) |
||||
{ |
||||
return JsonConvert.DeserializeObject<Dictionary<string, object>>(json); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 653a0f163689052438748b7beda14886 |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,28 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR |
||||
|
||||
using System.Collections.Generic; |
||||
|
||||
using BestHTTP.JSON.LitJson; |
||||
|
||||
namespace BestHTTP.SignalR.JsonEncoders |
||||
{ |
||||
public sealed class LitJsonEncoder : IJsonEncoder |
||||
{ |
||||
public string Encode(object obj) |
||||
{ |
||||
JsonWriter writer = new JsonWriter(); |
||||
JsonMapper.ToJson(obj, writer); |
||||
|
||||
return writer.ToString(); |
||||
} |
||||
|
||||
public IDictionary<string, object> DecodeMessage(string json) |
||||
{ |
||||
JsonReader reader = new JsonReader(json); |
||||
|
||||
return JsonMapper.ToObject<Dictionary<string, object>>(reader); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 2870a746a601b8b439c495ff39474385 |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 22c3e67505eddc1498b5cc3253d3ee1c |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,240 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using System; |
||||
using UnityEngine; |
||||
using BestHTTP.SignalRCore; |
||||
using BestHTTP.SignalRCore.Encoders; |
||||
using UnityEngine.UI; |
||||
using BestHTTP.Examples.Helpers; |
||||
#if CSHARP_7_OR_LATER |
||||
using System.Threading.Tasks; |
||||
#endif |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
// Server side of this example can be found here: |
||||
// https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/TestHub.cs |
||||
public class AsyncTestHubSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
#pragma warning disable 0414 |
||||
[SerializeField] |
||||
private string _path = "/TestHub"; |
||||
|
||||
[SerializeField] |
||||
private ScrollRect _scrollRect; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _contentRoot; |
||||
|
||||
[SerializeField] |
||||
private TextListItem _listItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private int _maxListItemEntries = 100; |
||||
|
||||
[SerializeField] |
||||
private Button _connectButton; |
||||
|
||||
[SerializeField] |
||||
private Button _closeButton; |
||||
|
||||
#pragma warning restore |
||||
|
||||
// Instance of the HubConnection |
||||
HubConnection hub; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
#if !CSHARP_7_OR_LATER |
||||
AddText("<color=red>This sample can work only when at least c# 7.3 is supported!</color>"); |
||||
SetButtons(false, false); |
||||
#else |
||||
SetButtons(true, false); |
||||
#endif |
||||
} |
||||
|
||||
#if CSHARP_7_OR_LATER |
||||
async void OnDestroy() |
||||
{ |
||||
await hub?.CloseAsync(); |
||||
} |
||||
#endif |
||||
|
||||
/// <summary> |
||||
/// GUI button callback |
||||
/// </summary> |
||||
public |
||||
#if CSHARP_7_OR_LATER |
||||
async |
||||
#endif |
||||
void OnConnectButton() |
||||
{ |
||||
#if CSHARP_7_OR_LATER |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
try |
||||
{ |
||||
MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( |
||||
MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, |
||||
MessagePack.Unity.UnityResolver.Instance, |
||||
//MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, |
||||
//MessagePack.Resolvers.StandardResolver.Instance, |
||||
MessagePack.Resolvers.ContractlessStandardResolver.Instance |
||||
); |
||||
|
||||
var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); |
||||
MessagePack.MessagePackSerializer.DefaultOptions = options; |
||||
} |
||||
catch |
||||
{ } |
||||
#endif |
||||
|
||||
IProtocol protocol = null; |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
protocol = new MessagePackCSharpProtocol(); |
||||
#elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK |
||||
protocol = new MessagePackProtocol(); |
||||
#else |
||||
protocol = new JsonProtocol(new LitJsonEncoder()); |
||||
#endif |
||||
// Crete the HubConnection |
||||
hub = new HubConnection(new Uri(this.sampleSelector.BaseURL + this._path), protocol); |
||||
|
||||
// Subscribe to hub events |
||||
hub.OnError += Hub_OnError; |
||||
|
||||
hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport(<color=green>{0}</color>) event: <color=green>{1}</color>", transport.TransportType, ev)); |
||||
|
||||
// Set up server callable functions |
||||
hub.On("Send", (string arg) => AddText(string.Format("On '<color=green>Send</color>': '<color=yellow>{0}</color>'", arg)).AddLeftPadding(20)); |
||||
hub.On<Person>("Person", (person) => AddText(string.Format("On '<color=green>Person</color>': '<color=yellow>{0}</color>'", person)).AddLeftPadding(20)); |
||||
hub.On<Person, Person>("TwoPersons", (person1, person2) => AddText(string.Format("On '<color=green>TwoPersons</color>': '<color=yellow>{0}</color>', '<color=yellow>{1}</color>'", person1, person2)).AddLeftPadding(20)); |
||||
|
||||
AddText("StartConnect called"); |
||||
|
||||
SetButtons(false, false); |
||||
|
||||
// And finally start to connect to the server |
||||
await hub.ConnectAsync(); |
||||
|
||||
SetButtons(false, true); |
||||
AddText(string.Format("Hub Connected with <color=green>{0}</color> transport using the <color=green>{1}</color> encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); |
||||
|
||||
// Call a server function with a string param. We expect no return value. |
||||
await hub.SendAsync("Send", "my message"); |
||||
|
||||
// Call a parameterless function. We expect a string return value. |
||||
try |
||||
{ |
||||
string result = await hub.InvokeAsync<string>("NoParam"); |
||||
|
||||
AddText(string.Format("'<color=green>NoParam</color>' returned: '<color=yellow>{0}</color>'", result)) |
||||
.AddLeftPadding(20); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
AddText(string.Format("'<color=green>NoParam</color>' error: '<color=red>{0}</color>'", ex.Message)).AddLeftPadding(20); |
||||
} |
||||
|
||||
// Call a function on the server to add two numbers. OnSuccess will be called with the result and OnError if there's an error. |
||||
var addResult = await hub.InvokeAsync<int>("Add", 10, 20); |
||||
AddText(string.Format("'<color=green>Add(10, 20)</color>' returned: '<color=yellow>{0}</color>'", addResult)).AddLeftPadding(20); |
||||
|
||||
var nullabelTestResult = await hub.InvokeAsync<int?>("NullableTest", 10); |
||||
AddText(string.Format("'<color=green>NullableTest(10)</color>' returned: '<color=yellow>{0}</color>'", nullabelTestResult)).AddLeftPadding(20); |
||||
|
||||
// Call a function that will return a Person object constructed from the function's parameters. |
||||
var getPersonResult = await hub.InvokeAsync<Person>("GetPerson", "Mr. Smith", 26); |
||||
AddText(string.Format("'<color=green>GetPerson(\"Mr. Smith\", 26)</color>' returned: '<color=yellow>{0}</color>'", getPersonResult)).AddLeftPadding(20); |
||||
|
||||
// To test errors/exceptions this call always throws an exception on the server side resulting in an OnError call. |
||||
// OnError expected here! |
||||
|
||||
try |
||||
{ |
||||
var singleResultFailureResult = await hub.InvokeAsync<int>("SingleResultFailure", 10, 20); |
||||
AddText(string.Format("'<color=green>SingleResultFailure(10, 20)</color>' returned: '<color=yellow>{0}</color>'", singleResultFailureResult)).AddLeftPadding(20); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
AddText(string.Format("'<color=green>SingleResultFailure(10, 20)</color>' error: '<color=red>{0}</color>'", ex.Message)).AddLeftPadding(20); |
||||
} |
||||
|
||||
// This call demonstrates IEnumerable<> functions, result will be the yielded numbers. |
||||
var batchedResult = await hub.InvokeAsync<int[]>("Batched", 10); |
||||
AddText(string.Format("'<color=green>Batched(10)</color>' returned items: '<color=yellow>{0}</color>'", batchedResult.Length)).AddLeftPadding(20); |
||||
|
||||
// OnItem is called for a streaming request for every items returned by the server. OnSuccess will still be called with all the items. |
||||
hub.GetDownStreamController<int>("ObservableCounter", 10, 1000) |
||||
.OnItem(result => AddText(string.Format("'<color=green>ObservableCounter(10, 1000)</color>' OnItem: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnSuccess(result => AddText("'<color=green>ObservableCounter(10, 1000)</color>' OnSuccess.").AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>ObservableCounter(10, 1000)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// A stream request can be cancelled any time. |
||||
var controller = hub.GetDownStreamController<int>("ChannelCounter", 10, 1000); |
||||
|
||||
controller.OnItem(result => AddText(string.Format("'<color=green>ChannelCounter(10, 1000)</color>' OnItem: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnSuccess(result => AddText("'<color=green>ChannelCounter(10, 1000)</color>' OnSuccess.").AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>ChannelCounter(10, 1000)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// a stream can be cancelled by calling the controller's Cancel method |
||||
controller.Cancel(); |
||||
|
||||
// This call will stream strongly typed objects |
||||
hub.GetDownStreamController<Person>("GetRandomPersons", 20, 2000) |
||||
.OnItem(result => AddText(string.Format("'<color=green>GetRandomPersons(20, 1000)</color>' OnItem: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnSuccess(result => AddText("'<color=green>GetRandomPersons(20, 1000)</color>' OnSuccess.").AddLeftPadding(20)); |
||||
#endif |
||||
} |
||||
|
||||
/// <summary> |
||||
/// GUI button callback |
||||
/// </summary> |
||||
public |
||||
#if CSHARP_7_OR_LATER |
||||
async |
||||
#endif |
||||
void OnCloseButton() |
||||
{ |
||||
#if CSHARP_7_OR_LATER |
||||
if (this.hub != null) |
||||
{ |
||||
AddText("Calling CloseAsync"); |
||||
SetButtons(false, false); |
||||
|
||||
await this.hub.CloseAsync(); |
||||
|
||||
SetButtons(true, false); |
||||
AddText("Hub Closed"); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. |
||||
/// </summary> |
||||
private void Hub_OnError(HubConnection hub, string error) |
||||
{ |
||||
SetButtons(true, false); |
||||
AddText(string.Format("Hub Error: <color=red>{0}</color>", error)); |
||||
} |
||||
|
||||
private void SetButtons(bool connect, bool close) |
||||
{ |
||||
if (this._connectButton != null) |
||||
this._connectButton.interactable = connect; |
||||
|
||||
if (this._closeButton != null) |
||||
this._closeButton.interactable = close; |
||||
} |
||||
|
||||
private TextListItem AddText(string text) |
||||
{ |
||||
return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2 |
||||
guid: e516808e7e284014c8b7afb3ff270943 |
||||
timeCreated: 1577715218 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: d8c1ea4e11d6be6488ad882939d63018 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,68 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using System; |
||||
|
||||
namespace BestHTTP.SignalRCore.Authentication |
||||
{ |
||||
public sealed class HeaderAuthenticator : IAuthenticationProvider |
||||
{ |
||||
/// <summary> |
||||
/// No pre-auth step required for this type of authentication |
||||
/// </summary> |
||||
public bool IsPreAuthRequired { get { return false; } } |
||||
|
||||
#pragma warning disable 0067 |
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; |
||||
|
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationFailedDelegate OnAuthenticationFailed; |
||||
|
||||
#pragma warning restore 0067 |
||||
|
||||
private string _credentials; |
||||
|
||||
public HeaderAuthenticator(string credentials) |
||||
{ |
||||
this._credentials = credentials; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Not used as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public void StartAuthentication() |
||||
{ } |
||||
|
||||
/// <summary> |
||||
/// Prepares the request by adding two headers to it |
||||
/// </summary> |
||||
public void PrepareRequest(BestHTTP.HTTPRequest request) |
||||
{ |
||||
#if !UNITY_WEBGL || UNITY_EDITOR |
||||
request.SetHeader("Authorization", "Bearer " + this._credentials); |
||||
#endif |
||||
} |
||||
|
||||
public Uri PrepareUri(Uri uri) |
||||
{ |
||||
#if UNITY_WEBGL && !UNITY_EDITOR |
||||
string query = string.IsNullOrEmpty(uri.Query) ? "?" : uri.Query + "&"; |
||||
UriBuilder uriBuilder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, query + "access_token=" + this._credentials); |
||||
return uriBuilder.Uri; |
||||
#else |
||||
return uri; |
||||
#endif |
||||
} |
||||
|
||||
public void Cancel() |
||||
{ |
||||
|
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 80b2246c164414c468eea4f0550eb9ad |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: d7347b3e5f014ce4b8ac59f9ac94f7c8 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,37 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE && BESTHTTP_SIGNALR_CORE_ENABLE_NEWTONSOFT_JSON_DOTNET_ENCODER |
||||
using System; |
||||
using BestHTTP.PlatformSupport.Memory; |
||||
|
||||
namespace BestHTTP.SignalRCore.Encoders |
||||
{ |
||||
public sealed class JsonDotNetEncoder : BestHTTP.SignalRCore.IEncoder |
||||
{ |
||||
public object ConvertTo(Type toType, object obj) |
||||
{ |
||||
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj); |
||||
|
||||
return Newtonsoft.Json.JsonConvert.DeserializeObject(json, toType); |
||||
} |
||||
|
||||
public T DecodeAs<T>(BufferSegment buffer) |
||||
{ |
||||
using (var reader = new System.IO.StreamReader(new System.IO.MemoryStream(buffer.Data, buffer.Offset, buffer.Count))) |
||||
using (var jsonReader = new Newtonsoft.Json.JsonTextReader(reader)) |
||||
return new Newtonsoft.Json.JsonSerializer().Deserialize<T>(jsonReader); |
||||
} |
||||
|
||||
public BufferSegment Encode<T>(T value) |
||||
{ |
||||
var json = Newtonsoft.Json.JsonConvert.SerializeObject(value); |
||||
|
||||
int len = System.Text.Encoding.UTF8.GetByteCount(json); |
||||
byte[] buffer = BufferPool.Get(len + 1, true); |
||||
System.Text.Encoding.UTF8.GetBytes(json, 0, json.Length, buffer, 0); |
||||
|
||||
buffer[len] = 0x1e; |
||||
|
||||
return new BufferSegment(buffer, 0, len + 1); |
||||
} |
||||
} |
||||
} |
||||
#endif |
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 5e565b09f1e97ee4c9d119735901397b |
||||
timeCreated: 1515401618 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,47 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
using System; |
||||
using BestHTTP.PlatformSupport.Memory; |
||||
using BestHTTP.JSON.LitJson; |
||||
|
||||
namespace BestHTTP.SignalRCore.Encoders |
||||
{ |
||||
public sealed class LitJsonEncoder : BestHTTP.SignalRCore.IEncoder |
||||
{ |
||||
public LitJsonEncoder() |
||||
{ |
||||
BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter<int, long>((input) => input); |
||||
BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter<long, int>((input) => (int)input); |
||||
BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter<double, int>((input) => (int)(input + 0.5)); |
||||
BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter<string, DateTime>((input) => Convert.ToDateTime((string)input).ToUniversalTime()); |
||||
BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter<double, float>((input) => (float)input); |
||||
BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter<string, byte[]>((input) => Convert.FromBase64String(input)); |
||||
BestHTTP.JSON.LitJson.JsonMapper.RegisterExporter<float>((f, writer) => writer.Write((double)f)); |
||||
} |
||||
|
||||
public T DecodeAs<T>(BufferSegment buffer) |
||||
{ |
||||
using (var reader = new System.IO.StreamReader(new System.IO.MemoryStream(buffer.Data, buffer.Offset, buffer.Count))) |
||||
{ |
||||
return JsonMapper.ToObject<T>(reader); |
||||
} |
||||
} |
||||
|
||||
public PlatformSupport.Memory.BufferSegment Encode<T>(T value) |
||||
{ |
||||
var json = JsonMapper.ToJson(value); |
||||
int len = System.Text.Encoding.UTF8.GetByteCount(json); |
||||
byte[] buffer = BufferPool.Get(len + 1, true); |
||||
System.Text.Encoding.UTF8.GetBytes(json, 0, json.Length, buffer, 0); |
||||
buffer[len] = (byte)JsonProtocol.Separator; |
||||
return new BufferSegment(buffer, 0, len + 1); |
||||
} |
||||
|
||||
public object ConvertTo(Type toType, object obj) |
||||
{ |
||||
string json = BestHTTP.JSON.LitJson.JsonMapper.ToJson(obj); |
||||
return BestHTTP.JSON.LitJson.JsonMapper.ToObject(toType, json); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2 |
||||
guid: b5784b64da7c8ad47b441a9f2f27a33b |
||||
timeCreated: 1515401618 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,567 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE && BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
using System; |
||||
using System.Buffers; |
||||
using System.Collections.Generic; |
||||
|
||||
using BestHTTP.Extensions; |
||||
|
||||
using BestHTTP.PlatformSupport.Memory; |
||||
using BestHTTP.SignalRCore.Messages; |
||||
|
||||
using MessagePack; |
||||
|
||||
namespace BestHTTP.SignalRCore.Encoders |
||||
{ |
||||
class BufferPoolBufferWriter : IBufferWriter<byte> |
||||
{ |
||||
private BufferPoolMemoryStream underlyingStream; |
||||
private BufferSegment last; |
||||
|
||||
public BufferPoolBufferWriter(BufferPoolMemoryStream stream) |
||||
{ |
||||
this.underlyingStream = stream; |
||||
this.last = BufferSegment.Empty; |
||||
} |
||||
|
||||
public void Advance(int count) |
||||
{ |
||||
this.underlyingStream.Write(this.last.Data, this.last.Offset, this.last.Count + count); |
||||
BufferPool.Release(this.last); |
||||
this.last = BufferSegment.Empty; |
||||
} |
||||
|
||||
public Memory<byte> GetMemory(int sizeHint = 0) |
||||
{ |
||||
var buffer = BufferPool.Get(Math.Max(sizeHint, BufferPool.MinBufferSize), true); |
||||
//Array.Clear(buffer, 0, buffer.Length); |
||||
|
||||
this.last = new BufferSegment(buffer, 0, 0); |
||||
return new Memory<byte>(buffer, 0, buffer.Length); |
||||
} |
||||
|
||||
public Span<byte> GetSpan(int sizeHint = 0) |
||||
{ |
||||
var buffer = BufferPool.Get(Math.Max(sizeHint, BufferPool.MinBufferSize), true); |
||||
//Array.Clear(buffer, 0, buffer.Length); |
||||
|
||||
this.last = new BufferSegment(buffer, 0, 0); |
||||
|
||||
return new Span<byte>(buffer, 0, buffer.Length); |
||||
} |
||||
} |
||||
|
||||
public sealed class MessagePackCSharpProtocol : BestHTTP.SignalRCore.IProtocol |
||||
{ |
||||
public string Name { get { return "messagepack"; } } |
||||
public TransferModes Type { get { return TransferModes.Binary; } } |
||||
public IEncoder Encoder { get; private set; } |
||||
public HubConnection Connection { get; set; } |
||||
|
||||
public BufferSegment EncodeMessage(Message message) |
||||
{ |
||||
var memBuffer = BufferPool.Get(256, true); |
||||
var stream = new BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true); |
||||
|
||||
// Write 5 bytes for placeholder for length prefix |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
|
||||
var bufferWriter = new BufferPoolBufferWriter(stream); |
||||
var writer = new MessagePackWriter(bufferWriter); |
||||
|
||||
switch (message.type) |
||||
{ |
||||
case MessageTypes.StreamItem: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1 |
||||
// [2, Headers, InvocationId, Item] |
||||
|
||||
writer.WriteArrayHeader(4); |
||||
|
||||
writer.Write(2); |
||||
WriteHeaders(ref writer); |
||||
WriteString(ref writer, message.invocationId); |
||||
WriteValue(ref writer, bufferWriter, message.item); |
||||
|
||||
break; |
||||
|
||||
case MessageTypes.Completion: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1 |
||||
// [3, Headers, InvocationId, ResultKind, Result?] |
||||
|
||||
byte resultKind = (byte)(!string.IsNullOrEmpty(message.error) ? /*error*/ 1 : message.result != null ? /*non-void*/ 3 : /*void*/ 2); |
||||
|
||||
writer.WriteArrayHeader(resultKind == 2 ? 4 : 5); |
||||
|
||||
writer.Write(3); |
||||
WriteHeaders(ref writer); |
||||
WriteString(ref writer, message.invocationId); |
||||
writer.Write(resultKind); |
||||
|
||||
if (resultKind == 1) // error |
||||
WriteString(ref writer, message.error); |
||||
else if (resultKind == 3) // non-void |
||||
WriteValue(ref writer, bufferWriter, message.result); |
||||
|
||||
break; |
||||
|
||||
case MessageTypes.Invocation: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1 |
||||
// [1, Headers, InvocationId, NonBlocking, Target, [Arguments], [StreamIds]] |
||||
|
||||
case MessageTypes.StreamInvocation: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1 |
||||
// [4, Headers, InvocationId, Target, [Arguments], [StreamIds]] |
||||
|
||||
writer.WriteArrayHeader(message.streamIds != null ? 6 : 5); |
||||
|
||||
writer.Write((int)message.type); |
||||
WriteHeaders(ref writer); |
||||
WriteString(ref writer, message.invocationId); |
||||
WriteString(ref writer, message.target); |
||||
writer.WriteArrayHeader(message.arguments != null ? message.arguments.Length : 0); |
||||
if (message.arguments != null) |
||||
for (int i = 0; i < message.arguments.Length; ++i) |
||||
WriteValue(ref writer, bufferWriter, message.arguments[i]); |
||||
|
||||
if (message.streamIds != null) |
||||
{ |
||||
writer.WriteArrayHeader(message.streamIds.Length); |
||||
|
||||
for (int i = 0; i < message.streamIds.Length; ++i) |
||||
WriteValue(ref writer, bufferWriter, message.streamIds[i]); |
||||
} |
||||
|
||||
break; |
||||
|
||||
case MessageTypes.CancelInvocation: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1 |
||||
// [5, Headers, InvocationId] |
||||
|
||||
writer.WriteArrayHeader(3); |
||||
|
||||
writer.Write(5); |
||||
WriteHeaders(ref writer); |
||||
WriteString(ref writer, message.invocationId); |
||||
|
||||
break; |
||||
|
||||
case MessageTypes.Ping: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1 |
||||
// [6] |
||||
|
||||
writer.WriteArrayHeader(1); |
||||
writer.Write(6); |
||||
|
||||
break; |
||||
|
||||
case MessageTypes.Close: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1 |
||||
// [7, Error, AllowReconnect?] |
||||
|
||||
writer.WriteArrayHeader(string.IsNullOrEmpty(message.error) ? 1 : 2); |
||||
|
||||
writer.Write(7); |
||||
if (!string.IsNullOrEmpty(message.error)) |
||||
WriteString(ref writer, message.error); |
||||
|
||||
break; |
||||
} |
||||
|
||||
writer.Flush(); |
||||
|
||||
// get how much bytes got written to the buffer. This includes the 5 placeholder bytes too. |
||||
int length = (int)stream.Position; |
||||
|
||||
// this is the length without the 5 placeholder bytes |
||||
int contentLength = length - 5; |
||||
|
||||
// get the stream's internal buffer. We set the releaseBuffer flag to false, so we can use it safely. |
||||
var buffer = stream.GetBuffer(); |
||||
|
||||
// add varint length prefix |
||||
byte prefixBytes = GetRequiredBytesForLengthPrefix(contentLength); |
||||
WriteLengthAsVarInt(buffer, 5 - prefixBytes, contentLength); |
||||
|
||||
// return with the final segment |
||||
return new BufferSegment(buffer, 5 - prefixBytes, contentLength + prefixBytes); |
||||
} |
||||
|
||||
private void WriteValue(ref MessagePackWriter writer, BufferPoolBufferWriter bufferWriter, object item) |
||||
{ |
||||
if (item == null) |
||||
writer.WriteNil(); |
||||
else |
||||
{ |
||||
writer.Flush(); |
||||
MessagePackSerializer.Serialize(item.GetType(), bufferWriter, item); |
||||
} |
||||
} |
||||
|
||||
private void WriteString(ref MessagePackWriter writer, string str) |
||||
{ |
||||
if (str == null) |
||||
writer.WriteNil(); |
||||
else |
||||
{ |
||||
int count = System.Text.Encoding.UTF8.GetByteCount(str); |
||||
var buffer = BufferPool.Get(count, true); |
||||
System.Text.Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, 0); |
||||
|
||||
writer.WriteString(new ReadOnlySpan<byte>(buffer, 0, count)); |
||||
|
||||
BufferPool.Release(buffer); |
||||
} |
||||
} |
||||
|
||||
private void WriteHeaders(ref MessagePackWriter writer) |
||||
{ |
||||
writer.WriteMapHeader(0); |
||||
} |
||||
|
||||
public void ParseMessages(BufferSegment segment, ref List<Message> messages) |
||||
{ |
||||
int offset = segment.Offset; |
||||
while (offset < segment.Count) |
||||
{ |
||||
int length = (int)ReadVarInt(segment.Data, ref offset); |
||||
|
||||
var reader = new MessagePackReader(new ReadOnlyMemory<byte>(segment.Data, offset, length)); |
||||
|
||||
int arrayLength = reader.ReadArrayHeader(); |
||||
int messageType = reader.ReadByte(); |
||||
|
||||
switch ((MessageTypes)messageType) |
||||
{ |
||||
case MessageTypes.Invocation: messages.Add(ReadInvocation(ref reader)); break; |
||||
case MessageTypes.StreamItem: messages.Add(ReadStreamItem(ref reader)); break; |
||||
case MessageTypes.Completion: messages.Add(ReadCompletion(ref reader)); break; |
||||
case MessageTypes.StreamInvocation: messages.Add(ReadStreamInvocation(ref reader)); break; |
||||
case MessageTypes.CancelInvocation: messages.Add(ReadCancelInvocation(ref reader)); break; |
||||
case MessageTypes.Ping: |
||||
|
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1 |
||||
messages.Add(new Message { type = MessageTypes.Ping }); |
||||
break; |
||||
case MessageTypes.Close: messages.Add(ReadClose(ref reader)); break; |
||||
} |
||||
|
||||
offset += length; |
||||
} |
||||
} |
||||
|
||||
private Message ReadClose(ref MessagePackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1 |
||||
|
||||
string error = reader.ReadString(); |
||||
bool allowReconnect = false; |
||||
try |
||||
{ |
||||
allowReconnect = reader.ReadBoolean(); |
||||
} |
||||
catch { } |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Close, |
||||
error = error, |
||||
allowReconnect = allowReconnect |
||||
}; |
||||
} |
||||
|
||||
private Message ReadCancelInvocation(ref MessagePackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1 |
||||
|
||||
ReadHeaders(ref reader); |
||||
string invocationId = reader.ReadString(); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.CancelInvocation, |
||||
invocationId = invocationId |
||||
}; |
||||
} |
||||
|
||||
private Message ReadStreamInvocation(ref MessagePackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1 |
||||
|
||||
ReadHeaders(ref reader); |
||||
string invocationId = reader.ReadString(); |
||||
string target = reader.ReadString(); |
||||
object[] arguments = ReadArguments(ref reader, target); |
||||
string[] streamIds = ReadStreamIds(ref reader); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.StreamInvocation, |
||||
invocationId = invocationId, |
||||
target = target, |
||||
arguments = arguments, |
||||
streamIds = streamIds |
||||
}; |
||||
} |
||||
|
||||
private Message ReadCompletion(ref MessagePackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1 |
||||
|
||||
ReadHeaders(ref reader); |
||||
string invocationId = reader.ReadString(); |
||||
byte resultKind = reader.ReadByte(); |
||||
|
||||
switch (resultKind) |
||||
{ |
||||
// 1 - Error result - Result contains a String with the error message |
||||
case 1: |
||||
string error = reader.ReadString(); |
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Completion, |
||||
invocationId = invocationId, |
||||
error = error |
||||
}; |
||||
|
||||
// 2 - Void result - Result is absent |
||||
case 2: |
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Completion, |
||||
invocationId = invocationId |
||||
}; |
||||
|
||||
// 3 - Non-Void result - Result contains the value returned by the server |
||||
case 3: |
||||
object item = ReadItem(ref reader, invocationId); |
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Completion, |
||||
invocationId = invocationId, |
||||
item = item, |
||||
result = item |
||||
}; |
||||
|
||||
default: |
||||
throw new NotImplementedException("Unknown resultKind: " + resultKind); |
||||
} |
||||
} |
||||
|
||||
private Message ReadStreamItem(ref MessagePackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1 |
||||
|
||||
ReadHeaders(ref reader); |
||||
string invocationId = reader.ReadString(); |
||||
object item = ReadItem(ref reader, invocationId); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.StreamItem, |
||||
invocationId = invocationId, |
||||
item = item |
||||
}; |
||||
} |
||||
|
||||
private Message ReadInvocation(ref MessagePackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1 |
||||
|
||||
ReadHeaders(ref reader); |
||||
string invocationId = reader.ReadString(); |
||||
string target = reader.ReadString(); |
||||
object[] arguments = ReadArguments(ref reader, target); |
||||
string[] streamIds = ReadStreamIds(ref reader); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Invocation, |
||||
invocationId = invocationId, |
||||
target = target, |
||||
arguments = arguments, |
||||
streamIds = streamIds |
||||
}; |
||||
} |
||||
|
||||
private object ReadItem(ref MessagePackReader reader, string invocationId) |
||||
{ |
||||
long longId = 0; |
||||
if (long.TryParse(invocationId, out longId)) |
||||
{ |
||||
Type itemType = this.Connection.GetItemType(longId); |
||||
|
||||
return MessagePackSerializer.Deserialize(itemType, reader.ReadRaw()); |
||||
} |
||||
else |
||||
{ |
||||
reader.Skip(); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
private string[] ReadStreamIds(ref MessagePackReader reader) |
||||
{ |
||||
var count = reader.ReadArrayHeader(); |
||||
string[] result = null; |
||||
|
||||
if (count > 0) |
||||
{ |
||||
result = new string[count]; |
||||
for (int i = 0; i < count; i++) |
||||
result[i] = reader.ReadString(); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
private object[] ReadArguments(ref MessagePackReader reader, string target) |
||||
{ |
||||
var subscription = this.Connection.GetSubscription(target); |
||||
|
||||
object[] args = null; |
||||
if (subscription == null || subscription.callbacks == null || subscription.callbacks.Count == 0) |
||||
{ |
||||
reader.Skip(); |
||||
} |
||||
else |
||||
{ |
||||
int count = reader.ReadArrayHeader(); |
||||
|
||||
if (subscription.callbacks[0].ParamTypes != null) |
||||
{ |
||||
args = new object[subscription.callbacks[0].ParamTypes.Length]; |
||||
for (int i = 0; i < subscription.callbacks[0].ParamTypes.Length; ++i) |
||||
args[i] = MessagePackSerializer.Deserialize(subscription.callbacks[0].ParamTypes[i], reader.ReadRaw()); |
||||
} |
||||
else |
||||
args = null; |
||||
} |
||||
|
||||
return args; |
||||
} |
||||
|
||||
private Dictionary<string, string> ReadHeaders(ref MessagePackReader reader) |
||||
{ |
||||
int count = reader.ReadMapHeader(); |
||||
|
||||
Dictionary<string, string> result = null; |
||||
if (count > 0) |
||||
{ |
||||
result = new Dictionary<string, string>(count); |
||||
|
||||
for (int i = 0; i < count; i++) |
||||
{ |
||||
string key = reader.ReadString(); |
||||
string value = reader.ReadString(); |
||||
|
||||
result.Add(key, value); |
||||
} |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
public static byte GetRequiredBytesForLengthPrefix(int length) |
||||
{ |
||||
byte bytes = 0; |
||||
do |
||||
{ |
||||
length >>= 7; |
||||
bytes++; |
||||
} |
||||
while (length > 0); |
||||
|
||||
return bytes; |
||||
} |
||||
|
||||
public static int WriteLengthAsVarInt(byte[] data, int offset, int length) |
||||
{ |
||||
do |
||||
{ |
||||
var current = data[offset]; |
||||
current = (byte)(length & 0x7f); |
||||
length >>= 7; |
||||
if (length > 0) |
||||
{ |
||||
current |= 0x80; |
||||
} |
||||
|
||||
data[offset++] = current; |
||||
} |
||||
while (length > 0); |
||||
|
||||
return offset; |
||||
} |
||||
|
||||
public static uint ReadVarInt(byte[] data, ref int offset) |
||||
{ |
||||
var length = 0U; |
||||
var numBytes = 0; |
||||
|
||||
byte byteRead; |
||||
do |
||||
{ |
||||
byteRead = data[offset + numBytes]; |
||||
length = length | (((uint)(byteRead & 0x7f)) << (numBytes * 7)); |
||||
numBytes++; |
||||
} |
||||
while (offset + numBytes < data.Length && ((byteRead & 0x80) != 0)); |
||||
|
||||
offset += numBytes; |
||||
|
||||
return length; |
||||
} |
||||
|
||||
public object ConvertTo(Type toType, object obj) |
||||
{ |
||||
if (obj == null) |
||||
return null; |
||||
|
||||
#if NETFX_CORE |
||||
TypeInfo typeInfo = toType.GetTypeInfo(); |
||||
#endif |
||||
|
||||
#if NETFX_CORE |
||||
if (typeInfo.IsEnum) |
||||
#else |
||||
if (toType.IsEnum) |
||||
#endif |
||||
return Enum.Parse(toType, obj.ToString(), true); |
||||
|
||||
#if NETFX_CORE |
||||
if (typeInfo.IsPrimitive) |
||||
#else |
||||
if (toType.IsPrimitive) |
||||
#endif |
||||
return Convert.ChangeType(obj, toType); |
||||
|
||||
if (toType == typeof(string)) |
||||
return obj.ToString(); |
||||
|
||||
#if NETFX_CORE |
||||
if (typeInfo.IsGenericType && toType.Name == "Nullable`1") |
||||
return Convert.ChangeType(obj, toType.GenericTypeArguments[0]); |
||||
#else |
||||
if (toType.IsGenericType && toType.Name == "Nullable`1") |
||||
return Convert.ChangeType(obj, toType.GetGenericArguments()[0]); |
||||
#endif |
||||
|
||||
return obj; |
||||
} |
||||
|
||||
public object[] GetRealArguments(Type[] argTypes, object[] arguments) |
||||
{ |
||||
if (arguments == null || arguments.Length == 0) |
||||
return null; |
||||
|
||||
if (argTypes.Length > arguments.Length) |
||||
throw new Exception(string.Format("argType.Length({0}) < arguments.length({1})", argTypes.Length, arguments.Length)); |
||||
|
||||
return arguments; |
||||
} |
||||
} |
||||
} |
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 3c4d3d400dd496d43a409e350143e85a |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,868 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE && BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK |
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using BestHTTP.PlatformSupport.Memory; |
||||
using BestHTTP.SignalRCore.Messages; |
||||
using GameDevWare.Serialization; |
||||
using GameDevWare.Serialization.MessagePack; |
||||
using GameDevWare.Serialization.Serializers; |
||||
|
||||
using UnityEngine; |
||||
|
||||
namespace BestHTTP.SignalRCore.Encoders |
||||
{ |
||||
public sealed class MessagePackProtocolSerializationOptions |
||||
{ |
||||
/// <summary> |
||||
/// A function that must return a TypeSerializer for the given Type. To serialize an enum it can return an EnumNumberSerializer (default) to serialize enums as numbers or EnumSerializer to serialize them as strings. |
||||
/// </summary> |
||||
public Func<Type, TypeSerializer> EnumSerializerFactory; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// IPRotocol implementation using the "Json & MessagePack Serialization" asset store package (https://assetstore.unity.com/packages/tools/network/json-messagepack-serialization-59918). |
||||
/// </summary> |
||||
public sealed class MessagePackProtocol : BestHTTP.SignalRCore.IProtocol |
||||
{ |
||||
public string Name { get { return "messagepack"; } } |
||||
|
||||
public TransferModes Type { get { return TransferModes.Binary; } } |
||||
|
||||
public IEncoder Encoder { get; private set; } |
||||
|
||||
public HubConnection Connection { get; set; } |
||||
|
||||
public MessagePackProtocolSerializationOptions Options { get; set; } |
||||
|
||||
public MessagePackProtocol() |
||||
: this(new MessagePackProtocolSerializationOptions { EnumSerializerFactory = (enumType) => new EnumNumberSerializer(enumType) }) |
||||
{ |
||||
|
||||
} |
||||
|
||||
public MessagePackProtocol(MessagePackProtocolSerializationOptions options) |
||||
{ |
||||
this.Options = options; |
||||
|
||||
GameDevWare.Serialization.Json.DefaultSerializers.Clear(); |
||||
GameDevWare.Serialization.Json.DefaultSerializers.AddRange(new TypeSerializer[] |
||||
{ |
||||
new BinarySerializer(), |
||||
new DateTimeOffsetSerializer(), |
||||
new DateTimeSerializer(), |
||||
new GuidSerializer(), |
||||
new StreamSerializer(), |
||||
new UriSerializer(), |
||||
new VersionSerializer(), |
||||
new TimeSpanSerializer(), |
||||
new DictionaryEntrySerializer(), |
||||
|
||||
new BestHTTP.SignalRCore.Encoders.Vector2Serializer(), |
||||
new BestHTTP.SignalRCore.Encoders.Vector3Serializer(), |
||||
new BestHTTP.SignalRCore.Encoders.Vector4Serializer(), |
||||
|
||||
new PrimitiveSerializer(typeof (bool)), |
||||
new PrimitiveSerializer(typeof (byte)), |
||||
new PrimitiveSerializer(typeof (decimal)), |
||||
new PrimitiveSerializer(typeof (double)), |
||||
new PrimitiveSerializer(typeof (short)), |
||||
new PrimitiveSerializer(typeof (int)), |
||||
new PrimitiveSerializer(typeof (long)), |
||||
new PrimitiveSerializer(typeof (sbyte)), |
||||
new PrimitiveSerializer(typeof (float)), |
||||
new PrimitiveSerializer(typeof (ushort)), |
||||
new PrimitiveSerializer(typeof (uint)), |
||||
new PrimitiveSerializer(typeof (ulong)), |
||||
new PrimitiveSerializer(typeof (string)), |
||||
}); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This function must convert all element in the arguments array to the corresponding type from the argTypes array. |
||||
/// </summary> |
||||
public object[] GetRealArguments(Type[] argTypes, object[] arguments) |
||||
{ |
||||
if (arguments == null || arguments.Length == 0) |
||||
return null; |
||||
|
||||
if (argTypes.Length > arguments.Length) |
||||
throw new Exception(string.Format("argType.Length({0}) < arguments.length({1})", argTypes.Length, arguments.Length)); |
||||
|
||||
return arguments; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Convert a value to the given type. |
||||
/// </summary> |
||||
public object ConvertTo(Type toType, object obj) |
||||
{ |
||||
if (obj == null) |
||||
return null; |
||||
|
||||
#if NETFX_CORE |
||||
TypeInfo typeInfo = toType.GetTypeInfo(); |
||||
#endif |
||||
|
||||
#if NETFX_CORE |
||||
if (typeInfo.IsEnum) |
||||
#else |
||||
if (toType.IsEnum) |
||||
#endif |
||||
return Enum.Parse(toType, obj.ToString(), true); |
||||
|
||||
#if NETFX_CORE |
||||
if (typeInfo.IsPrimitive) |
||||
#else |
||||
if (toType.IsPrimitive) |
||||
#endif |
||||
return Convert.ChangeType(obj, toType); |
||||
|
||||
if (toType == typeof(string)) |
||||
return obj.ToString(); |
||||
|
||||
#if NETFX_CORE |
||||
if (typeInfo.IsGenericType && toType.Name == "Nullable`1") |
||||
return Convert.ChangeType(obj, toType.GenericTypeArguments[0]); |
||||
#else |
||||
if (toType.IsGenericType && toType.Name == "Nullable`1") |
||||
return Convert.ChangeType(obj, toType.GetGenericArguments()[0]); |
||||
#endif |
||||
|
||||
return obj; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This function must return the encoded representation of the given message. |
||||
/// </summary> |
||||
public BufferSegment EncodeMessage(Message message) |
||||
{ |
||||
var memBuffer = BufferPool.Get(256, true); |
||||
var stream = new BestHTTP.Extensions.BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true); |
||||
|
||||
// Write 5 bytes for placeholder for length prefix |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
stream.WriteByte(0); |
||||
|
||||
var buffer = BufferPool.Get(MsgPackWriter.DEFAULT_BUFFER_SIZE, true); |
||||
|
||||
var context = new SerializationContext { |
||||
Options = SerializationOptions.SuppressTypeInformation, |
||||
EnumSerializerFactory = this.Options.EnumSerializerFactory, |
||||
ExtensionTypeHandler = CustomMessagePackExtensionTypeHandler.Instance |
||||
}; |
||||
|
||||
var writer = new MsgPackWriter(stream, context, buffer); |
||||
|
||||
switch (message.type) |
||||
{ |
||||
case MessageTypes.StreamItem: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1 |
||||
// [2, Headers, InvocationId, Item] |
||||
|
||||
writer.WriteArrayBegin(4); |
||||
|
||||
writer.WriteNumber(2); |
||||
WriteHeaders(writer); |
||||
writer.WriteString(message.invocationId); |
||||
WriteValue(writer, message.item); |
||||
|
||||
writer.WriteArrayEnd(); |
||||
break; |
||||
|
||||
case MessageTypes.Completion: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1 |
||||
// [3, Headers, InvocationId, ResultKind, Result?] |
||||
|
||||
byte resultKind = (byte)(!string.IsNullOrEmpty(message.error) ? /*error*/ 1 : message.result != null ? /*non-void*/ 3 : /*void*/ 2); |
||||
|
||||
writer.WriteArrayBegin(resultKind == 2 ? 4 : 5); |
||||
|
||||
writer.WriteNumber(3); |
||||
WriteHeaders(writer); |
||||
writer.WriteString(message.invocationId); |
||||
writer.WriteNumber(resultKind); |
||||
|
||||
if (resultKind == 1) // error |
||||
writer.WriteString(message.error); |
||||
else if (resultKind == 3) // non-void |
||||
WriteValue(writer, message.result); |
||||
|
||||
writer.WriteArrayEnd(); |
||||
break; |
||||
|
||||
case MessageTypes.Invocation: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1 |
||||
// [1, Headers, InvocationId, NonBlocking, Target, [Arguments], [StreamIds]] |
||||
|
||||
case MessageTypes.StreamInvocation: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1 |
||||
// [4, Headers, InvocationId, Target, [Arguments], [StreamIds]] |
||||
|
||||
writer.WriteArrayBegin(message.streamIds != null ? 6 : 5); |
||||
|
||||
writer.WriteNumber((int)message.type); |
||||
WriteHeaders(writer); |
||||
writer.WriteString(message.invocationId); |
||||
writer.WriteString(message.target); |
||||
writer.WriteArrayBegin(message.arguments != null ? message.arguments.Length : 0); |
||||
if (message.arguments != null) |
||||
for (int i = 0; i < message.arguments.Length; ++i) |
||||
WriteValue(writer, message.arguments[i]); |
||||
writer.WriteArrayEnd(); |
||||
|
||||
if (message.streamIds != null) |
||||
{ |
||||
writer.WriteArrayBegin(message.streamIds.Length); |
||||
|
||||
for (int i = 0; i < message.streamIds.Length; ++i) |
||||
WriteValue(writer, message.streamIds[i]); |
||||
|
||||
writer.WriteArrayEnd(); |
||||
} |
||||
|
||||
writer.WriteArrayEnd(); |
||||
break; |
||||
|
||||
case MessageTypes.CancelInvocation: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1 |
||||
// [5, Headers, InvocationId] |
||||
|
||||
writer.WriteArrayBegin(3); |
||||
|
||||
writer.WriteNumber(5); |
||||
WriteHeaders(writer); |
||||
writer.WriteString(message.invocationId); |
||||
|
||||
writer.WriteArrayEnd(); |
||||
break; |
||||
|
||||
case MessageTypes.Ping: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1 |
||||
// [6] |
||||
|
||||
writer.WriteArrayBegin(1); |
||||
|
||||
writer.WriteNumber(6); |
||||
|
||||
writer.WriteArrayEnd(); |
||||
break; |
||||
|
||||
case MessageTypes.Close: |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1 |
||||
// [7, Error, AllowReconnect?] |
||||
|
||||
writer.WriteArrayBegin(string.IsNullOrEmpty(message.error) ? 1 : 2); |
||||
|
||||
writer.WriteNumber(7); |
||||
if (!string.IsNullOrEmpty(message.error)) |
||||
writer.WriteString(message.error); |
||||
|
||||
writer.WriteArrayEnd(); |
||||
break; |
||||
} |
||||
|
||||
writer.Flush(); |
||||
|
||||
// release back the buffer we used for the MsgPackWriter |
||||
BufferPool.Release(buffer); |
||||
|
||||
// get how much bytes got written to the buffer. This includes the 5 placeholder bytes too. |
||||
int length = (int)stream.Position; |
||||
|
||||
// this is the length without the 5 placeholder bytes |
||||
int contentLength = length - 5; |
||||
|
||||
// get the stream's internal buffer. We set the releaseBuffer flag to false, so we can use it safely. |
||||
buffer = stream.GetBuffer(); |
||||
|
||||
// add varint length prefix |
||||
byte prefixBytes = GetRequiredBytesForLengthPrefix(contentLength); |
||||
WriteLengthAsVarInt(buffer, 5 - prefixBytes, contentLength); |
||||
|
||||
// return with the final segment |
||||
return new BufferSegment(buffer, 5 - prefixBytes, contentLength + prefixBytes); |
||||
} |
||||
|
||||
private void WriteValue(MsgPackWriter writer, object value) |
||||
{ |
||||
if (value == null) |
||||
writer.WriteNull(); |
||||
else |
||||
writer.WriteValue(value, value.GetType()); |
||||
} |
||||
|
||||
private void WriteHeaders(MsgPackWriter writer) |
||||
{ |
||||
writer.WriteObjectBegin(0); |
||||
writer.WriteObjectEnd(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This function must parse binary representation of the messages into the list of Messages. |
||||
/// </summary> |
||||
public void ParseMessages(BufferSegment segment, ref List<Message> messages) |
||||
{ |
||||
messages.Clear(); |
||||
|
||||
int offset = segment.Offset; |
||||
while (offset < segment.Count) |
||||
{ |
||||
int length = (int)ReadVarInt(segment.Data, ref offset); |
||||
|
||||
using (var stream = new System.IO.MemoryStream(segment.Data, offset, length)) |
||||
{ |
||||
var buff = BufferPool.Get(MsgPackReader.DEFAULT_BUFFER_SIZE, true); |
||||
try |
||||
{ |
||||
var context = new SerializationContext { |
||||
Options = SerializationOptions.SuppressTypeInformation, |
||||
ExtensionTypeHandler = CustomMessagePackExtensionTypeHandler.Instance |
||||
}; |
||||
var reader = new MsgPackReader(stream, context, Endianness.BigEndian, buff); |
||||
|
||||
reader.NextToken(); |
||||
reader.NextToken(); |
||||
|
||||
int messageType = reader.ReadByte(); |
||||
switch ((MessageTypes)messageType) |
||||
{ |
||||
case MessageTypes.Invocation: messages.Add(ReadInvocation(reader)); break; |
||||
case MessageTypes.StreamItem: messages.Add(ReadStreamItem(reader)); break; |
||||
case MessageTypes.Completion: messages.Add(ReadCompletion(reader)); break; |
||||
case MessageTypes.StreamInvocation: messages.Add(ReadStreamInvocation(reader)); break; |
||||
case MessageTypes.CancelInvocation: messages.Add(ReadCancelInvocation(reader)); break; |
||||
case MessageTypes.Ping: |
||||
|
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1 |
||||
messages.Add(new Message { type = MessageTypes.Ping }); |
||||
break; |
||||
case MessageTypes.Close: messages.Add(ReadClose(reader)); break; |
||||
} |
||||
|
||||
reader.NextToken(); |
||||
} |
||||
finally |
||||
{ |
||||
BufferPool.Release(buff); |
||||
} |
||||
} |
||||
|
||||
offset += length; |
||||
} |
||||
} |
||||
|
||||
private Message ReadClose(MsgPackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1 |
||||
|
||||
string error = reader.ReadString(); |
||||
bool allowReconnect = false; |
||||
try |
||||
{ |
||||
allowReconnect = reader.ReadBoolean(); |
||||
} |
||||
catch { } |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Close, |
||||
error = error, |
||||
allowReconnect = allowReconnect |
||||
}; |
||||
} |
||||
|
||||
private Message ReadCancelInvocation(MsgPackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1 |
||||
|
||||
ReadHeaders(reader); |
||||
string invocationId = reader.ReadString(); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.CancelInvocation, |
||||
invocationId = invocationId |
||||
}; |
||||
} |
||||
|
||||
private Message ReadStreamInvocation(MsgPackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1 |
||||
|
||||
ReadHeaders(reader); |
||||
string invocationId = reader.ReadString(); |
||||
string target = reader.ReadString(); |
||||
object[] arguments = ReadArguments(reader, target); |
||||
string[] streamIds = ReadStreamIds(reader); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.StreamInvocation, |
||||
invocationId = invocationId, |
||||
target = target, |
||||
arguments = arguments, |
||||
streamIds = streamIds |
||||
}; |
||||
} |
||||
|
||||
private Message ReadCompletion(MsgPackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1 |
||||
|
||||
ReadHeaders(reader); |
||||
string invocationId = reader.ReadString(); |
||||
byte resultKind = reader.ReadByte(); |
||||
|
||||
switch(resultKind) |
||||
{ |
||||
// 1 - Error result - Result contains a String with the error message |
||||
case 1: |
||||
string error = reader.ReadString(); |
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Completion, |
||||
invocationId = invocationId, |
||||
error = error |
||||
}; |
||||
|
||||
// 2 - Void result - Result is absent |
||||
case 2: |
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Completion, |
||||
invocationId = invocationId |
||||
}; |
||||
|
||||
// 3 - Non-Void result - Result contains the value returned by the server |
||||
case 3: |
||||
object item = ReadItem(reader, invocationId); |
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Completion, |
||||
invocationId = invocationId, |
||||
item = item, |
||||
result = item |
||||
}; |
||||
|
||||
default: |
||||
throw new NotImplementedException("Unknown resultKind: " + resultKind); |
||||
} |
||||
} |
||||
|
||||
private Message ReadStreamItem(MsgPackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1 |
||||
|
||||
ReadHeaders(reader); |
||||
string invocationId = reader.ReadString(); |
||||
object item = ReadItem(reader, invocationId); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.StreamItem, |
||||
invocationId = invocationId, |
||||
item = item |
||||
}; |
||||
} |
||||
|
||||
private Message ReadInvocation(MsgPackReader reader) |
||||
{ |
||||
// https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1 |
||||
|
||||
ReadHeaders(reader); |
||||
string invocationId = reader.ReadString(); |
||||
string target = reader.ReadString(); |
||||
object[] arguments = ReadArguments(reader, target); |
||||
string[] streamIds = ReadStreamIds(reader); |
||||
|
||||
return new Message |
||||
{ |
||||
type = MessageTypes.Invocation, |
||||
invocationId = invocationId, |
||||
target = target, |
||||
arguments = arguments, |
||||
streamIds = streamIds |
||||
}; |
||||
} |
||||
|
||||
private object ReadItem(MsgPackReader reader, string invocationId) |
||||
{ |
||||
long longId = 0; |
||||
if (long.TryParse(invocationId, out longId)) |
||||
{ |
||||
Type itemType = this.Connection.GetItemType(longId); |
||||
return reader.ReadValue(itemType); |
||||
} |
||||
else |
||||
return reader.ReadValue(typeof(object)); |
||||
} |
||||
|
||||
private string[] ReadStreamIds(MsgPackReader reader) |
||||
{ |
||||
return reader.ReadValue(typeof(string[])) as string[]; |
||||
} |
||||
|
||||
private object[] ReadArguments(MsgPackReader reader, string target) |
||||
{ |
||||
var subscription = this.Connection.GetSubscription(target); |
||||
|
||||
object[] args; |
||||
if (subscription == null || subscription.callbacks == null || subscription.callbacks.Count == 0) |
||||
{ |
||||
args = reader.ReadValue(typeof(object[])) as object[]; |
||||
} |
||||
else |
||||
{ |
||||
reader.NextToken(); |
||||
|
||||
if (subscription.callbacks[0].ParamTypes != null) |
||||
{ |
||||
args = new object[subscription.callbacks[0].ParamTypes.Length]; |
||||
for (int i = 0; i < subscription.callbacks[0].ParamTypes.Length; ++i) |
||||
args[i] = reader.ReadValue(subscription.callbacks[0].ParamTypes[i]); |
||||
} |
||||
else |
||||
args = null; |
||||
|
||||
reader.NextToken(); |
||||
} |
||||
|
||||
return args; |
||||
} |
||||
|
||||
private Dictionary<string, string> ReadHeaders(MsgPackReader reader) |
||||
{ |
||||
return reader.ReadValue(typeof(Dictionary<string, string>)) as Dictionary<string, string>; |
||||
} |
||||
|
||||
public static byte GetRequiredBytesForLengthPrefix(int length) |
||||
{ |
||||
byte bytes = 0; |
||||
do |
||||
{ |
||||
length >>= 7; |
||||
bytes++; |
||||
} |
||||
while (length > 0); |
||||
|
||||
return bytes; |
||||
} |
||||
|
||||
public static int WriteLengthAsVarInt(byte[] data, int offset, int length) |
||||
{ |
||||
do |
||||
{ |
||||
var current = data[offset]; |
||||
current = (byte)(length & 0x7f); |
||||
length >>= 7; |
||||
if (length > 0) |
||||
{ |
||||
current |= 0x80; |
||||
} |
||||
|
||||
data[offset++] = current; |
||||
} |
||||
while (length > 0); |
||||
|
||||
return offset; |
||||
} |
||||
|
||||
public static uint ReadVarInt(byte[] data, ref int offset) |
||||
{ |
||||
var length = 0U; |
||||
var numBytes = 0; |
||||
|
||||
byte byteRead; |
||||
do |
||||
{ |
||||
byteRead = data[offset + numBytes]; |
||||
length = length | (((uint)(byteRead & 0x7f)) << (numBytes * 7)); |
||||
numBytes++; |
||||
} |
||||
while (offset + numBytes < data.Length && ((byteRead & 0x80) != 0)); |
||||
|
||||
offset += numBytes; |
||||
|
||||
return length; |
||||
} |
||||
} |
||||
|
||||
public sealed class CustomMessagePackExtensionTypeHandler : MessagePackExtensionTypeHandler |
||||
{ |
||||
public const int EXTENSION_TYPE_DATE_TIME = -1; |
||||
public const int DATE_TIME_SIZE = 8; |
||||
|
||||
public const long BclSecondsAtUnixEpoch = 62135596800; |
||||
public const int NanosecondsPerTick = 100; |
||||
public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); |
||||
|
||||
private static readonly Type[] DefaultExtensionTypes = new[] { typeof(DateTime) }; |
||||
public static CustomMessagePackExtensionTypeHandler Instance = new CustomMessagePackExtensionTypeHandler(); |
||||
|
||||
public override IEnumerable<Type> ExtensionTypes |
||||
{ |
||||
get { return DefaultExtensionTypes; } |
||||
} |
||||
|
||||
|
||||
public override bool TryRead(sbyte type, ArraySegment<byte> data, out object value) |
||||
{ |
||||
if (data.Array == null) throw new ArgumentNullException("data"); |
||||
|
||||
value = default(object); |
||||
switch (type) |
||||
{ |
||||
case EXTENSION_TYPE_DATE_TIME: |
||||
switch (data.Count) |
||||
{ |
||||
case 4: |
||||
{ |
||||
var intValue = unchecked((int)(FromBytes(data.Array, data.Offset, 4))); |
||||
value = CustomMessagePackExtensionTypeHandler.UnixEpoch.AddSeconds(unchecked((uint)intValue)); |
||||
return true; |
||||
} |
||||
case 8: |
||||
{ |
||||
long longValue = FromBytes(data.Array, data.Offset, 8); |
||||
ulong ulongValue = unchecked((ulong)longValue); |
||||
long nanoseconds = (long)(ulongValue >> 34); |
||||
ulong seconds = ulongValue & 0x00000003ffffffffL; |
||||
value = CustomMessagePackExtensionTypeHandler.UnixEpoch.AddSeconds(seconds).AddTicks(nanoseconds / CustomMessagePackExtensionTypeHandler.NanosecondsPerTick); |
||||
return true; |
||||
} |
||||
case 12: |
||||
{ |
||||
var intValue = unchecked((int)(FromBytes(data.Array, data.Offset, 4))); |
||||
long longValue = FromBytes(data.Array, data.Offset, 8); |
||||
|
||||
var nanoseconds = unchecked((uint)intValue); |
||||
value = CustomMessagePackExtensionTypeHandler.UnixEpoch.AddSeconds(longValue).AddTicks(nanoseconds / CustomMessagePackExtensionTypeHandler.NanosecondsPerTick); |
||||
return true; |
||||
} |
||||
default: |
||||
throw new Exception($"Length of extension was {data.Count}. Either 4, 8 or 12 were expected."); |
||||
} |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public override bool TryWrite(object value, out sbyte type, ref ArraySegment<byte> data) |
||||
{ |
||||
if (value == null) |
||||
{ |
||||
type = 0; |
||||
return false; |
||||
} |
||||
else if (value is DateTime) |
||||
{ |
||||
type = EXTENSION_TYPE_DATE_TIME; |
||||
|
||||
var dateTime = (DateTime)(object)value; |
||||
|
||||
// The spec requires UTC. Convert to UTC if we're sure the value was expressed as Local time. |
||||
// If it's Unspecified, we want to leave it alone since .NET will change the value when we convert |
||||
// and we simply don't know, so we should leave it as-is. |
||||
if (dateTime.Kind == DateTimeKind.Local) |
||||
{ |
||||
dateTime = dateTime.ToUniversalTime(); |
||||
} |
||||
|
||||
var secondsSinceBclEpoch = dateTime.Ticks / TimeSpan.TicksPerSecond; |
||||
var seconds = secondsSinceBclEpoch - CustomMessagePackExtensionTypeHandler.BclSecondsAtUnixEpoch; |
||||
var nanoseconds = (dateTime.Ticks % TimeSpan.TicksPerSecond) * CustomMessagePackExtensionTypeHandler.NanosecondsPerTick; |
||||
|
||||
if ((seconds >> 34) == 0) |
||||
{ |
||||
var data64 = unchecked((ulong)((nanoseconds << 34) | seconds)); |
||||
if ((data64 & 0xffffffff00000000L) == 0) |
||||
{ |
||||
// timestamp 32(seconds in 32-bit unsigned int) |
||||
var data32 = (UInt32)data64; |
||||
|
||||
const int TIMESTAMP_SIZE = 4; |
||||
|
||||
if (data.Array == null || data.Count < TIMESTAMP_SIZE) |
||||
data = new ArraySegment<byte>(new byte[TIMESTAMP_SIZE]); |
||||
|
||||
CopyBytesImpl(data32, 4, data.Array, data.Offset); |
||||
|
||||
if (data.Count != DATE_TIME_SIZE) |
||||
data = new ArraySegment<byte>(data.Array, data.Offset, DATE_TIME_SIZE); |
||||
} |
||||
else |
||||
{ |
||||
// timestamp 64(nanoseconds in 30-bit unsigned int | seconds in 34-bit unsigned int) |
||||
const int TIMESTAMP_SIZE = 8; |
||||
if (data.Array == null || data.Count < TIMESTAMP_SIZE) |
||||
data = new ArraySegment<byte>(new byte[TIMESTAMP_SIZE]); |
||||
|
||||
CopyBytesImpl(unchecked((long)data64), 8, data.Array, data.Offset); |
||||
|
||||
if (data.Count != DATE_TIME_SIZE) |
||||
data = new ArraySegment<byte>(data.Array, data.Offset, DATE_TIME_SIZE); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
// timestamp 96( nanoseconds in 32-bit unsigned int | seconds in 64-bit signed int ) |
||||
|
||||
const int TIMESTAMP_SIZE = 12; |
||||
|
||||
if (data.Array == null || data.Count < TIMESTAMP_SIZE) |
||||
data = new ArraySegment<byte>(new byte[TIMESTAMP_SIZE]); |
||||
|
||||
CopyBytesImpl((uint)nanoseconds, 4, data.Array, data.Offset); |
||||
CopyBytesImpl(seconds, 8, data.Array, data.Offset + 4); |
||||
|
||||
if (data.Count != DATE_TIME_SIZE) |
||||
data = new ArraySegment<byte>(data.Array, data.Offset, DATE_TIME_SIZE); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
type = default(sbyte); |
||||
return false; |
||||
} |
||||
|
||||
private void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) |
||||
{ |
||||
var endOffset = index + bytes - 1; |
||||
for (var i = 0; i < bytes; i++) |
||||
{ |
||||
buffer[endOffset - i] = unchecked((byte)(value & 0xff)); |
||||
value = value >> 8; |
||||
} |
||||
} |
||||
|
||||
private long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) |
||||
{ |
||||
long ret = 0; |
||||
for (var i = 0; i < bytesToConvert; i++) |
||||
{ |
||||
ret = unchecked((ret << 8) | buffer[startIndex + i]); |
||||
} |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
// https://github.com/neuecc/MessagePack-CSharp/blob/13c299a5172c60154bae53395612af194c02d286/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Formatters.cs#L15 |
||||
public sealed class Vector2Serializer : TypeSerializer |
||||
{ |
||||
public override Type SerializedType { get { return typeof(Vector2); } } |
||||
|
||||
public override object Deserialize(IJsonReader reader) |
||||
{ |
||||
if (reader == null) throw new ArgumentNullException("reader"); |
||||
|
||||
if (reader.Token == JsonToken.Null) |
||||
return null; |
||||
|
||||
var value = new Vector2(); |
||||
reader.ReadArrayBegin(); |
||||
|
||||
int idx = 0; |
||||
while (reader.Token != JsonToken.EndOfArray) |
||||
value[idx++] = reader.ReadSingle(); |
||||
|
||||
reader.ReadArrayEnd(nextToken: false); |
||||
|
||||
return value; |
||||
} |
||||
|
||||
public override void Serialize(IJsonWriter writer, object value) |
||||
{ |
||||
if (writer == null) throw new ArgumentNullException("writer"); |
||||
if (value == null) throw new ArgumentNullException("value"); |
||||
|
||||
var vector2 = (Vector2)value; |
||||
writer.WriteArrayBegin(2); |
||||
writer.Write(vector2.x); |
||||
writer.Write(vector2.y); |
||||
writer.WriteArrayEnd(); |
||||
} |
||||
} |
||||
|
||||
// https://github.com/neuecc/MessagePack-CSharp/blob/13c299a5172c60154bae53395612af194c02d286/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Formatters.cs#L56 |
||||
public sealed class Vector3Serializer : TypeSerializer |
||||
{ |
||||
public override Type SerializedType { get { return typeof(Vector3); } } |
||||
|
||||
public override object Deserialize(IJsonReader reader) |
||||
{ |
||||
if (reader == null) throw new ArgumentNullException("reader"); |
||||
|
||||
if (reader.Token == JsonToken.Null) |
||||
return null; |
||||
|
||||
var value = new Vector3(); |
||||
reader.ReadArrayBegin(); |
||||
|
||||
int idx = 0; |
||||
while (reader.Token != JsonToken.EndOfArray) |
||||
value[idx++] = reader.ReadSingle(); |
||||
|
||||
reader.ReadArrayEnd(nextToken: false); |
||||
|
||||
return value; |
||||
} |
||||
|
||||
public override void Serialize(IJsonWriter writer, object value) |
||||
{ |
||||
if (writer == null) throw new ArgumentNullException("writer"); |
||||
if (value == null) throw new ArgumentNullException("value"); |
||||
|
||||
var vector3 = (Vector3)value; |
||||
writer.WriteArrayBegin(3); |
||||
writer.Write(vector3.x); |
||||
writer.Write(vector3.y); |
||||
writer.Write(vector3.z); |
||||
writer.WriteArrayEnd(); |
||||
} |
||||
} |
||||
|
||||
// https://github.com/neuecc/MessagePack-CSharp/blob/13c299a5172c60154bae53395612af194c02d286/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Formatters.cs#L102 |
||||
public sealed class Vector4Serializer : TypeSerializer |
||||
{ |
||||
public override Type SerializedType { get { return typeof(Vector4); } } |
||||
|
||||
public override object Deserialize(IJsonReader reader) |
||||
{ |
||||
if (reader == null) throw new ArgumentNullException("reader"); |
||||
|
||||
if (reader.Token == JsonToken.Null) |
||||
return null; |
||||
|
||||
var value = new Vector4(); |
||||
reader.ReadArrayBegin(); |
||||
|
||||
int idx = 0; |
||||
while (reader.Token != JsonToken.EndOfArray) |
||||
value[idx++] = reader.ReadSingle(); |
||||
|
||||
reader.ReadArrayEnd(nextToken: false); |
||||
|
||||
return value; |
||||
} |
||||
public override void Serialize(IJsonWriter writer, object value) |
||||
{ |
||||
if (writer == null) throw new ArgumentNullException("writer"); |
||||
if (value == null) throw new ArgumentNullException("value"); |
||||
|
||||
var vector4 = (Vector4)value; |
||||
writer.WriteArrayBegin(4); |
||||
writer.Write(vector4.x); |
||||
writer.Write(vector4.y); |
||||
writer.Write(vector4.z); |
||||
writer.Write(vector4.z); |
||||
writer.WriteArrayEnd(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 1fe4434eade98e547aca1e026a4dbcc3 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,178 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using BestHTTP.Examples; |
||||
using BestHTTP.Examples.Helpers; |
||||
using BestHTTP.SignalRCore; |
||||
using BestHTTP.SignalRCore.Encoders; |
||||
using System; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
/// <summary> |
||||
/// A sample to demonstrate Bearer token authorization on the server. The client will connect to the /redirect route |
||||
/// where it will receive the token and will receive the new url (/HubWithAuthorization) to connect to. |
||||
/// HubWithAuthorization without the token would throw an error. |
||||
/// </summary> |
||||
public sealed class HubWithAuthorizationSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[SerializeField] |
||||
private string _path = "/redirect"; |
||||
|
||||
[SerializeField] |
||||
private ScrollRect _scrollRect; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _contentRoot; |
||||
|
||||
[SerializeField] |
||||
private TextListItem _listItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private int _maxListItemEntries = 100; |
||||
|
||||
[SerializeField] |
||||
private Button _connectButton; |
||||
|
||||
[SerializeField] |
||||
private Button _closeButton; |
||||
|
||||
#pragma warning restore |
||||
|
||||
// Instance of the HubConnection |
||||
HubConnection hub; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
if (hub != null) |
||||
hub.StartClose(); |
||||
} |
||||
|
||||
public void OnConnectButton() |
||||
{ |
||||
// Server side of this example can be found here: |
||||
// https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/ |
||||
|
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
try |
||||
{ |
||||
MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( |
||||
MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, |
||||
MessagePack.Unity.UnityResolver.Instance, |
||||
//MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, |
||||
//MessagePack.Resolvers.StandardResolver.Instance, |
||||
MessagePack.Resolvers.ContractlessStandardResolver.Instance |
||||
); |
||||
|
||||
var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); |
||||
MessagePack.MessagePackSerializer.DefaultOptions = options; |
||||
} |
||||
catch |
||||
{ } |
||||
#endif |
||||
|
||||
IProtocol protocol = null; |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
protocol = new MessagePackCSharpProtocol(); |
||||
#elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK |
||||
protocol = new MessagePackProtocol(); |
||||
#else |
||||
protocol = new JsonProtocol(new LitJsonEncoder()); |
||||
#endif |
||||
|
||||
// Crete the HubConnection |
||||
hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._path), protocol); |
||||
|
||||
// Subscribe to hub events |
||||
hub.OnConnected += Hub_OnConnected; |
||||
hub.OnError += Hub_OnError; |
||||
hub.OnClosed += Hub_OnClosed; |
||||
|
||||
hub.OnRedirected += Hub_Redirected; |
||||
|
||||
hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport(<color=green>{0}</color>) event: <color=green>{1}</color>", transport.TransportType, ev)); |
||||
|
||||
// And finally start to connect to the server |
||||
hub.StartConnect(); |
||||
|
||||
AddText("StartConnect called"); |
||||
SetButtons(false, false); |
||||
} |
||||
|
||||
public void OnCloseButton() |
||||
{ |
||||
if (hub != null) |
||||
{ |
||||
hub.StartClose(); |
||||
|
||||
AddText("StartClose called"); |
||||
SetButtons(false, false); |
||||
} |
||||
} |
||||
|
||||
private void Hub_Redirected(HubConnection hub, Uri oldUri, Uri newUri) |
||||
{ |
||||
AddText(string.Format("Hub connection redirected to '<color=green>{0}</color>' with Access Token: '<color=green>{1}</color>'", hub.Uri, hub.NegotiationResult.AccessToken)); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. |
||||
/// </summary> |
||||
private void Hub_OnConnected(HubConnection hub) |
||||
{ |
||||
AddText(string.Format("Hub Connected with <color=green>{0}</color> transport using the <color=green>{1}</color> encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); |
||||
SetButtons(false, true); |
||||
|
||||
// Call a parameterless function. We expect a string return value. |
||||
hub.Invoke<string>("Echo", "Message from the client") |
||||
.OnSuccess(ret => AddText(string.Format("'<color=green>Echo</color>' returned: '<color=yellow>{0}</color>'", ret)).AddLeftPadding(20)); |
||||
|
||||
AddText("'<color=green>Message from the client</color>' sent!") |
||||
.AddLeftPadding(20); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This is called when the hub is closed after a StartClose() call. |
||||
/// </summary> |
||||
private void Hub_OnClosed(HubConnection hub) |
||||
{ |
||||
AddText("Hub Closed"); |
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. |
||||
/// </summary> |
||||
private void Hub_OnError(HubConnection hub, string error) |
||||
{ |
||||
AddText(string.Format("Hub Error: <color=red>{0}</color>", error)); |
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
private void SetButtons(bool connect, bool close) |
||||
{ |
||||
if (this._connectButton != null) |
||||
this._connectButton.interactable = connect; |
||||
|
||||
if (this._closeButton != null) |
||||
this._closeButton.interactable = close; |
||||
} |
||||
|
||||
private TextListItem AddText(string text) |
||||
{ |
||||
return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: a0d6f71c9108b3b419ee412fa4ddd018 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,307 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using BestHTTP; |
||||
using BestHTTP.Connections; |
||||
using BestHTTP.Examples.Helpers; |
||||
using BestHTTP.SignalRCore; |
||||
using BestHTTP.SignalRCore.Encoders; |
||||
using System; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
public sealed class HubWithPreAuthorizationSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[SerializeField] |
||||
private string _hubPath = "/HubWithAuthorization"; |
||||
|
||||
[SerializeField] |
||||
private string _jwtTokenPath = "/generateJwtToken"; |
||||
|
||||
[SerializeField] |
||||
private ScrollRect _scrollRect; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _contentRoot; |
||||
|
||||
[SerializeField] |
||||
private TextListItem _listItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private int _maxListItemEntries = 100; |
||||
|
||||
[SerializeField] |
||||
private Button _connectButton; |
||||
|
||||
[SerializeField] |
||||
private Button _closeButton; |
||||
|
||||
#pragma warning restore |
||||
|
||||
// Instance of the HubConnection |
||||
HubConnection hub; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
if (hub != null) |
||||
hub.StartClose(); |
||||
} |
||||
|
||||
public void OnConnectButton() |
||||
{ |
||||
// Server side of this example can be found here: |
||||
// https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/ |
||||
|
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
try |
||||
{ |
||||
MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( |
||||
MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, |
||||
MessagePack.Unity.UnityResolver.Instance, |
||||
//MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, |
||||
//MessagePack.Resolvers.StandardResolver.Instance, |
||||
MessagePack.Resolvers.ContractlessStandardResolver.Instance |
||||
); |
||||
|
||||
var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); |
||||
MessagePack.MessagePackSerializer.DefaultOptions = options; |
||||
} |
||||
catch |
||||
{ } |
||||
#endif |
||||
|
||||
IProtocol protocol = null; |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
protocol = new MessagePackCSharpProtocol(); |
||||
#elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK |
||||
protocol = new MessagePackProtocol(); |
||||
#else |
||||
protocol = new JsonProtocol(new LitJsonEncoder()); |
||||
#endif |
||||
|
||||
// Crete the HubConnection |
||||
hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._hubPath), protocol); |
||||
|
||||
hub.AuthenticationProvider = new PreAuthAccessTokenAuthenticator(new Uri(base.sampleSelector.BaseURL + this._jwtTokenPath)); |
||||
|
||||
hub.AuthenticationProvider.OnAuthenticationSucceded += AuthenticationProvider_OnAuthenticationSucceded; |
||||
hub.AuthenticationProvider.OnAuthenticationFailed += AuthenticationProvider_OnAuthenticationFailed; |
||||
|
||||
// Subscribe to hub events |
||||
hub.OnConnected += Hub_OnConnected; |
||||
hub.OnError += Hub_OnError; |
||||
hub.OnClosed += Hub_OnClosed; |
||||
|
||||
hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport(<color=green>{0}</color>) event: <color=green>{1}</color>", transport.TransportType, ev)); |
||||
|
||||
// And finally start to connect to the server |
||||
hub.StartConnect(); |
||||
|
||||
AddText("StartConnect called"); |
||||
SetButtons(false, false); |
||||
} |
||||
|
||||
public void OnCloseButton() |
||||
{ |
||||
if (hub != null) |
||||
{ |
||||
hub.StartClose(); |
||||
|
||||
AddText("StartClose called"); |
||||
SetButtons(false, false); |
||||
} |
||||
} |
||||
|
||||
private void AuthenticationProvider_OnAuthenticationSucceded(IAuthenticationProvider provider) |
||||
{ |
||||
string str = string.Format("Pre-Authentication Succeded! Token: '<color=green>{0}</color>' ", (hub.AuthenticationProvider as PreAuthAccessTokenAuthenticator).Token); |
||||
|
||||
AddText(str); |
||||
} |
||||
|
||||
private void AuthenticationProvider_OnAuthenticationFailed(IAuthenticationProvider provider, string reason) |
||||
{ |
||||
AddText(string.Format("Authentication Failed! Reason: '{0}'", reason)); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. |
||||
/// </summary> |
||||
private void Hub_OnConnected(HubConnection hub) |
||||
{ |
||||
AddText(string.Format("Hub Connected with <color=green>{0}</color> transport using the <color=green>{1}</color> encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); |
||||
SetButtons(false, true); |
||||
|
||||
// Call a parameterless function. We expect a string return value. |
||||
hub.Invoke<string>("Echo", "Message from the client") |
||||
.OnSuccess(ret => AddText(string.Format("'<color=green>Echo</color>' returned: '<color=yellow>{0}</color>'", ret)).AddLeftPadding(20)); |
||||
|
||||
AddText("'<color=green>Message from the client</color>' sent!") |
||||
.AddLeftPadding(20); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This is called when the hub is closed after a StartClose() call. |
||||
/// </summary> |
||||
private void Hub_OnClosed(HubConnection hub) |
||||
{ |
||||
AddText("Hub Closed"); |
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. |
||||
/// </summary> |
||||
private void Hub_OnError(HubConnection hub, string error) |
||||
{ |
||||
AddText(string.Format("Hub Error: <color=red>{0}</color>", error)); |
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
private void SetButtons(bool connect, bool close) |
||||
{ |
||||
if (this._connectButton != null) |
||||
this._connectButton.interactable = connect; |
||||
|
||||
if (this._closeButton != null) |
||||
this._closeButton.interactable = close; |
||||
} |
||||
|
||||
private TextListItem AddText(string text) |
||||
{ |
||||
return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); |
||||
} |
||||
} |
||||
|
||||
public sealed class PreAuthAccessTokenAuthenticator : IAuthenticationProvider |
||||
{ |
||||
/// <summary> |
||||
/// No pre-auth step required for this type of authentication |
||||
/// </summary> |
||||
public bool IsPreAuthRequired { get { return true; } } |
||||
|
||||
#pragma warning disable 0067 |
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; |
||||
|
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationFailedDelegate OnAuthenticationFailed; |
||||
|
||||
#pragma warning restore 0067 |
||||
|
||||
public string Token { get; private set; } |
||||
|
||||
private Uri authenticationUri; |
||||
|
||||
private HTTPRequest authenticationRequest; |
||||
private bool isCancellationRequested; |
||||
|
||||
public PreAuthAccessTokenAuthenticator(Uri authUri) |
||||
{ |
||||
this.authenticationUri = authUri; |
||||
} |
||||
|
||||
public void StartAuthentication() |
||||
{ |
||||
this.authenticationRequest = new HTTPRequest(this.authenticationUri, OnAuthenticationRequestFinished); |
||||
this.authenticationRequest.Send(); |
||||
} |
||||
|
||||
private void OnAuthenticationRequestFinished(HTTPRequest req, HTTPResponse resp) |
||||
{ |
||||
switch (req.State) |
||||
{ |
||||
// The request finished without any problem. |
||||
case HTTPRequestStates.Finished: |
||||
if (resp.IsSuccess) |
||||
{ |
||||
this.authenticationRequest = null; |
||||
this.Token = resp.DataAsText; |
||||
if (this.OnAuthenticationSucceded != null) |
||||
this.OnAuthenticationSucceded(this); |
||||
} |
||||
else // Internal server error? |
||||
AuthenticationFailed(string.Format("Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", |
||||
resp.StatusCode, |
||||
resp.Message, |
||||
resp.DataAsText)); |
||||
break; |
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error. |
||||
case HTTPRequestStates.Error: |
||||
AuthenticationFailed("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "" + req.Exception.StackTrace) : "No Exception")); |
||||
break; |
||||
|
||||
// The request aborted, initiated by the user. |
||||
case HTTPRequestStates.Aborted: |
||||
AuthenticationFailed("Request Aborted!"); |
||||
break; |
||||
|
||||
// Connecting to the server is timed out. |
||||
case HTTPRequestStates.ConnectionTimedOut: |
||||
AuthenticationFailed("Connection Timed Out!"); |
||||
break; |
||||
|
||||
// The request didn't finished in the given time. |
||||
case HTTPRequestStates.TimedOut: |
||||
AuthenticationFailed("Processing the request Timed Out!"); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
private void AuthenticationFailed(string reason) |
||||
{ |
||||
this.authenticationRequest = null; |
||||
|
||||
if (this.isCancellationRequested) |
||||
return; |
||||
|
||||
if (this.OnAuthenticationFailed != null) |
||||
this.OnAuthenticationFailed(this, reason); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Prepares the request by adding two headers to it |
||||
/// </summary> |
||||
public void PrepareRequest(BestHTTP.HTTPRequest request) |
||||
{ |
||||
if (HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri) == SupportedProtocols.HTTP) |
||||
request.Uri = PrepareUri(request.Uri); |
||||
} |
||||
|
||||
public Uri PrepareUri(Uri uri) |
||||
{ |
||||
if (!string.IsNullOrEmpty(this.Token)) |
||||
{ |
||||
string query = string.IsNullOrEmpty(uri.Query) ? "?" : uri.Query + "&"; |
||||
UriBuilder uriBuilder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, query + "access_token=" + this.Token); |
||||
return uriBuilder.Uri; |
||||
} |
||||
else |
||||
return uri; |
||||
} |
||||
|
||||
public void Cancel() |
||||
{ |
||||
this.isCancellationRequested = true; |
||||
if (this.authenticationRequest != null) |
||||
this.authenticationRequest.Abort(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: bfb23ec912f317944b34485c85ab6b31 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,34 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
public enum PersonStates |
||||
{ |
||||
Unknown, |
||||
Joined |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Helper class to demonstrate strongly typed callbacks |
||||
/// </summary> |
||||
internal sealed class Person |
||||
{ |
||||
public UnityEngine.Vector3[] Positions { get; set; } |
||||
public string Name { get; set; } |
||||
public long Age { get; set; } |
||||
public DateTime Joined { get; set; } |
||||
public PersonStates State { get; set; } |
||||
public List<Person> Friends { get; set; } |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("[Person Name: '{0}', Age: '<color=yellow>{1}</color>', Joined: {2}, State: {3}, Friends: {4}, Position: {5}]", |
||||
this.Name, this.Age, this.Joined, this.State, this.Friends != null ? this.Friends.Count : 0, this.Positions != null ? this.Positions.Length : 0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 2f03f1530cd505e4d9b16a6b4f2e89c3 |
||||
timeCreated: 1577715217 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,240 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using BestHTTP; |
||||
using BestHTTP.Connections; |
||||
using BestHTTP.Examples.Helpers; |
||||
using BestHTTP.SignalRCore; |
||||
using BestHTTP.SignalRCore.Encoders; |
||||
using System; |
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
/// <summary> |
||||
/// This sample demonstrates redirection capabilities. The server will redirect a few times the client before |
||||
/// routing it to the final endpoint. |
||||
/// </summary> |
||||
public sealed class RedirectSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[SerializeField] |
||||
private string _path = "/redirect_sample"; |
||||
|
||||
[SerializeField] |
||||
private ScrollRect _scrollRect; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _contentRoot; |
||||
|
||||
[SerializeField] |
||||
private TextListItem _listItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private int _maxListItemEntries = 100; |
||||
|
||||
[SerializeField] |
||||
private Button _connectButton; |
||||
|
||||
[SerializeField] |
||||
private Button _closeButton; |
||||
|
||||
#pragma warning restore |
||||
|
||||
// Instance of the HubConnection |
||||
public HubConnection hub; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
if (hub != null) |
||||
{ |
||||
hub.StartClose(); |
||||
} |
||||
} |
||||
|
||||
public void OnConnectButton() |
||||
{ |
||||
// Server side of this example can be found here: |
||||
// https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/ |
||||
|
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
try |
||||
{ |
||||
MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( |
||||
MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, |
||||
MessagePack.Unity.UnityResolver.Instance, |
||||
//MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, |
||||
//MessagePack.Resolvers.StandardResolver.Instance, |
||||
MessagePack.Resolvers.ContractlessStandardResolver.Instance |
||||
); |
||||
|
||||
var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); |
||||
MessagePack.MessagePackSerializer.DefaultOptions = options; |
||||
} |
||||
catch |
||||
{ } |
||||
#endif |
||||
|
||||
IProtocol protocol = null; |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
protocol = new MessagePackCSharpProtocol(); |
||||
#elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK |
||||
protocol = new MessagePackProtocol(); |
||||
#else |
||||
protocol = new JsonProtocol(new LitJsonEncoder()); |
||||
#endif |
||||
|
||||
// Crete the HubConnection |
||||
hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._path), protocol); |
||||
hub.AuthenticationProvider = new RedirectLoggerAccessTokenAuthenticator(hub); |
||||
|
||||
// Subscribe to hub events |
||||
hub.OnConnected += Hub_OnConnected; |
||||
hub.OnError += Hub_OnError; |
||||
hub.OnClosed += Hub_OnClosed; |
||||
|
||||
hub.OnRedirected += Hub_Redirected; |
||||
|
||||
hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport(<color=green>{0}</color>) event: <color=green>{1}</color>", transport.TransportType, ev)); |
||||
|
||||
// And finally start to connect to the server |
||||
hub.StartConnect(); |
||||
|
||||
AddText("StartConnect called"); |
||||
SetButtons(false, false); |
||||
} |
||||
|
||||
public void OnCloseButton() |
||||
{ |
||||
if (hub != null) |
||||
{ |
||||
AddText("Calling StartClose"); |
||||
|
||||
hub.StartClose(); |
||||
|
||||
SetButtons(false, false); |
||||
} |
||||
} |
||||
|
||||
private void Hub_Redirected(HubConnection hub, Uri oldUri, Uri newUri) |
||||
{ |
||||
AddText(string.Format("Hub connection redirected to '<color=green>{0}</color>'!", hub.Uri)); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. |
||||
/// </summary> |
||||
private void Hub_OnConnected(HubConnection hub) |
||||
{ |
||||
AddText(string.Format("Hub Connected with <color=green>{0}</color> transport using the <color=green>{1}</color> encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); |
||||
|
||||
// Call a parameterless function. We expect a string return value. |
||||
hub.Invoke<string>("Echo", "Message from the client") |
||||
.OnSuccess(ret => AddText(string.Format(" '<color=green>Echo</color>' returned: '<color=yellow>{0}</color>'", ret))); |
||||
|
||||
SetButtons(false, true); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This is called when the hub is closed after a StartClose() call. |
||||
/// </summary> |
||||
private void Hub_OnClosed(HubConnection hub) |
||||
{ |
||||
AddText("Hub Closed"); |
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. |
||||
/// </summary> |
||||
private void Hub_OnError(HubConnection hub, string error) |
||||
{ |
||||
AddText(string.Format("Hub Error: <color=red>{0}</color>", error)); |
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
private void SetButtons(bool connect, bool close) |
||||
{ |
||||
if (this._connectButton != null) |
||||
this._connectButton.interactable = connect; |
||||
|
||||
if (this._closeButton != null) |
||||
this._closeButton.interactable = close; |
||||
} |
||||
|
||||
private void AddText(string text) |
||||
{ |
||||
GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); |
||||
} |
||||
} |
||||
|
||||
public sealed class RedirectLoggerAccessTokenAuthenticator : IAuthenticationProvider |
||||
{ |
||||
/// <summary> |
||||
/// No pre-auth step required for this type of authentication |
||||
/// </summary> |
||||
public bool IsPreAuthRequired { get { return false; } } |
||||
|
||||
#pragma warning disable 0067 |
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; |
||||
|
||||
/// <summary> |
||||
/// Not used event as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public event OnAuthenticationFailedDelegate OnAuthenticationFailed; |
||||
|
||||
#pragma warning restore 0067 |
||||
|
||||
private HubConnection _connection; |
||||
|
||||
public RedirectLoggerAccessTokenAuthenticator(HubConnection connection) |
||||
{ |
||||
this._connection = connection; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Not used as IsPreAuthRequired is false |
||||
/// </summary> |
||||
public void StartAuthentication() |
||||
{ } |
||||
|
||||
/// <summary> |
||||
/// Prepares the request by adding two headers to it |
||||
/// </summary> |
||||
public void PrepareRequest(BestHTTP.HTTPRequest request) |
||||
{ |
||||
request.SetHeader("x-redirect-count", _connection.RedirectCount.ToString()); |
||||
|
||||
if (HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri) == SupportedProtocols.HTTP) |
||||
request.Uri = PrepareUri(request.Uri); |
||||
} |
||||
|
||||
public Uri PrepareUri(Uri uri) |
||||
{ |
||||
if (this._connection.NegotiationResult != null && !string.IsNullOrEmpty(this._connection.NegotiationResult.AccessToken)) |
||||
{ |
||||
string query = string.IsNullOrEmpty(uri.Query) ? "?" : uri.Query + "&"; |
||||
UriBuilder uriBuilder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, query + "access_token=" + this._connection.NegotiationResult.AccessToken); |
||||
return uriBuilder.Uri; |
||||
} |
||||
else |
||||
return uri; |
||||
} |
||||
|
||||
public void Cancel() |
||||
{ } |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: febf35a5598c76843a7573e8f650079e |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,242 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using System; |
||||
using UnityEngine; |
||||
using BestHTTP.SignalRCore; |
||||
using BestHTTP.SignalRCore.Encoders; |
||||
using UnityEngine.UI; |
||||
using BestHTTP.Examples.Helpers; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
public enum MyEnum : int |
||||
{ |
||||
None, |
||||
One, |
||||
Two |
||||
} |
||||
|
||||
public sealed class Metadata |
||||
{ |
||||
public string strData; |
||||
public int intData; |
||||
public MyEnum myEnum; |
||||
} |
||||
|
||||
// Server side of this example can be found here: |
||||
// https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/TestHub.cs |
||||
public class TestHubSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
[SerializeField] |
||||
private string _path = "/TestHub"; |
||||
|
||||
[SerializeField] |
||||
private ScrollRect _scrollRect; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _contentRoot; |
||||
|
||||
[SerializeField] |
||||
private TextListItem _listItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private int _maxListItemEntries = 100; |
||||
|
||||
[SerializeField] |
||||
private Button _connectButton; |
||||
|
||||
[SerializeField] |
||||
private Button _closeButton; |
||||
|
||||
#pragma warning restore |
||||
|
||||
// Instance of the HubConnection |
||||
HubConnection hub; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
if (hub != null) |
||||
hub.StartClose(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// GUI button callback |
||||
/// </summary> |
||||
public void OnConnectButton() |
||||
{ |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
try |
||||
{ |
||||
MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( |
||||
MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, |
||||
MessagePack.Unity.UnityResolver.Instance, |
||||
//MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, |
||||
//MessagePack.Resolvers.StandardResolver.Instance, |
||||
MessagePack.Resolvers.ContractlessStandardResolver.Instance |
||||
); |
||||
|
||||
var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); |
||||
MessagePack.MessagePackSerializer.DefaultOptions = options; |
||||
} |
||||
catch |
||||
{ } |
||||
#endif |
||||
|
||||
IProtocol protocol = null; |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
protocol = new MessagePackCSharpProtocol(); |
||||
#elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK |
||||
protocol = new MessagePackProtocol(); |
||||
#else |
||||
protocol = new JsonProtocol(new LitJsonEncoder()); |
||||
#endif |
||||
|
||||
// Crete the HubConnection |
||||
hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._path), protocol); |
||||
|
||||
// Optionally add an authenticator |
||||
//hub.AuthenticationProvider = new BestHTTP.SignalRCore.Authentication.HeaderAuthenticator("<generated jwt token goes here>"); |
||||
|
||||
// Subscribe to hub events |
||||
hub.OnConnected += Hub_OnConnected; |
||||
hub.OnError += Hub_OnError; |
||||
hub.OnClosed += Hub_OnClosed; |
||||
|
||||
hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport(<color=green>{0}</color>) event: <color=green>{1}</color>", transport.TransportType, ev)); |
||||
|
||||
// Set up server callable functions |
||||
hub.On("Send", (string arg) => AddText(string.Format("On '<color=green>Send</color>': '<color=yellow>{0}</color>'", arg)).AddLeftPadding(20)); |
||||
hub.On<Person>("Person", (person) => AddText(string.Format("On '<color=green>Person</color>': '<color=yellow>{0}</color>'", person)).AddLeftPadding(20)); |
||||
hub.On<Person, Person>("TwoPersons", (person1, person2) => AddText(string.Format("On '<color=green>TwoPersons</color>': '<color=yellow>{0}</color>', '<color=yellow>{1}</color>'", person1, person2)).AddLeftPadding(20)); |
||||
|
||||
// And finally start to connect to the server |
||||
hub.StartConnect(); |
||||
|
||||
AddText("StartConnect called"); |
||||
|
||||
SetButtons(false, false); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// GUI button callback |
||||
/// </summary> |
||||
public void OnCloseButton() |
||||
{ |
||||
if (this.hub != null) |
||||
{ |
||||
this.hub.StartClose(); |
||||
|
||||
AddText("StartClose called"); |
||||
SetButtons(false, false); |
||||
} |
||||
} |
||||
|
||||
|
||||
/// <summary> |
||||
/// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. |
||||
/// </summary> |
||||
private void Hub_OnConnected(HubConnection hub) |
||||
{ |
||||
SetButtons(false, true); |
||||
AddText(string.Format("Hub Connected with <color=green>{0}</color> transport using the <color=green>{1}</color> encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); |
||||
|
||||
hub.Send("SendMetadata", new Metadata() { intData = 123, strData = "meta data", myEnum = MyEnum.One }); |
||||
|
||||
// Call a server function with a string param. We expect no return value. |
||||
hub.Send("Send", "my message"); |
||||
|
||||
// Call a parameterless function. We expect a string return value. |
||||
hub.Invoke<string>("NoParam") |
||||
.OnSuccess(ret => AddText(string.Format("'<color=green>NoParam</color>' returned: '<color=yellow>{0}</color>'", ret)).AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>NoParam</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// Call a function on the server to add two numbers. OnSuccess will be called with the result and OnError if there's an error. |
||||
hub.Invoke<int>("Add", 10, 20) |
||||
.OnSuccess(result => AddText(string.Format("'<color=green>Add(10, 20)</color>' returned: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>Add(10, 20)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
hub.Invoke<int?>("NullableTest", 10) |
||||
.OnSuccess(result => AddText(string.Format("'<color=green>NullableTest(10)</color>' returned: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>NullableTest(10)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// Call a function that will return a Person object constructed from the function's parameters. |
||||
hub.Invoke<Person>("GetPerson", "Mr. Smith", 26) |
||||
.OnSuccess(result => AddText(string.Format("'<color=green>GetPerson(\"Mr. Smith\", 26)</color>' returned: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>GetPerson(\"Mr. Smith\", 26)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// To test errors/exceptions this call always throws an exception on the server side resulting in an OnError call. |
||||
// OnError expected here! |
||||
hub.Invoke<int>("SingleResultFailure", 10, 20) |
||||
.OnSuccess(result => AddText(string.Format("'<color=green>SingleResultFailure(10, 20)</color>' returned: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>SingleResultFailure(10, 20)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// This call demonstrates IEnumerable<> functions, result will be the yielded numbers. |
||||
hub.Invoke<int[]>("Batched", 10) |
||||
.OnSuccess(result => AddText(string.Format("'<color=green>Batched(10)</color>' returned items: '<color=yellow>{0}</color>'", result.Length)).AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>Batched(10)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// OnItem is called for a streaming request for every items returned by the server. OnSuccess will still be called with all the items. |
||||
hub.GetDownStreamController<int>("ObservableCounter", 10, 1000) |
||||
.OnItem(result => AddText(string.Format("'<color=green>ObservableCounter(10, 1000)</color>' OnItem: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnSuccess(result => AddText("'<color=green>ObservableCounter(10, 1000)</color>' OnSuccess.").AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>ObservableCounter(10, 1000)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// A stream request can be cancelled any time. |
||||
var controller = hub.GetDownStreamController<int>("ChannelCounter", 10, 1000); |
||||
|
||||
controller.OnItem(result => AddText(string.Format("'<color=green>ChannelCounter(10, 1000)</color>' OnItem: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnSuccess(result => AddText("'<color=green>ChannelCounter(10, 1000)</color>' OnSuccess.").AddLeftPadding(20)) |
||||
.OnError(error => AddText(string.Format("'<color=green>ChannelCounter(10, 1000)</color>' error: '<color=red>{0}</color>'", error)).AddLeftPadding(20)); |
||||
|
||||
// a stream can be cancelled by calling the controller's Cancel method |
||||
controller.Cancel(); |
||||
|
||||
// This call will stream strongly typed objects |
||||
hub.GetDownStreamController<Person>("GetRandomPersons", 20, 2000) |
||||
.OnItem(result => AddText(string.Format("'<color=green>GetRandomPersons(20, 1000)</color>' OnItem: '<color=yellow>{0}</color>'", result)).AddLeftPadding(20)) |
||||
.OnSuccess(result => AddText("'<color=green>GetRandomPersons(20, 1000)</color>' OnSuccess.").AddLeftPadding(20)); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This is called when the hub is closed after a StartClose() call. |
||||
/// </summary> |
||||
private void Hub_OnClosed(HubConnection hub) |
||||
{ |
||||
SetButtons(true, false); |
||||
AddText("Hub Closed"); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. |
||||
/// </summary> |
||||
private void Hub_OnError(HubConnection hub, string error) |
||||
{ |
||||
SetButtons(true, false); |
||||
AddText(string.Format("Hub Error: <color=red>{0}</color>", error)); |
||||
} |
||||
|
||||
private void SetButtons(bool connect, bool close) |
||||
{ |
||||
if (this._connectButton != null) |
||||
this._connectButton.interactable = connect; |
||||
|
||||
if (this._closeButton != null) |
||||
this._closeButton.interactable = close; |
||||
} |
||||
|
||||
private TextListItem AddText(string text) |
||||
{ |
||||
return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2 |
||||
guid: 6bd28a72bf1727542937e09ab435abcc |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,404 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
||||
|
||||
using BestHTTP; |
||||
using BestHTTP.Examples.Helpers; |
||||
using BestHTTP.SignalRCore; |
||||
using BestHTTP.SignalRCore.Encoders; |
||||
|
||||
using System; |
||||
using System.Collections; |
||||
|
||||
using UnityEngine; |
||||
using UnityEngine.UI; |
||||
|
||||
namespace BestHTTP.Examples |
||||
{ |
||||
/// <summary> |
||||
/// This sample demonstrates redirection capabilities. The server will redirect a few times the client before |
||||
/// routing it to the final endpoint. |
||||
/// </summary> |
||||
public sealed class UploadHubSample : BestHTTP.Examples.Helpers.SampleBase |
||||
{ |
||||
#pragma warning disable 0649 |
||||
|
||||
[SerializeField] |
||||
private string _path = "/uploading"; |
||||
|
||||
[SerializeField] |
||||
private ScrollRect _scrollRect; |
||||
|
||||
[SerializeField] |
||||
private RectTransform _contentRoot; |
||||
|
||||
[SerializeField] |
||||
private TextListItem _listItemPrefab; |
||||
|
||||
[SerializeField] |
||||
private int _maxListItemEntries = 100; |
||||
|
||||
[SerializeField] |
||||
private Button _connectButton; |
||||
|
||||
[SerializeField] |
||||
private Button _closeButton; |
||||
|
||||
[SerializeField] |
||||
private float _yieldWaitTime = 0.1f; |
||||
|
||||
#pragma warning restore |
||||
|
||||
// Instance of the HubConnection |
||||
private HubConnection hub; |
||||
|
||||
protected override void Start() |
||||
{ |
||||
base.Start(); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
void OnDestroy() |
||||
{ |
||||
if (hub != null) |
||||
{ |
||||
hub.StartClose(); |
||||
} |
||||
} |
||||
|
||||
public void OnConnectButton() |
||||
{ |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
try |
||||
{ |
||||
MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( |
||||
MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, |
||||
MessagePack.Unity.UnityResolver.Instance, |
||||
//MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, |
||||
//MessagePack.Resolvers.StandardResolver.Instance, |
||||
MessagePack.Resolvers.ContractlessStandardResolver.Instance |
||||
); |
||||
|
||||
var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); |
||||
MessagePack.MessagePackSerializer.DefaultOptions = options; |
||||
} |
||||
catch |
||||
{ } |
||||
#endif |
||||
|
||||
IProtocol protocol = null; |
||||
#if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP |
||||
protocol = new MessagePackCSharpProtocol(); |
||||
#elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK |
||||
protocol = new MessagePackProtocol(); |
||||
#else |
||||
protocol = new JsonProtocol(new LitJsonEncoder()); |
||||
#endif |
||||
|
||||
// Crete the HubConnection |
||||
hub = new HubConnection(new Uri(this.sampleSelector.BaseURL + this._path), protocol); |
||||
|
||||
// Subscribe to hub events |
||||
hub.OnConnected += Hub_OnConnected; |
||||
hub.OnError += Hub_OnError; |
||||
hub.OnClosed += Hub_OnClosed; |
||||
|
||||
hub.OnRedirected += Hub_Redirected; |
||||
|
||||
hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport(<color=green>{0}</color>) event: <color=green>{1}</color>", transport.TransportType, ev)); |
||||
|
||||
// And finally start to connect to the server |
||||
hub.StartConnect(); |
||||
|
||||
AddText("StartConnect called"); |
||||
|
||||
SetButtons(false, false); |
||||
} |
||||
|
||||
public void OnCloseButton() |
||||
{ |
||||
if (this.hub != null) |
||||
{ |
||||
this.hub.StartClose(); |
||||
|
||||
AddText("StartClose called"); |
||||
SetButtons(false, false); |
||||
} |
||||
} |
||||
|
||||
private void Hub_Redirected(HubConnection hub, Uri oldUri, Uri newUri) |
||||
{ |
||||
AddText(string.Format("Hub connection redirected to '<color=green>{0}</color>'!", hub.Uri)); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. |
||||
/// </summary> |
||||
private void Hub_OnConnected(HubConnection hub) |
||||
{ |
||||
AddText(string.Format("Hub Connected with <color=green>{0}</color> transport using the <color=green>{1}</color> encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); |
||||
|
||||
StartCoroutine(UploadWord()); |
||||
|
||||
SetButtons(false, true); |
||||
} |
||||
|
||||
private IEnumerator UploadWord() |
||||
{ |
||||
AddText("<color=green>UploadWord</color>:"); |
||||
|
||||
var controller = hub.GetUpStreamController<string, string>("UploadWord"); |
||||
controller.OnSuccess(result => |
||||
{ |
||||
AddText(string.Format("UploadWord completed, result: '<color=yellow>{0}</color>'", result)) |
||||
.AddLeftPadding(20); |
||||
AddText(""); |
||||
|
||||
StartCoroutine(ScoreTracker()); |
||||
}); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
controller.UploadParam("Hello "); |
||||
|
||||
AddText("'<color=green>Hello </color>' uploaded!") |
||||
.AddLeftPadding(20); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
controller.UploadParam("World"); |
||||
|
||||
AddText("'<color=green>World</color>' uploaded!") |
||||
.AddLeftPadding(20); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
controller.UploadParam("!!"); |
||||
|
||||
AddText("'<color=green>!!</color>' uploaded!") |
||||
.AddLeftPadding(20); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
|
||||
controller.Finish(); |
||||
|
||||
AddText("Sent upload finished message.") |
||||
.AddLeftPadding(20); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
} |
||||
|
||||
private IEnumerator ScoreTracker() |
||||
{ |
||||
AddText("<color=green>ScoreTracker</color>:"); |
||||
var controller = hub.GetUpStreamController<string, int, int>("ScoreTracker"); |
||||
|
||||
controller.OnSuccess(result => |
||||
{ |
||||
AddText(string.Format("ScoreTracker completed, result: '<color=yellow>{0}</color>'", result)) |
||||
.AddLeftPadding(20); |
||||
AddText(""); |
||||
|
||||
StartCoroutine(ScoreTrackerWithParameterChannels()); |
||||
}); |
||||
|
||||
const int numScores = 5; |
||||
for (int i = 0; i < numScores; i++) |
||||
{ |
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
|
||||
int p1 = UnityEngine.Random.Range(0, 10); |
||||
int p2 = UnityEngine.Random.Range(0, 10); |
||||
controller.UploadParam(p1, p2); |
||||
|
||||
AddText(string.Format("Score({0}/{1}) uploaded! p1's score: <color=green>{2}</color> p2's score: <color=green>{3}</color>", i + 1, numScores, p1, p2)) |
||||
.AddLeftPadding(20); |
||||
} |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
controller.Finish(); |
||||
|
||||
AddText("Sent upload finished message.") |
||||
.AddLeftPadding(20); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
} |
||||
|
||||
private IEnumerator ScoreTrackerWithParameterChannels() |
||||
{ |
||||
AddText("<color=green>ScoreTracker using upload channels</color>:"); |
||||
|
||||
using (var controller = hub.GetUpStreamController<string, int, int>("ScoreTracker")) |
||||
{ |
||||
controller.OnSuccess(result => |
||||
{ |
||||
AddText(string.Format("ScoreTracker completed, result: '<color=yellow>{0}</color>'", result)) |
||||
.AddLeftPadding(20); |
||||
AddText(""); |
||||
|
||||
StartCoroutine(StreamEcho()); |
||||
}); |
||||
|
||||
const int numScores = 5; |
||||
|
||||
// While the server's ScoreTracker has two parameters, we can upload those parameters separately |
||||
// So here we |
||||
|
||||
using (var player1param = controller.GetUploadChannel<int>(0)) |
||||
{ |
||||
for (int i = 0; i < numScores; i++) |
||||
{ |
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
|
||||
int score = UnityEngine.Random.Range(0, 10); |
||||
player1param.Upload(score); |
||||
|
||||
AddText(string.Format("Player 1's score({0}/{1}) uploaded! Score: <color=green>{2}</color>", i + 1, numScores, score)) |
||||
.AddLeftPadding(20); |
||||
} |
||||
} |
||||
|
||||
AddText(""); |
||||
|
||||
using (var player2param = controller.GetUploadChannel<int>(1)) |
||||
{ |
||||
for (int i = 0; i < numScores; i++) |
||||
{ |
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
|
||||
int score = UnityEngine.Random.Range(0, 10); |
||||
player2param.Upload(score); |
||||
|
||||
AddText(string.Format("Player 2's score({0}/{1}) uploaded! Score: <color=green>{2}</color>", i + 1, numScores, score)) |
||||
.AddLeftPadding(20); |
||||
} |
||||
} |
||||
|
||||
AddText("All scores uploaded!") |
||||
.AddLeftPadding(20); |
||||
} |
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
} |
||||
|
||||
private IEnumerator StreamEcho() |
||||
{ |
||||
AddText("<color=green>StreamEcho</color>:"); |
||||
using (var controller = hub.GetUpAndDownStreamController<string, string>("StreamEcho")) |
||||
{ |
||||
controller.OnSuccess(result => |
||||
{ |
||||
AddText("StreamEcho completed!") |
||||
.AddLeftPadding(20); |
||||
AddText(""); |
||||
|
||||
StartCoroutine(PersonEcho()); |
||||
}); |
||||
|
||||
controller.OnItem(item => |
||||
{ |
||||
AddText(string.Format("Received from server: '<color=yellow>{0}</color>'", item)) |
||||
.AddLeftPadding(20); |
||||
}); |
||||
|
||||
const int numMessages = 5; |
||||
for (int i = 0; i < numMessages; i++) |
||||
{ |
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
|
||||
string message = string.Format("Message from client {0}/{1}", i + 1, numMessages); |
||||
controller.UploadParam(message); |
||||
|
||||
AddText(string.Format("Sent message to the server: <color=green>{0}</color>", message)) |
||||
.AddLeftPadding(20); |
||||
} |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
} |
||||
|
||||
AddText("Upload finished!") |
||||
.AddLeftPadding(20); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This is basically the same as the previous StreamEcho, but it's streaming a complex object (Person |
||||
/// </summary> |
||||
private IEnumerator PersonEcho() |
||||
{ |
||||
AddText("<color=green>PersonEcho</color>:"); |
||||
|
||||
using (var controller = hub.GetUpAndDownStreamController<Person, Person>("PersonEcho")) |
||||
{ |
||||
controller.OnSuccess(result => |
||||
{ |
||||
AddText("PersonEcho completed!") |
||||
.AddLeftPadding(20); |
||||
AddText(""); |
||||
AddText("All Done!"); |
||||
}); |
||||
|
||||
controller.OnItem(item => |
||||
{ |
||||
AddText(string.Format("Received from server: '<color=yellow>{0}</color>'", item)) |
||||
.AddLeftPadding(20); |
||||
}); |
||||
|
||||
const int numMessages = 5; |
||||
for (int i = 0; i < numMessages; i++) |
||||
{ |
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
|
||||
Person person = new Person() |
||||
{ |
||||
Name = "Mr. Smith", |
||||
Age = 20 + i * 2 |
||||
}; |
||||
|
||||
controller.UploadParam(person); |
||||
|
||||
AddText(string.Format("Sent person to the server: <color=green>{0}</color>", person)) |
||||
.AddLeftPadding(20); |
||||
} |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
} |
||||
AddText("Upload finished!") |
||||
.AddLeftPadding(20); |
||||
|
||||
yield return new WaitForSeconds(_yieldWaitTime); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// This is called when the hub is closed after a StartClose() call. |
||||
/// </summary> |
||||
private void Hub_OnClosed(HubConnection hub) |
||||
{ |
||||
AddText("Hub Closed"); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. |
||||
/// </summary> |
||||
private void Hub_OnError(HubConnection hub, string error) |
||||
{ |
||||
AddText(string.Format("Hub Error: <color=red>{0}</color>", error)); |
||||
|
||||
SetButtons(true, false); |
||||
} |
||||
|
||||
private void SetButtons(bool connect, bool close) |
||||
{ |
||||
if (this._connectButton != null) |
||||
this._connectButton.interactable = connect; |
||||
|
||||
if (this._closeButton != null) |
||||
this._closeButton.interactable = close; |
||||
} |
||||
|
||||
private TextListItem AddText(string text) |
||||
{ |
||||
return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2 |
||||
guid: c462c96cbe15002418d1e7a4850a1bb1 |
||||
timeCreated: 1548919763 |
||||
licenseType: Store |
||||
MonoImporter: |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: c66af72c0f511ab44aa91600813e6280 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2 |
||||
guid: fe22d8907a4087949a70b00d13b1b9a7 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
@ -0,0 +1,27 @@
|
||||
#if !BESTHTTP_DISABLE_SOCKETIO |
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace BestHTTP.SocketIO.JsonEncoders |
||||
{ |
||||
/*using Newtonsoft.Json; |
||||
|
||||
/// <summary> |
||||
/// This class uses Newtonsoft's Json encoder (JSON .NET For Unity - http://u3d.as/5q2). |
||||
/// </summary> |
||||
public sealed class JsonDotNetEncoder : IJsonEncoder |
||||
{ |
||||
public List<object> Decode(string json) |
||||
{ |
||||
return JsonConvert.DeserializeObject<List<object>>(json); |
||||
} |
||||
|
||||
public string Encode(List<object> obj) |
||||
{ |
||||
return JsonConvert.SerializeObject(obj); |
||||
} |
||||
}*/ |
||||
} |
||||
|
||||
#endif |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue