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