├── Helpers ├── LinkSelect.png ├── Plugins │ └── WebGL │ │ └── OpenWindow.jslib ├── SelectorUI │ ├── Category.cs │ ├── ExampleListItem.cs │ ├── ExampleInfo.cs │ ├── SampleSelectorUI.cs │ ├── Category.prefab │ ├── ExampleListItem.prefab │ └── ExampleInfo.prefab ├── SampleBase.cs ├── TextListItem.cs ├── Link.cs ├── Components │ ├── Cache.cs │ └── Cookies.cs ├── GUIHelper.cs └── TextListItem.prefab ├── README.md ├── SignalR ├── Json Encoders │ ├── JSonDotnetEncoder.cs │ └── LitJsonEncoder.cs └── Authentication Providers │ ├── SampleHeaderAuthentication.cs │ └── SampleCookieAuthentication.cs ├── SocketIO ├── SocketIO Json Encoders │ ├── JsonDotNetEncoder.cs │ └── LitJsonEncoder.cs └── SocketIOChatSample.cs ├── SignalRCore ├── Person.cs ├── Encoders │ ├── JsonDotNetEncoder.cs │ └── LitJsonEncoder.cs ├── Authentication Providers │ └── HeaderAuthenticator.cs ├── HubWithAuthorizationSample.cs ├── RedirectSample.cs ├── AsyncTestHubSample.cs ├── TestHubSample.cs ├── HubWithPreAuthorizationSample.cs └── UploadHubSample.cs ├── Plugin └── AsyncExtensions.cs ├── HTTP ├── ResumableStreamingSample.cs ├── TextureDownloadSample.cs ├── UploadStream.cs ├── MultipartFormDataStream.cs ├── AssetBundleSample.cs └── StreamingSample.cs ├── SampleRoot.cs ├── Server-Sent Events └── SimpleSample.cs ├── Websocket └── WebSocketSample.cs ├── .gitignore └── SocketIO3 └── ChatSample.cs /Helpers/LinkSelect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Benedicht/BestHTTP_Examples/HEAD/Helpers/LinkSelect.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BestHTTP_Examples 2 | Example sources of the BestHTTP Unity3D plugin (http://u3d.as/5sb). 3 | -------------------------------------------------------------------------------- /Helpers/Plugins/WebGL/OpenWindow.jslib: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on: 3 | * https://github.com/valyard/UnityWebGLOpenLink 4 | */ 5 | 6 | var OpenWindowPlugin = { 7 | openWindow: function(link) 8 | { 9 | var url = Pointer_stringify(link); 10 | 11 | var func = function() 12 | { 13 | window.open(url); 14 | document.removeEventListener('mouseup', func); 15 | } 16 | 17 | document.addEventListener('mouseup', func); 18 | } 19 | }; 20 | 21 | mergeInto(LibraryManager.library, OpenWindowPlugin); -------------------------------------------------------------------------------- /Helpers/SelectorUI/Category.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace BestHTTP.Examples.Helpers.SelectorUI 7 | { 8 | public sealed class Category : MonoBehaviour 9 | { 10 | #pragma warning disable 0649 11 | 12 | [SerializeField] 13 | private Text _text; 14 | 15 | #pragma warning restore 16 | 17 | public void SetLabel(string category) 18 | { 19 | this._text.text = category; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Helpers/SampleBase.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace BestHTTP.Examples.Helpers 4 | { 5 | public abstract class SampleBase : MonoBehaviour 6 | { 7 | [Header("Common Properties")] 8 | public string Category; 9 | public string DisplayName; 10 | 11 | [TextArea] 12 | public string Description; 13 | 14 | public RuntimePlatform[] BannedPlatforms = new RuntimePlatform[0]; 15 | 16 | protected SampleRoot sampleSelector; 17 | 18 | protected virtual void Start() 19 | { 20 | this.sampleSelector = FindObjectOfType(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Helpers/TextListItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | 7 | namespace BestHTTP.Examples.Helpers 8 | { 9 | public class TextListItem : MonoBehaviour 10 | { 11 | #pragma warning disable 0649 12 | [SerializeField] 13 | private Text _text; 14 | #pragma warning restore 15 | 16 | public void SetText(string text) 17 | { 18 | this._text.text = text; 19 | } 20 | 21 | public void AddLeftPadding(int padding) 22 | { 23 | this.GetComponent().padding.left += padding; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SignalR/Json Encoders/JSonDotnetEncoder.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR && BESTHTTP_SIGNALR_WITH_JSONDOTNET 2 | 3 | using System.Collections.Generic; 4 | 5 | using Newtonsoft.Json; 6 | 7 | namespace BestHTTP.SignalR.JsonEncoders 8 | { 9 | public sealed class JSonDotnetEncoder : IJsonEncoder 10 | { 11 | public string Encode(object obj) 12 | { 13 | return JsonConvert.SerializeObject(obj); 14 | } 15 | 16 | public IDictionary DecodeMessage(string json) 17 | { 18 | return JsonConvert.DeserializeObject>(json); 19 | } 20 | } 21 | } 22 | 23 | #endif -------------------------------------------------------------------------------- /SocketIO/SocketIO Json Encoders/JsonDotNetEncoder.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SOCKETIO 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace BestHTTP.SocketIO.JsonEncoders 7 | { 8 | /*using Newtonsoft.Json; 9 | 10 | /// 11 | /// This class uses Newtonsoft's Json encoder (JSON .NET For Unity - http://u3d.as/5q2). 12 | /// 13 | public sealed class JsonDotNetEncoder : IJsonEncoder 14 | { 15 | public List Decode(string json) 16 | { 17 | return JsonConvert.DeserializeObject>(json); 18 | } 19 | 20 | public string Encode(List obj) 21 | { 22 | return JsonConvert.SerializeObject(obj); 23 | } 24 | }*/ 25 | } 26 | 27 | #endif -------------------------------------------------------------------------------- /SignalR/Json Encoders/LitJsonEncoder.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR 2 | 3 | using System.Collections.Generic; 4 | 5 | using BestHTTP.JSON.LitJson; 6 | 7 | namespace BestHTTP.SignalR.JsonEncoders 8 | { 9 | public sealed class LitJsonEncoder : IJsonEncoder 10 | { 11 | public string Encode(object obj) 12 | { 13 | JsonWriter writer = new JsonWriter(); 14 | JsonMapper.ToJson(obj, writer); 15 | 16 | return writer.ToString(); 17 | } 18 | 19 | public IDictionary DecodeMessage(string json) 20 | { 21 | JsonReader reader = new JsonReader(json); 22 | 23 | return JsonMapper.ToObject>(reader); 24 | } 25 | } 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /SocketIO/SocketIO Json Encoders/LitJsonEncoder.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SOCKETIO 2 | 3 | using System.Collections.Generic; 4 | 5 | using BestHTTP.JSON.LitJson; 6 | 7 | namespace BestHTTP.SocketIO.JsonEncoders 8 | { 9 | /// 10 | /// This IJsonEncoder implementation uses the LitJson library located in the Examples\LitJson directory. 11 | /// 12 | public sealed class LitJsonEncoder : IJsonEncoder 13 | { 14 | public List Decode(string json) 15 | { 16 | JsonReader reader = new JsonReader(json); 17 | return JsonMapper.ToObject>(reader); 18 | } 19 | 20 | public string Encode(List obj) 21 | { 22 | JsonWriter writer = new JsonWriter(); 23 | JsonMapper.ToJson(obj, writer); 24 | 25 | return writer.ToString(); 26 | } 27 | } 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Helpers/SelectorUI/ExampleListItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace BestHTTP.Examples.Helpers.SelectorUI 7 | { 8 | public sealed class ExampleListItem : MonoBehaviour 9 | { 10 | #pragma warning disable 0649 11 | [SerializeField] 12 | private Text _text; 13 | #pragma warning restore 14 | 15 | public SampleSelectorUI ParentUI { get; private set; } 16 | 17 | public SampleBase ExamplePrefab { get; private set; } 18 | 19 | public void Setup(SampleSelectorUI parentUI, SampleBase prefab) 20 | { 21 | this.ParentUI = parentUI; 22 | this.ExamplePrefab = prefab; 23 | 24 | this._text.text = prefab.DisplayName; 25 | } 26 | 27 | public void OnButton() 28 | { 29 | this.ParentUI.SelectSample(this); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Helpers/SelectorUI/ExampleInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace BestHTTP.Examples.Helpers.SelectorUI 7 | { 8 | public sealed class ExampleInfo : MonoBehaviour 9 | { 10 | #pragma warning disable 0649 11 | [SerializeField] 12 | private Text _header; 13 | 14 | [SerializeField] 15 | private Text _description; 16 | 17 | #pragma warning restore 18 | 19 | private SampleSelectorUI _parentUI; 20 | 21 | private SampleBase _example; 22 | 23 | public void Setup(SampleSelectorUI parentUI, SampleBase example) 24 | { 25 | this._parentUI = parentUI; 26 | this._example = example; 27 | 28 | this._header.text = this._example.name; 29 | this._description.text = this._example.Description; 30 | } 31 | 32 | public void OnExecuteExample() 33 | { 34 | this._parentUI.ExecuteExample(this._example); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SignalRCore/Person.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace BestHTTP.Examples 7 | { 8 | public enum PersonStates 9 | { 10 | Unknown, 11 | Joined 12 | } 13 | 14 | /// 15 | /// Helper class to demonstrate strongly typed callbacks 16 | /// 17 | internal sealed class Person 18 | { 19 | public UnityEngine.Vector3[] Positions { get; set; } 20 | public string Name { get; set; } 21 | public long Age { get; set; } 22 | public DateTime Joined { get; set; } 23 | public PersonStates State { get; set; } 24 | public List Friends { get; set; } 25 | 26 | public override string ToString() 27 | { 28 | return string.Format("[Person Name: '{0}', Age: '{1}', Joined: {2}, State: {3}, Friends: {4}, Position: {5}]", 29 | this.Name, this.Age, this.Joined, this.State, this.Friends != null ? this.Friends.Count : 0, this.Positions != null ? this.Positions.Length : 0); 30 | } 31 | } 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Helpers/Link.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using UnityEngine; 3 | using UnityEngine.EventSystems; 4 | 5 | namespace BestHTTP.Examples 6 | { 7 | public class Link : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler 8 | { 9 | public string url; 10 | public Texture2D linkSelectCursor; 11 | 12 | void IPointerDownHandler.OnPointerDown(PointerEventData eventData) 13 | { 14 | #if UNITY_WEBGL && !UNITY_EDITOR 15 | openWindow(this.url); 16 | #else 17 | Application.OpenURL(this.url); 18 | #endif 19 | } 20 | 21 | void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData) 22 | { 23 | Cursor.SetCursor(this.linkSelectCursor, Vector2.zero, CursorMode.Auto); 24 | } 25 | 26 | void IPointerExitHandler.OnPointerExit(PointerEventData eventData) 27 | { 28 | Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto); 29 | } 30 | 31 | #if UNITY_WEBGL && !UNITY_EDITOR 32 | [DllImport("__Internal")] 33 | private static extern void openWindow(string url); 34 | #endif 35 | } 36 | } -------------------------------------------------------------------------------- /Helpers/Components/Cache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using BestHTTP.Core; 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | 7 | namespace BestHTTP.Examples.Helpers.Components 8 | { 9 | public class Cache : MonoBehaviour 10 | { 11 | #pragma warning disable 0649, 0169 12 | [SerializeField] 13 | private Text _count; 14 | 15 | [SerializeField] 16 | private Text _size; 17 | 18 | [SerializeField] 19 | private Button _clear; 20 | #pragma warning restore 21 | 22 | private void Start() 23 | { 24 | PluginEventHelper.OnEvent += OnPluginEvent; 25 | UpdateLabels(); 26 | } 27 | 28 | private void OnDestroy() 29 | { 30 | PluginEventHelper.OnEvent -= OnPluginEvent; 31 | } 32 | 33 | private void OnPluginEvent(PluginEventInfo @event) 34 | { 35 | if (@event.Event == PluginEvents.SaveCacheLibrary) 36 | UpdateLabels(); 37 | } 38 | 39 | private void UpdateLabels() 40 | { 41 | #if !BESTHTTP_DISABLE_CACHING 42 | this._count.text = BestHTTP.Caching.HTTPCacheService.GetCacheEntityCount().ToString("N0"); 43 | this._size.text = BestHTTP.Caching.HTTPCacheService.GetCacheSize().ToString("N0"); 44 | #else 45 | this._count.text = "0"; 46 | this._size.text = "0"; 47 | #endif 48 | } 49 | 50 | public void OnClearButtonClicked() 51 | { 52 | #if !BESTHTTP_DISABLE_CACHING 53 | BestHTTP.Caching.HTTPCacheService.BeginClear(); 54 | #endif 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Helpers/Components/Cookies.cs: -------------------------------------------------------------------------------- 1 | using BestHTTP.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | using UnityEngine.UI; 7 | 8 | namespace BestHTTP.Examples.Helpers.Components 9 | { 10 | public class Cookies : MonoBehaviour 11 | { 12 | #pragma warning disable 0649, 0169 13 | [SerializeField] 14 | private Text _count; 15 | 16 | [SerializeField] 17 | private Text _size; 18 | 19 | [SerializeField] 20 | private Button _clear; 21 | #pragma warning restore 22 | 23 | private void Start() 24 | { 25 | PluginEventHelper.OnEvent += OnPluginEvent; 26 | UpdateLabels(); 27 | } 28 | 29 | private void OnDestroy() 30 | { 31 | PluginEventHelper.OnEvent -= OnPluginEvent; 32 | } 33 | 34 | private void OnPluginEvent(PluginEventInfo @event) 35 | { 36 | #if !BESTHTTP_DISABLE_COOKIES 37 | if (@event.Event == PluginEvents.SaveCookieLibrary) 38 | UpdateLabels(); 39 | #endif 40 | } 41 | 42 | private void UpdateLabels() 43 | { 44 | #if !BESTHTTP_DISABLE_COOKIES 45 | var cookies = BestHTTP.Cookies.CookieJar.GetAll(); 46 | var size = cookies.Sum(c => c.GuessSize()); 47 | 48 | this._count.text = cookies.Count.ToString("N0"); 49 | this._size.text = size.ToString("N0"); 50 | #else 51 | this._count.text = "0"; 52 | this._size.text = "0"; 53 | #endif 54 | } 55 | 56 | public void OnClearButtonClicked() 57 | { 58 | #if !BESTHTTP_DISABLE_COOKIES 59 | BestHTTP.Cookies.CookieJar.Clear(); 60 | #endif 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SignalRCore/Encoders/JsonDotNetEncoder.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE && BESTHTTP_SIGNALR_CORE_ENABLE_NEWTONSOFT_JSON_DOTNET_ENCODER 2 | using System; 3 | using BestHTTP.PlatformSupport.Memory; 4 | 5 | namespace BestHTTP.SignalRCore.Encoders 6 | { 7 | public sealed class JsonDotNetEncoder : BestHTTP.SignalRCore.IEncoder 8 | { 9 | Newtonsoft.Json.JsonSerializerSettings _settings; 10 | 11 | public JsonDotNetEncoder() 12 | { } 13 | 14 | public JsonDotNetEncoder(Newtonsoft.Json.JsonSerializerSettings settings) 15 | { 16 | this._settings = settings; 17 | } 18 | 19 | public object ConvertTo(Type toType, object obj) 20 | { 21 | string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj, this._settings); 22 | 23 | return Newtonsoft.Json.JsonConvert.DeserializeObject(json, toType, this._settings); 24 | } 25 | 26 | public T DecodeAs(BufferSegment buffer) 27 | { 28 | using (var reader = new System.IO.StreamReader(new System.IO.MemoryStream(buffer.Data, buffer.Offset, buffer.Count))) 29 | using (var jsonReader = new Newtonsoft.Json.JsonTextReader(reader)) 30 | return Newtonsoft.Json.JsonSerializer.CreateDefault(this._settings).Deserialize(jsonReader); 31 | } 32 | 33 | public BufferSegment Encode(T value) 34 | { 35 | var json = Newtonsoft.Json.JsonConvert.SerializeObject(value, this._settings); 36 | 37 | int len = System.Text.Encoding.UTF8.GetByteCount(json); 38 | byte[] buffer = BufferPool.Get(len + 1, true); 39 | System.Text.Encoding.UTF8.GetBytes(json, 0, json.Length, buffer, 0); 40 | 41 | buffer[len] = 0x1e; 42 | 43 | return new BufferSegment(buffer, 0, len + 1); 44 | } 45 | } 46 | } 47 | #endif 48 | -------------------------------------------------------------------------------- /Helpers/GUIHelper.cs: -------------------------------------------------------------------------------- 1 | using BestHTTP.Examples.Helpers; 2 | using System; 3 | using System.Collections; 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | 7 | namespace BestHTTP.Examples 8 | { 9 | public static class GUIHelper 10 | { 11 | // https://en.wikipedia.org/wiki/Binary_prefix 12 | private static string[] prefixes = new string[] { " B", " KiB", " MiB", " GiB", " TiB" }; 13 | public static string GetBytesStr(double bytes, byte precision) 14 | { 15 | int prefixIdx = 0; 16 | while (bytes >= 1024) 17 | { 18 | bytes = bytes / 1024; 19 | prefixIdx++; 20 | } 21 | 22 | return bytes.ToString("F" + precision) + prefixes[prefixIdx]; 23 | } 24 | 25 | public static void RemoveChildren(RectTransform transform, int maxChildCount) 26 | { 27 | while (transform.childCount > maxChildCount) 28 | { 29 | var child = transform.GetChild(0); 30 | child.SetParent(null); 31 | 32 | GameObject.Destroy(child.gameObject); 33 | } 34 | } 35 | 36 | public static TextListItem AddText(TextListItem prefab, RectTransform contentRoot, string text, int maxEntries, ScrollRect scrollRect) 37 | { 38 | if (contentRoot == null) 39 | return null; 40 | 41 | var listItem = GameObject.Instantiate(prefab, contentRoot, false); 42 | listItem.SetText(text); 43 | 44 | GUIHelper.RemoveChildren(contentRoot, maxEntries); 45 | 46 | if (scrollRect != null && scrollRect.isActiveAndEnabled) 47 | scrollRect.StartCoroutine(ScrollToBottom(scrollRect)); 48 | 49 | return listItem; 50 | } 51 | 52 | public static IEnumerator ScrollToBottom(ScrollRect scrollRect) 53 | { 54 | yield return null; 55 | 56 | if (scrollRect != null && scrollRect.isActiveAndEnabled) 57 | scrollRect.normalizedPosition = new Vector2(0, 0); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /SignalRCore/Authentication Providers/HeaderAuthenticator.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using System; 4 | 5 | namespace BestHTTP.SignalRCore.Authentication 6 | { 7 | public sealed class HeaderAuthenticator : IAuthenticationProvider 8 | { 9 | /// 10 | /// No pre-auth step required for this type of authentication 11 | /// 12 | public bool IsPreAuthRequired { get { return false; } } 13 | 14 | #pragma warning disable 0067 15 | /// 16 | /// Not used event as IsPreAuthRequired is false 17 | /// 18 | public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; 19 | 20 | /// 21 | /// Not used event as IsPreAuthRequired is false 22 | /// 23 | public event OnAuthenticationFailedDelegate OnAuthenticationFailed; 24 | 25 | #pragma warning restore 0067 26 | 27 | private string _credentials; 28 | 29 | public HeaderAuthenticator(string credentials) 30 | { 31 | this._credentials = credentials; 32 | } 33 | 34 | /// 35 | /// Not used as IsPreAuthRequired is false 36 | /// 37 | public void StartAuthentication() 38 | { } 39 | 40 | /// 41 | /// Prepares the request by adding two headers to it 42 | /// 43 | public void PrepareRequest(BestHTTP.HTTPRequest request) 44 | { 45 | #if !UNITY_WEBGL || UNITY_EDITOR 46 | request.SetHeader("Authorization", "Bearer " + this._credentials); 47 | #endif 48 | } 49 | 50 | public Uri PrepareUri(Uri uri) 51 | { 52 | #if UNITY_WEBGL && !UNITY_EDITOR 53 | string query = string.IsNullOrEmpty(uri.Query) ? "?" : uri.Query + "&"; 54 | UriBuilder uriBuilder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, query + "access_token=" + this._credentials); 55 | return uriBuilder.Uri; 56 | #else 57 | return uri; 58 | #endif 59 | } 60 | 61 | public void Cancel() 62 | { 63 | 64 | } 65 | } 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /SignalRCore/Encoders/LitJsonEncoder.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | using System; 3 | using BestHTTP.PlatformSupport.Memory; 4 | using BestHTTP.JSON.LitJson; 5 | 6 | namespace BestHTTP.SignalRCore.Encoders 7 | { 8 | public sealed class LitJsonEncoder : BestHTTP.SignalRCore.IEncoder 9 | { 10 | public LitJsonEncoder() 11 | { 12 | BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter((input) => input); 13 | BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter((input) => (int)input); 14 | BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter((input) => (int)(input + 0.5)); 15 | BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter((input) => Convert.ToDateTime((string)input).ToUniversalTime()); 16 | BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter((input) => (float)input); 17 | BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter((input) => Convert.FromBase64String(input)); 18 | BestHTTP.JSON.LitJson.JsonMapper.RegisterExporter((f, writer) => writer.Write((double)f)); 19 | } 20 | 21 | public T DecodeAs(BufferSegment buffer) 22 | { 23 | using (var reader = new System.IO.StreamReader(new System.IO.MemoryStream(buffer.Data, buffer.Offset, buffer.Count))) 24 | { 25 | return JsonMapper.ToObject(reader); 26 | } 27 | } 28 | 29 | public PlatformSupport.Memory.BufferSegment Encode(T value) 30 | { 31 | var json = JsonMapper.ToJson(value); 32 | int len = System.Text.Encoding.UTF8.GetByteCount(json); 33 | byte[] buffer = BufferPool.Get(len + 1, true); 34 | System.Text.Encoding.UTF8.GetBytes(json, 0, json.Length, buffer, 0); 35 | buffer[len] = (byte)JsonProtocol.Separator; 36 | return new BufferSegment(buffer, 0, len + 1); 37 | } 38 | 39 | public object ConvertTo(Type toType, object obj) 40 | { 41 | string json = BestHTTP.JSON.LitJson.JsonMapper.ToJson(obj); 42 | return BestHTTP.JSON.LitJson.JsonMapper.ToObject(toType, json); 43 | } 44 | } 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /Plugin/AsyncExtensions.cs: -------------------------------------------------------------------------------- 1 | #if CSHARP_7_OR_LATER 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace BestHTTP 7 | { 8 | public static class AsyncExtensions 9 | { 10 | public static Task GetFromJsonResultAsync(this HTTPRequest request, CancellationToken token = default) 11 | { 12 | return HTTPRequestAsyncExtensions.CreateTask(request, token, (req, resp, tcs) => 13 | { 14 | switch (req.State) 15 | { 16 | // The request finished without any problem. 17 | case HTTPRequestStates.Finished: 18 | if (resp.IsSuccess) 19 | tcs.TrySetResult(BestHTTP.JSON.LitJson.JsonMapper.ToObject(resp.DataAsText)); 20 | else 21 | tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("Request finished Successfully, but the server sent an error.", resp)); 22 | break; 23 | 24 | // The request finished with an unexpected error. The request's Exception property may contain more info about the error. 25 | case HTTPRequestStates.Error: 26 | HTTPRequestAsyncExtensions.VerboseLogging(request, "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception")); 27 | 28 | tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("No Exception", null, req.Exception)); 29 | break; 30 | 31 | // The request aborted, initiated by the user. 32 | case HTTPRequestStates.Aborted: 33 | HTTPRequestAsyncExtensions.VerboseLogging(request, "Request Aborted!"); 34 | 35 | tcs.TrySetCanceled(); 36 | break; 37 | 38 | // Connecting to the server is timed out. 39 | case HTTPRequestStates.ConnectionTimedOut: 40 | HTTPRequestAsyncExtensions.VerboseLogging(request, "Connection Timed Out!"); 41 | 42 | tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("Connection Timed Out!")); 43 | break; 44 | 45 | // The request didn't finished in the given time. 46 | case HTTPRequestStates.TimedOut: 47 | HTTPRequestAsyncExtensions.VerboseLogging(request, "Processing the request Timed Out!"); 48 | 49 | tcs.TrySetException(HTTPRequestAsyncExtensions.CreateException("Processing the request Timed Out!")); 50 | break; 51 | } 52 | }); 53 | } 54 | } 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /HTTP/ResumableStreamingSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | using BestHTTP; 7 | 8 | namespace BestHTTP.Examples.HTTP 9 | { 10 | public sealed class ResumableStreamingSample : StreamingSample 11 | { 12 | const string ProcessedBytesKey = "ProcessedBytes"; 13 | const string DownloadLengthKey = "DownloadLength"; 14 | 15 | /// 16 | /// Expected content length 17 | /// 18 | protected override long DownloadLength { get { return PlayerPrefs.GetInt(this._downloadPath + DownloadLengthKey); } set { PlayerPrefs.SetInt(this._downloadPath + DownloadLengthKey, (int)value); } } 19 | 20 | /// 21 | /// Total processed bytes 22 | /// 23 | protected override long ProcessedBytes { get { return PlayerPrefs.GetInt(this._downloadPath + ProcessedBytesKey, 0); } set { PlayerPrefs.SetInt(this._downloadPath + ProcessedBytesKey, (int)value); } } 24 | 25 | private long downloadStartedAt = 0; 26 | 27 | protected override void Start() 28 | { 29 | base.Start(); 30 | 31 | // If we have a non-finished download, set the progress to the value where we left it 32 | float progress = GetSavedProgress(); 33 | if (progress > 0.0f) 34 | { 35 | this._downloadProgressSlider.value = progress; 36 | base._statusText.text = progress.ToString("F2"); 37 | } 38 | } 39 | 40 | protected override void SetupRequest() 41 | { 42 | base.SetupRequest(); 43 | 44 | // Are there any progress, that we can continue? 45 | this.downloadStartedAt = this.ProcessedBytes; 46 | 47 | if (this.downloadStartedAt > 0) 48 | { 49 | // Set the range header 50 | request.SetRangeHeader(this.downloadStartedAt); 51 | } 52 | else 53 | // This is a new request 54 | DeleteKeys(); 55 | } 56 | 57 | protected override void OnRequestFinished(HTTPRequest req, HTTPResponse resp) 58 | { 59 | base.OnRequestFinished(req, resp); 60 | 61 | if (req.State == HTTPRequestStates.Finished && resp.IsSuccess) 62 | DeleteKeys(); 63 | } 64 | 65 | protected override void OnDownloadProgress(HTTPRequest originalRequest, long downloaded, long downloadLength) 66 | { 67 | double downloadPercent = ((this.downloadStartedAt + downloaded) / (double)this.DownloadLength) * 100; 68 | 69 | this._downloadProgressSlider.value = (float)downloadPercent; 70 | this._downloadProgressText.text = string.Format("{0:F1}%", downloadPercent); 71 | } 72 | 73 | protected override void ResetProcessedValues() 74 | { 75 | SetDataProcessedUI(this.ProcessedBytes, this.DownloadLength); 76 | } 77 | 78 | private float GetSavedProgress() 79 | { 80 | long down = this.ProcessedBytes; 81 | long length = this.DownloadLength; 82 | 83 | if (down > 0 && length > 0) 84 | return (down / (float)length) * 100f; 85 | 86 | return -1; 87 | } 88 | 89 | private void DeleteKeys() 90 | { 91 | PlayerPrefs.DeleteKey(this._downloadPath + ProcessedBytesKey); 92 | PlayerPrefs.DeleteKey(this._downloadPath + DownloadLengthKey); 93 | PlayerPrefs.Save(); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /Helpers/SelectorUI/SampleSelectorUI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | 7 | namespace BestHTTP.Examples.Helpers.SelectorUI 8 | { 9 | public class SampleSelectorUI : MonoBehaviour 10 | { 11 | #pragma warning disable 0649, 0169 12 | 13 | [SerializeField] 14 | private Category _categoryListItemPrefab; 15 | 16 | [SerializeField] 17 | private ExampleListItem _exampleListItemPrefab; 18 | 19 | [SerializeField] 20 | private ExampleInfo _exampleInfoPrefab; 21 | 22 | [SerializeField] 23 | private RectTransform _listRoot; 24 | 25 | [SerializeField] 26 | private RectTransform _dyncamicContentRoot; 27 | 28 | private SampleRoot sampleSelector; 29 | private ExampleListItem selectedSample; 30 | private GameObject dynamicContent; 31 | 32 | #pragma warning restore 33 | 34 | private void Start() 35 | { 36 | this.sampleSelector = FindObjectOfType(); 37 | DisplayExamples(); 38 | } 39 | 40 | private void DisplayExamples() 41 | { 42 | // Sort examples by category 43 | this.sampleSelector.samples.Sort((a, b) => { 44 | if (a == null || b == null) 45 | return 0; 46 | 47 | int result = a.Category.CompareTo(b.Category); 48 | if (result == 0) 49 | result = a.DisplayName.CompareTo(b.DisplayName); 50 | return result; 51 | }); 52 | 53 | string currentCategory = null; 54 | 55 | for (int i = 0; i < this.sampleSelector.samples.Count; ++i) 56 | { 57 | var examplePrefab = this.sampleSelector.samples[i]; 58 | 59 | if (examplePrefab == null) 60 | continue; 61 | 62 | if (examplePrefab.BannedPlatforms.Contains(UnityEngine.Application.platform)) 63 | continue; 64 | 65 | if (currentCategory != examplePrefab.Category) 66 | { 67 | var category = Instantiate(this._categoryListItemPrefab, this._listRoot, false); 68 | category.SetLabel(examplePrefab.Category); 69 | 70 | currentCategory = examplePrefab.Category; 71 | } 72 | 73 | var listItem = Instantiate(this._exampleListItemPrefab, this._listRoot, false); 74 | listItem.Setup(this, examplePrefab); 75 | 76 | if (this.sampleSelector.selectedExamplePrefab == null) 77 | { 78 | SelectSample(listItem); 79 | } 80 | } 81 | } 82 | 83 | public void SelectSample(ExampleListItem item) 84 | { 85 | this.sampleSelector.selectedExamplePrefab = item.ExamplePrefab; 86 | if (this.dynamicContent != null) 87 | Destroy(this.dynamicContent); 88 | 89 | var example = Instantiate(this._exampleInfoPrefab, this._dyncamicContentRoot, false); 90 | example.Setup(this, item.ExamplePrefab); 91 | this.dynamicContent = example.gameObject; 92 | } 93 | 94 | public void ExecuteExample(SampleBase example) 95 | { 96 | if (this.dynamicContent != null) 97 | Destroy(this.dynamicContent); 98 | this.dynamicContent = Instantiate(example, this._dyncamicContentRoot, false).gameObject; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /SampleRoot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | 7 | using BestHTTP.Examples.Helpers; 8 | 9 | namespace BestHTTP.Examples 10 | { 11 | public class SampleRoot : MonoBehaviour 12 | { 13 | #pragma warning disable 0649, 0169 14 | [Header("Common Properties")] 15 | public string BaseURL = "https://besthttpwebgldemo.azurewebsites.net"; 16 | 17 | [Header("References")] 18 | 19 | [SerializeField] 20 | private Text _pluginVersion; 21 | 22 | [SerializeField] 23 | private Dropdown _logLevelDropdown; 24 | 25 | [SerializeField] 26 | private Text _proxyLabel; 27 | 28 | [SerializeField] 29 | private InputField _proxyInputField; 30 | 31 | #pragma warning restore 32 | 33 | [SerializeField] 34 | public List samples = new List(); 35 | 36 | [HideInInspector] 37 | public SampleBase selectedExamplePrefab; 38 | 39 | private void Start() 40 | { 41 | Application.runInBackground = true; 42 | 43 | this._pluginVersion.text = "Version: " + HTTPManager.UserAgent; 44 | 45 | int logLevel = PlayerPrefs.GetInt("BestHTTP.HTTPManager.Logger.Level", (int)HTTPManager.Logger.Level); 46 | this._logLevelDropdown.value = logLevel; 47 | HTTPManager.Logger.Level = (BestHTTP.Logger.Loglevels)logLevel; 48 | 49 | #if (UNITY_WEBGL && !UNITY_EDITOR) || BESTHTTP_DISABLE_PROXY 50 | this._proxyLabel.gameObject.SetActive(false); 51 | this._proxyInputField.gameObject.SetActive(false); 52 | #else 53 | string proxyURL = PlayerPrefs.GetString("BestHTTP.HTTPManager.Proxy", null); 54 | if (!string.IsNullOrEmpty(proxyURL)) 55 | { 56 | try 57 | { 58 | HTTPManager.Proxy = new HTTPProxy(new Uri(proxyURL), null, true); 59 | #if UNITY_2019_1_OR_NEWER 60 | this._proxyInputField.SetTextWithoutNotify(proxyURL); 61 | #else 62 | this._proxyInputField.onEndEdit.RemoveAllListeners(); 63 | this._proxyInputField.text = proxyURL; 64 | this._proxyInputField.onEndEdit.AddListener(this.OnProxyEditEnd); 65 | #endif 66 | } 67 | catch 68 | { } 69 | } 70 | else 71 | HTTPManager.Proxy = null; 72 | #endif 73 | 74 | #if !BESTHTTP_DISABLE_CACHING 75 | // Remove too old cache entries. 76 | BestHTTP.Caching.HTTPCacheService.BeginMaintainence(new BestHTTP.Caching.HTTPCacheMaintananceParams(TimeSpan.FromDays(30), ulong.MaxValue)); 77 | #endif 78 | } 79 | 80 | public void OnLogLevelChanged(int idx) 81 | { 82 | HTTPManager.Logger.Level = (BestHTTP.Logger.Loglevels)idx; 83 | PlayerPrefs.SetInt("BestHTTP.HTTPManager.Logger.Level", idx); 84 | } 85 | 86 | public void OnProxyEditEnd(string proxyURL) 87 | { 88 | #if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_PROXY 89 | try 90 | { 91 | if (string.IsNullOrEmpty(this._proxyInputField.text)) 92 | HTTPManager.Proxy = null; 93 | else 94 | HTTPManager.Proxy = new HTTPProxy(new Uri(this._proxyInputField.text), null, true); 95 | 96 | PlayerPrefs.SetString("BestHTTP.HTTPManager.Proxy", this._proxyInputField.text); 97 | } 98 | catch 99 | { } 100 | #endif 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /SignalR/Authentication Providers/SampleHeaderAuthentication.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR 2 | 3 | namespace BestHTTP.SignalR.Authentication 4 | { 5 | /// 6 | /// Custom http-header based authenticator. 7 | /// 8 | /// 9 | /// // Server side implementation of the Header-based authenticator 10 | /// // Use it by adding the app.Use(typeof(HeaderBasedAuthenticationMiddleware)); line to the Startup class' Configuration function. 11 | /// private class HeaderBasedAuthenticationMiddleware : OwinMiddleware 12 | /// { 13 | /// public HeaderBasedAuthenticationMiddleware(OwinMiddleware next) 14 | /// : base(next) 15 | /// { 16 | /// } 17 | /// 18 | /// public override Task Invoke(IOwinContext context) 19 | /// { 20 | /// string username = context.Request.Headers.Get("username"); 21 | /// string roles = context.Request.Headers.Get("roles"); 22 | /// 23 | /// if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(roles)) 24 | /// { 25 | /// var identity = new System.Security.Principal.GenericIdentity(username); 26 | /// 27 | /// var principal = new System.Security.Principal.GenericPrincipal(identity, SplitString(roles)); 28 | /// 29 | /// context.Request.User = principal; 30 | /// } 31 | /// 32 | /// return Next.Invoke(context); 33 | /// } 34 | /// 35 | /// private static string[] SplitString(string original) 36 | /// { 37 | /// if (String.IsNullOrEmpty(original)) 38 | /// return new string[0]; 39 | /// 40 | /// var split = from piece in original.Split(',') let trimmed = piece.Trim() where !String.IsNullOrEmpty(trimmed) select trimmed; 41 | /// 42 | /// return split.ToArray(); 43 | /// } 44 | /// } 45 | /// 46 | /// 47 | /// 48 | class HeaderAuthenticator : IAuthenticationProvider 49 | { 50 | public string User { get; private set; } 51 | public string Roles { get; private set; } 52 | 53 | /// 54 | /// No pre-auth step required for this type of authentication 55 | /// 56 | public bool IsPreAuthRequired { get { return false; } } 57 | 58 | #pragma warning disable 0067 59 | /// 60 | /// Not used event as IsPreAuthRequired is false 61 | /// 62 | public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; 63 | 64 | /// 65 | /// Not used event as IsPreAuthRequired is false 66 | /// 67 | public event OnAuthenticationFailedDelegate OnAuthenticationFailed; 68 | 69 | #pragma warning restore 0067 70 | 71 | /// 72 | /// Constructor to initialise the authenticator with username and roles. 73 | /// 74 | public HeaderAuthenticator(string user, string roles) 75 | { 76 | this.User = user; 77 | this.Roles = roles; 78 | } 79 | 80 | /// 81 | /// Not used as IsPreAuthRequired is false 82 | /// 83 | public void StartAuthentication() 84 | { } 85 | 86 | /// 87 | /// Prepares the request by adding two headers to it 88 | /// 89 | public void PrepareRequest(BestHTTP.HTTPRequest request, RequestTypes type) 90 | { 91 | request.SetHeader("username", this.User); 92 | request.SetHeader("roles", this.Roles); 93 | } 94 | } 95 | } 96 | 97 | #endif -------------------------------------------------------------------------------- /Server-Sent Events/SimpleSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SERVERSENT_EVENTS 2 | 3 | using System; 4 | using BestHTTP.Examples.Helpers; 5 | using BestHTTP.ServerSentEvents; 6 | using UnityEngine; 7 | using UnityEngine.UI; 8 | 9 | namespace BestHTTP.Examples.ServerSentEvents 10 | { 11 | public class SimpleSample : BestHTTP.Examples.Helpers.SampleBase 12 | { 13 | #pragma warning disable 0649 14 | 15 | [Tooltip("The url of the resource to use.")] 16 | [SerializeField] 17 | private string _path = "/sse"; 18 | 19 | [SerializeField] 20 | private ScrollRect _scrollRect; 21 | 22 | [SerializeField] 23 | private RectTransform _contentRoot; 24 | 25 | [SerializeField] 26 | private TextListItem _listItemPrefab; 27 | 28 | [SerializeField] 29 | private int _maxListItemEntries = 100; 30 | 31 | [SerializeField] 32 | private Button _startButton; 33 | 34 | [SerializeField] 35 | private Button _closeButton; 36 | 37 | #pragma warning restore 38 | 39 | private EventSource eventSource; 40 | 41 | protected override void Start() 42 | { 43 | base.Start(); 44 | 45 | SetButtons(true, false); 46 | } 47 | 48 | void OnDestroy() 49 | { 50 | if (this.eventSource != null) 51 | { 52 | this.eventSource.Close(); 53 | this.eventSource = null; 54 | } 55 | } 56 | 57 | public void OnStartButton() 58 | { 59 | GUIHelper.RemoveChildren(this._contentRoot, 0); 60 | 61 | // Create the EventSource instance 62 | this.eventSource = new EventSource(new Uri(base.sampleSelector.BaseURL + this._path)); 63 | 64 | // Subscribe to generic events 65 | this.eventSource.OnOpen += OnOpen; 66 | this.eventSource.OnClosed += OnClosed; 67 | this.eventSource.OnError += OnError; 68 | this.eventSource.OnStateChanged += this.OnStateChanged; 69 | this.eventSource.OnMessage += OnMessage; 70 | 71 | // Subscribe to an application specific event 72 | this.eventSource.On("datetime", OnDateTime); 73 | 74 | // Start to connect to the server 75 | this.eventSource.Open(); 76 | 77 | AddText("Opening Server-Sent Events..."); 78 | 79 | SetButtons(false, true); 80 | } 81 | 82 | public void OnCloseButton() 83 | { 84 | SetButtons(false, false); 85 | this.eventSource.Close(); 86 | } 87 | 88 | private void OnOpen(EventSource eventSource) 89 | { 90 | AddText("Open"); 91 | } 92 | 93 | private void OnClosed(EventSource eventSource) 94 | { 95 | AddText("Closed"); 96 | 97 | this.eventSource = null; 98 | 99 | SetButtons(true, false); 100 | } 101 | 102 | private void OnError(EventSource eventSource, string error) 103 | { 104 | AddText(string.Format("Error: {0}", error)); 105 | } 106 | 107 | private void OnStateChanged(EventSource eventSource, States oldState, States newState) 108 | { 109 | AddText(string.Format("State Changed {0} => {1}", oldState, newState)); 110 | } 111 | 112 | private void OnMessage(EventSource eventSource, Message message) 113 | { 114 | AddText(string.Format("Message: {0}", message)); 115 | } 116 | 117 | private void OnDateTime(EventSource eventSource, Message message) 118 | { 119 | DateTimeData dtData = BestHTTP.JSON.LitJson.JsonMapper.ToObject(message.Data); 120 | 121 | AddText(string.Format("OnDateTime: {0}", dtData.ToString())); 122 | } 123 | 124 | private void SetButtons(bool start, bool close) 125 | { 126 | if (this._startButton != null) 127 | this._startButton.interactable = start; 128 | 129 | if (this._closeButton != null) 130 | this._closeButton.interactable = close; 131 | } 132 | 133 | private void AddText(string text) 134 | { 135 | GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 136 | } 137 | } 138 | 139 | [PlatformSupport.IL2CPP.Preserve] 140 | sealed class DateTimeData 141 | { 142 | #pragma warning disable 0649 143 | [PlatformSupport.IL2CPP.Preserve] 144 | public int eventid; 145 | 146 | [PlatformSupport.IL2CPP.Preserve] 147 | public string datetime; 148 | #pragma warning restore 149 | 150 | public override string ToString() 151 | { 152 | return string.Format("[DateTimeData EventId: {0}, DateTime: {1}]", this.eventid, this.datetime); 153 | } 154 | } 155 | } 156 | #endif 157 | -------------------------------------------------------------------------------- /SignalR/Authentication Providers/SampleCookieAuthentication.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR 2 | #if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR) 3 | 4 | using System; 5 | 6 | using BestHTTP.Cookies; 7 | using BestHTTP.SignalR.Transports; 8 | 9 | namespace BestHTTP.SignalR.Authentication 10 | { 11 | public sealed class SampleCookieAuthentication : IAuthenticationProvider 12 | { 13 | #region Public Properties 14 | 15 | public Uri AuthUri { get; private set; } 16 | public string UserName { get; private set; } 17 | public string Password { get; private set; } 18 | public string UserRoles { get; private set; } 19 | 20 | #endregion 21 | 22 | #region IAuthenticationProvider properties 23 | 24 | public bool IsPreAuthRequired { get; private set; } 25 | 26 | public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; 27 | public event OnAuthenticationFailedDelegate OnAuthenticationFailed; 28 | 29 | #endregion 30 | 31 | #region Privates 32 | 33 | private HTTPRequest AuthRequest; 34 | private Cookie Cookie; 35 | 36 | #endregion 37 | 38 | public SampleCookieAuthentication(Uri authUri, string user, string passwd, string roles) 39 | { 40 | this.AuthUri = authUri; 41 | this.UserName = user; 42 | this.Password = passwd; 43 | this.UserRoles = roles; 44 | this.IsPreAuthRequired = true; 45 | } 46 | 47 | #region IAuthenticationProvider Implementation 48 | 49 | public void StartAuthentication() 50 | { 51 | AuthRequest = new HTTPRequest(AuthUri, HTTPMethods.Post, OnAuthRequestFinished); 52 | 53 | // Setup the form 54 | AuthRequest.AddField("userName", UserName); 55 | AuthRequest.AddField("Password", Password); // not used in the sample 56 | AuthRequest.AddField("roles", UserRoles); 57 | 58 | AuthRequest.Send(); 59 | } 60 | 61 | public void PrepareRequest(HTTPRequest request, RequestTypes type) 62 | { 63 | // Adding the cookie to the request is not required, as it's managed by the plugin automatically, 64 | // but for now, we want to be really sure that it's added 65 | request.Cookies.Add(Cookie); 66 | } 67 | 68 | #endregion 69 | 70 | #region Request Handler 71 | 72 | void OnAuthRequestFinished(HTTPRequest req, HTTPResponse resp) 73 | { 74 | AuthRequest = null; 75 | string failReason = string.Empty; 76 | 77 | switch (req.State) 78 | { 79 | // The request finished without any problem. 80 | case HTTPRequestStates.Finished: 81 | if (resp.IsSuccess) 82 | { 83 | Cookie = resp.Cookies != null ? resp.Cookies.Find(c => c.Name.Equals(".ASPXAUTH")) : null; 84 | 85 | if (Cookie != null) 86 | { 87 | HTTPManager.Logger.Information("CookieAuthentication", "Auth. Cookie found!"); 88 | 89 | if (OnAuthenticationSucceded != null) 90 | OnAuthenticationSucceded(this); 91 | 92 | // return now, all other paths are authentication failures 93 | return; 94 | } 95 | else 96 | HTTPManager.Logger.Warning("CookieAuthentication", failReason = "Auth. Cookie NOT found!"); 97 | } 98 | else 99 | HTTPManager.Logger.Warning("CookieAuthentication", failReason = string.Format("Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", 100 | resp.StatusCode, 101 | resp.Message, 102 | resp.DataAsText)); 103 | break; 104 | 105 | // The request finished with an unexpected error. The request's Exception property may contain more info about the error. 106 | case HTTPRequestStates.Error: 107 | HTTPManager.Logger.Warning("CookieAuthentication", failReason = "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception")); 108 | break; 109 | 110 | // The request aborted, initiated by the user. 111 | case HTTPRequestStates.Aborted: 112 | HTTPManager.Logger.Warning("CookieAuthentication", failReason = "Request Aborted!"); 113 | break; 114 | 115 | // Connecting to the server is timed out. 116 | case HTTPRequestStates.ConnectionTimedOut: 117 | HTTPManager.Logger.Error("CookieAuthentication", failReason = "Connection Timed Out!"); 118 | break; 119 | 120 | // The request didn't finished in the given time. 121 | case HTTPRequestStates.TimedOut: 122 | HTTPManager.Logger.Error("CookieAuthentication", failReason = "Processing the request Timed Out!"); 123 | break; 124 | } 125 | 126 | if (OnAuthenticationFailed != null) 127 | OnAuthenticationFailed(this, failReason); 128 | } 129 | 130 | #endregion 131 | } 132 | } 133 | 134 | #endif 135 | #endif -------------------------------------------------------------------------------- /Helpers/SelectorUI/Category.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1001 &100100000 4 | Prefab: 5 | m_ObjectHideFlags: 1 6 | serializedVersion: 2 7 | m_Modification: 8 | m_TransformParent: {fileID: 0} 9 | m_Modifications: [] 10 | m_RemovedComponents: [] 11 | m_ParentPrefab: {fileID: 0} 12 | m_RootGameObject: {fileID: 1904131878966858} 13 | m_IsPrefabParent: 1 14 | --- !u!1 &1904131878966858 15 | GameObject: 16 | m_ObjectHideFlags: 0 17 | m_PrefabParentObject: {fileID: 0} 18 | m_PrefabInternal: {fileID: 100100000} 19 | serializedVersion: 5 20 | m_Component: 21 | - component: {fileID: 224461754452575090} 22 | - component: {fileID: 114104161713339180} 23 | - component: {fileID: 114607162270506794} 24 | - component: {fileID: 114804161523563040} 25 | m_Layer: 5 26 | m_Name: Category 27 | m_TagString: Untagged 28 | m_Icon: {fileID: 0} 29 | m_NavMeshLayer: 0 30 | m_StaticEditorFlags: 0 31 | m_IsActive: 1 32 | --- !u!1 &1966926200078894 33 | GameObject: 34 | m_ObjectHideFlags: 0 35 | m_PrefabParentObject: {fileID: 0} 36 | m_PrefabInternal: {fileID: 100100000} 37 | serializedVersion: 5 38 | m_Component: 39 | - component: {fileID: 224471434808036404} 40 | - component: {fileID: 222752501853716432} 41 | - component: {fileID: 114045595599197664} 42 | m_Layer: 5 43 | m_Name: Text 44 | m_TagString: Untagged 45 | m_Icon: {fileID: 0} 46 | m_NavMeshLayer: 0 47 | m_StaticEditorFlags: 0 48 | m_IsActive: 1 49 | --- !u!114 &114045595599197664 50 | MonoBehaviour: 51 | m_ObjectHideFlags: 1 52 | m_PrefabParentObject: {fileID: 0} 53 | m_PrefabInternal: {fileID: 100100000} 54 | m_GameObject: {fileID: 1966926200078894} 55 | m_Enabled: 1 56 | m_EditorHideFlags: 0 57 | m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 58 | m_Name: 59 | m_EditorClassIdentifier: 60 | m_Material: {fileID: 0} 61 | m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} 62 | m_RaycastTarget: 0 63 | m_OnCullStateChanged: 64 | m_PersistentCalls: 65 | m_Calls: [] 66 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 67 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 68 | m_FontData: 69 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 70 | m_FontSize: 14 71 | m_FontStyle: 1 72 | m_BestFit: 0 73 | m_MinSize: 10 74 | m_MaxSize: 40 75 | m_Alignment: 4 76 | m_AlignByGeometry: 0 77 | m_RichText: 1 78 | m_HorizontalOverflow: 0 79 | m_VerticalOverflow: 0 80 | m_LineSpacing: 1 81 | m_Text: Sample Name 82 | --- !u!114 &114104161713339180 83 | MonoBehaviour: 84 | m_ObjectHideFlags: 1 85 | m_PrefabParentObject: {fileID: 0} 86 | m_PrefabInternal: {fileID: 100100000} 87 | m_GameObject: {fileID: 1904131878966858} 88 | m_Enabled: 1 89 | m_EditorHideFlags: 0 90 | m_Script: {fileID: 11500000, guid: 1101c9e1e1276414d8062a6a7ca4db45, type: 3} 91 | m_Name: 92 | m_EditorClassIdentifier: 93 | _text: {fileID: 114045595599197664} 94 | --- !u!114 &114607162270506794 95 | MonoBehaviour: 96 | m_ObjectHideFlags: 1 97 | m_PrefabParentObject: {fileID: 0} 98 | m_PrefabInternal: {fileID: 100100000} 99 | m_GameObject: {fileID: 1904131878966858} 100 | m_Enabled: 1 101 | m_EditorHideFlags: 0 102 | m_Script: {fileID: 1741964061, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 103 | m_Name: 104 | m_EditorClassIdentifier: 105 | m_HorizontalFit: 2 106 | m_VerticalFit: 2 107 | --- !u!114 &114804161523563040 108 | MonoBehaviour: 109 | m_ObjectHideFlags: 1 110 | m_PrefabParentObject: {fileID: 0} 111 | m_PrefabInternal: {fileID: 100100000} 112 | m_GameObject: {fileID: 1904131878966858} 113 | m_Enabled: 1 114 | m_EditorHideFlags: 0 115 | m_Script: {fileID: 1297475563, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 116 | m_Name: 117 | m_EditorClassIdentifier: 118 | m_Padding: 119 | m_Left: 0 120 | m_Right: 0 121 | m_Top: 0 122 | m_Bottom: 0 123 | m_ChildAlignment: 0 124 | m_Spacing: 0 125 | m_ChildForceExpandWidth: 1 126 | m_ChildForceExpandHeight: 1 127 | m_ChildControlWidth: 1 128 | m_ChildControlHeight: 1 129 | --- !u!222 &222752501853716432 130 | CanvasRenderer: 131 | m_ObjectHideFlags: 1 132 | m_PrefabParentObject: {fileID: 0} 133 | m_PrefabInternal: {fileID: 100100000} 134 | m_GameObject: {fileID: 1966926200078894} 135 | --- !u!224 &224461754452575090 136 | RectTransform: 137 | m_ObjectHideFlags: 1 138 | m_PrefabParentObject: {fileID: 0} 139 | m_PrefabInternal: {fileID: 100100000} 140 | m_GameObject: {fileID: 1904131878966858} 141 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 142 | m_LocalPosition: {x: 0, y: 0, z: 0} 143 | m_LocalScale: {x: 1, y: 1, z: 1} 144 | m_Children: 145 | - {fileID: 224471434808036404} 146 | m_Father: {fileID: 0} 147 | m_RootOrder: 0 148 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 149 | m_AnchorMin: {x: 0, y: 0} 150 | m_AnchorMax: {x: 0, y: 0} 151 | m_AnchoredPosition: {x: 400, y: 0} 152 | m_SizeDelta: {x: 0, y: 0} 153 | m_Pivot: {x: 0.5, y: 0.5} 154 | --- !u!224 &224471434808036404 155 | RectTransform: 156 | m_ObjectHideFlags: 1 157 | m_PrefabParentObject: {fileID: 0} 158 | m_PrefabInternal: {fileID: 100100000} 159 | m_GameObject: {fileID: 1966926200078894} 160 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 161 | m_LocalPosition: {x: 0, y: 0, z: 0} 162 | m_LocalScale: {x: 1, y: 1, z: 1} 163 | m_Children: [] 164 | m_Father: {fileID: 224461754452575090} 165 | m_RootOrder: 0 166 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 167 | m_AnchorMin: {x: 0, y: 0} 168 | m_AnchorMax: {x: 0, y: 0} 169 | m_AnchoredPosition: {x: 0, y: 0} 170 | m_SizeDelta: {x: 0, y: 0} 171 | m_Pivot: {x: 0.5, y: 0.5} 172 | -------------------------------------------------------------------------------- /Websocket/WebSocketSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_WEBSOCKET 2 | 3 | using System; 4 | 5 | using BestHTTP.Examples.Helpers; 6 | 7 | using UnityEngine; 8 | using UnityEngine.UI; 9 | 10 | namespace BestHTTP.Examples.Websockets 11 | { 12 | public class WebSocketSample : BestHTTP.Examples.Helpers.SampleBase 13 | { 14 | #pragma warning disable 0649 15 | 16 | [SerializeField] 17 | [Tooltip("The WebSocket address to connect")] 18 | private string address = "wss://besthttpwebgldemo.azurewebsites.net/ws"; 19 | 20 | [SerializeField] 21 | private InputField _input; 22 | 23 | [SerializeField] 24 | private ScrollRect _scrollRect; 25 | 26 | [SerializeField] 27 | private RectTransform _contentRoot; 28 | 29 | [SerializeField] 30 | private TextListItem _listItemPrefab; 31 | 32 | [SerializeField] 33 | private int _maxListItemEntries = 100; 34 | 35 | [SerializeField] 36 | private Button _connectButton; 37 | 38 | [SerializeField] 39 | private Button _closeButton; 40 | 41 | #pragma warning restore 42 | 43 | /// 44 | /// Saved WebSocket instance 45 | /// 46 | WebSocket.WebSocket webSocket; 47 | 48 | protected override void Start() 49 | { 50 | base.Start(); 51 | 52 | SetButtons(true, false); 53 | this._input.interactable = false; 54 | } 55 | 56 | void OnDestroy() 57 | { 58 | if (this.webSocket != null) 59 | { 60 | this.webSocket.Close(); 61 | this.webSocket = null; 62 | } 63 | } 64 | 65 | public void OnConnectButton() 66 | { 67 | // Create the WebSocket instance 68 | this.webSocket = new WebSocket.WebSocket(new Uri(address)); 69 | 70 | #if !UNITY_WEBGL || UNITY_EDITOR 71 | this.webSocket.StartPingThread = true; 72 | 73 | #if !BESTHTTP_DISABLE_PROXY && (!UNITY_WEBGL || UNITY_EDITOR) 74 | if (HTTPManager.Proxy != null) 75 | this.webSocket.OnInternalRequestCreated = (ws, internalRequest) => internalRequest.Proxy = new HTTPProxy(HTTPManager.Proxy.Address, HTTPManager.Proxy.Credentials, false); 76 | #endif 77 | #endif 78 | 79 | // Subscribe to the WS events 80 | this.webSocket.OnOpen += OnOpen; 81 | this.webSocket.OnMessage += OnMessageReceived; 82 | this.webSocket.OnClosed += OnClosed; 83 | this.webSocket.OnError += OnError; 84 | 85 | // Start connecting to the server 86 | this.webSocket.Open(); 87 | 88 | AddText("Connecting..."); 89 | 90 | SetButtons(false, true); 91 | this._input.interactable = false; 92 | } 93 | 94 | public void OnCloseButton() 95 | { 96 | AddText("Closing!"); 97 | // Close the connection 98 | this.webSocket.Close(1000, "Bye!"); 99 | 100 | SetButtons(false, false); 101 | this._input.interactable = false; 102 | } 103 | 104 | public void OnInputField(string textToSend) 105 | { 106 | if ((!Input.GetKeyDown(KeyCode.KeypadEnter) && !Input.GetKeyDown(KeyCode.Return)) || string.IsNullOrEmpty(textToSend)) 107 | return; 108 | 109 | AddText(string.Format("Sending message: {0}", textToSend)) 110 | .AddLeftPadding(20); 111 | 112 | // Send message to the server 113 | this.webSocket.Send(textToSend); 114 | } 115 | 116 | #region WebSocket Event Handlers 117 | 118 | /// 119 | /// Called when the web socket is open, and we are ready to send and receive data 120 | /// 121 | void OnOpen(WebSocket.WebSocket ws) 122 | { 123 | AddText("WebSocket Open!"); 124 | 125 | this._input.interactable = true; 126 | } 127 | 128 | /// 129 | /// Called when we received a text message from the server 130 | /// 131 | void OnMessageReceived(WebSocket.WebSocket ws, string message) 132 | { 133 | AddText(string.Format("Message received: {0}", message)) 134 | .AddLeftPadding(20); 135 | } 136 | 137 | /// 138 | /// Called when the web socket closed 139 | /// 140 | void OnClosed(WebSocket.WebSocket ws, UInt16 code, string message) 141 | { 142 | AddText(string.Format("WebSocket closed! Code: {0} Message: {1}", code, message)); 143 | 144 | webSocket = null; 145 | 146 | SetButtons(true, false); 147 | } 148 | 149 | /// 150 | /// Called when an error occured on client side 151 | /// 152 | void OnError(WebSocket.WebSocket ws, string error) 153 | { 154 | AddText(string.Format("An error occured: {0}", error)); 155 | 156 | webSocket = null; 157 | 158 | SetButtons(true, false); 159 | } 160 | 161 | #endregion 162 | 163 | private void SetButtons(bool connect, bool close) 164 | { 165 | if (this._connectButton != null) 166 | this._connectButton.interactable = connect; 167 | 168 | if (this._closeButton != null) 169 | this._closeButton.interactable = close; 170 | } 171 | 172 | private TextListItem AddText(string text) 173 | { 174 | return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 175 | } 176 | } 177 | } 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /HTTP/TextureDownloadSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | using BestHTTP; 7 | 8 | namespace BestHTTP.Examples.HTTP 9 | { 10 | public sealed class TextureDownloadSample : BestHTTP.Examples.Helpers.SampleBase 11 | { 12 | #pragma warning disable 0649 13 | [Header("Texture Download Example")] 14 | 15 | [Tooltip("The URL of the server that will serve the image resources")] 16 | [SerializeField] 17 | private string _path = "/images/Demo/"; 18 | 19 | [Tooltip("The downloadable images")] 20 | [SerializeField] 21 | private string[] _imageNames = new string[9] { "One.png", "Two.png", "Three.png", "Four.png", "Five.png", "Six.png", "Seven.png", "Eight.png", "Nine.png" }; 22 | 23 | [SerializeField] 24 | private RawImage[] _images = new RawImage[0]; 25 | 26 | [SerializeField] 27 | private Text _maxConnectionPerServerLabel; 28 | 29 | [SerializeField] 30 | private Text _cacheLabel; 31 | 32 | #pragma warning restore 33 | 34 | private byte savedMaxConnectionPerServer; 35 | 36 | #if !BESTHTTP_DISABLE_CACHING 37 | private bool allDownloadedFromLocalCache; 38 | #endif 39 | 40 | private List activeRequests = new List(); 41 | 42 | protected override void Start() 43 | { 44 | base.Start(); 45 | 46 | this.savedMaxConnectionPerServer = HTTPManager.MaxConnectionPerServer; 47 | 48 | // Set a well observable value 49 | // This is how many concurrent requests can be made to a server 50 | HTTPManager.MaxConnectionPerServer = 1; 51 | 52 | this._maxConnectionPerServerLabel.text = HTTPManager.MaxConnectionPerServer.ToString(); 53 | } 54 | 55 | void OnDestroy() 56 | { 57 | // Set back to its defualt value. 58 | HTTPManager.MaxConnectionPerServer = this.savedMaxConnectionPerServer; 59 | foreach (var request in this.activeRequests) 60 | request.Abort(); 61 | this.activeRequests.Clear(); 62 | } 63 | 64 | public void OnMaxConnectionPerServerChanged(float value) 65 | { 66 | HTTPManager.MaxConnectionPerServer = (byte)Mathf.RoundToInt(value); 67 | this._maxConnectionPerServerLabel.text = HTTPManager.MaxConnectionPerServer.ToString(); 68 | } 69 | 70 | public void DownloadImages() 71 | { 72 | // Set these metadatas to its initial values 73 | #if !BESTHTTP_DISABLE_CACHING 74 | allDownloadedFromLocalCache = true; 75 | #endif 76 | 77 | for (int i = 0; i < _imageNames.Length; ++i) 78 | { 79 | // Set a blank placeholder texture, overriding previously downloaded texture 80 | this._images[i].texture = null; 81 | 82 | // Construct the request 83 | var request = new HTTPRequest(new Uri(this.sampleSelector.BaseURL + this._path + this._imageNames[i]), ImageDownloaded); 84 | 85 | // Set the Tag property, we can use it as a general storage bound to the request 86 | request.Tag = this._images[i]; 87 | 88 | // Send out the request 89 | request.Send(); 90 | 91 | this.activeRequests.Add(request); 92 | } 93 | 94 | this._cacheLabel.text = string.Empty; 95 | } 96 | 97 | /// 98 | /// Callback function of the image download http requests 99 | /// 100 | void ImageDownloaded(HTTPRequest req, HTTPResponse resp) 101 | { 102 | switch (req.State) 103 | { 104 | // The request finished without any problem. 105 | case HTTPRequestStates.Finished: 106 | if (resp.IsSuccess) 107 | { 108 | // The target RawImage reference is stored in the Tag property 109 | RawImage rawImage = req.Tag as RawImage; 110 | rawImage.texture = resp.DataAsTexture2D; 111 | 112 | #if !BESTHTTP_DISABLE_CACHING 113 | // Update the cache-info variable 114 | allDownloadedFromLocalCache = allDownloadedFromLocalCache && resp.IsFromCache; 115 | #endif 116 | } 117 | else 118 | { 119 | Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", 120 | resp.StatusCode, 121 | resp.Message, 122 | resp.DataAsText)); 123 | } 124 | break; 125 | 126 | // The request finished with an unexpected error. The request's Exception property may contain more info about the error. 127 | case HTTPRequestStates.Error: 128 | Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception")); 129 | break; 130 | 131 | // The request aborted, initiated by the user. 132 | case HTTPRequestStates.Aborted: 133 | Debug.LogWarning("Request Aborted!"); 134 | break; 135 | 136 | // Connecting to the server is timed out. 137 | case HTTPRequestStates.ConnectionTimedOut: 138 | Debug.LogError("Connection Timed Out!"); 139 | break; 140 | 141 | // The request didn't finished in the given time. 142 | case HTTPRequestStates.TimedOut: 143 | Debug.LogError("Processing the request Timed Out!"); 144 | break; 145 | } 146 | 147 | this.activeRequests.Remove(req); 148 | if (this.activeRequests.Count == 0) 149 | { 150 | #if !BESTHTTP_DISABLE_CACHING 151 | if (this.allDownloadedFromLocalCache) 152 | this._cacheLabel.text = "All images loaded from local cache!"; 153 | else 154 | #endif 155 | this._cacheLabel.text = string.Empty; 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /SignalRCore/HubWithAuthorizationSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using BestHTTP.Examples; 4 | using BestHTTP.Examples.Helpers; 5 | using BestHTTP.SignalRCore; 6 | using BestHTTP.SignalRCore.Encoders; 7 | using System; 8 | using UnityEngine; 9 | using UnityEngine.UI; 10 | 11 | namespace BestHTTP.Examples 12 | { 13 | /// 14 | /// A sample to demonstrate Bearer token authorization on the server. The client will connect to the /redirect route 15 | /// where it will receive the token and will receive the new url (/HubWithAuthorization) to connect to. 16 | /// HubWithAuthorization without the token would throw an error. 17 | /// 18 | public sealed class HubWithAuthorizationSample : BestHTTP.Examples.Helpers.SampleBase 19 | { 20 | #pragma warning disable 0649 21 | 22 | [SerializeField] 23 | private string _path = "/redirect"; 24 | 25 | [SerializeField] 26 | private ScrollRect _scrollRect; 27 | 28 | [SerializeField] 29 | private RectTransform _contentRoot; 30 | 31 | [SerializeField] 32 | private TextListItem _listItemPrefab; 33 | 34 | [SerializeField] 35 | private int _maxListItemEntries = 100; 36 | 37 | [SerializeField] 38 | private Button _connectButton; 39 | 40 | [SerializeField] 41 | private Button _closeButton; 42 | 43 | #pragma warning restore 44 | 45 | // Instance of the HubConnection 46 | HubConnection hub; 47 | 48 | protected override void Start() 49 | { 50 | base.Start(); 51 | 52 | SetButtons(true, false); 53 | } 54 | 55 | void OnDestroy() 56 | { 57 | if (hub != null) 58 | hub.StartClose(); 59 | } 60 | 61 | public void OnConnectButton() 62 | { 63 | // Server side of this example can be found here: 64 | // https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/ 65 | 66 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 67 | try 68 | { 69 | MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( 70 | MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, 71 | MessagePack.Unity.UnityResolver.Instance, 72 | //MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, 73 | //MessagePack.Resolvers.StandardResolver.Instance, 74 | MessagePack.Resolvers.ContractlessStandardResolver.Instance 75 | ); 76 | 77 | var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); 78 | MessagePack.MessagePackSerializer.DefaultOptions = options; 79 | } 80 | catch 81 | { } 82 | #endif 83 | 84 | IProtocol protocol = null; 85 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 86 | protocol = new MessagePackCSharpProtocol(); 87 | #elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK 88 | protocol = new MessagePackProtocol(); 89 | #else 90 | protocol = new JsonProtocol(new LitJsonEncoder()); 91 | #endif 92 | 93 | // Crete the HubConnection 94 | hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._path), protocol); 95 | 96 | // Subscribe to hub events 97 | hub.OnConnected += Hub_OnConnected; 98 | hub.OnError += Hub_OnError; 99 | hub.OnClosed += Hub_OnClosed; 100 | 101 | hub.OnRedirected += Hub_Redirected; 102 | 103 | hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport({0}) event: {1}", transport.TransportType, ev)); 104 | 105 | // And finally start to connect to the server 106 | hub.StartConnect(); 107 | 108 | AddText("StartConnect called"); 109 | SetButtons(false, false); 110 | } 111 | 112 | public void OnCloseButton() 113 | { 114 | if (hub != null) 115 | { 116 | hub.StartClose(); 117 | 118 | AddText("StartClose called"); 119 | SetButtons(false, false); 120 | } 121 | } 122 | 123 | private void Hub_Redirected(HubConnection hub, Uri oldUri, Uri newUri) 124 | { 125 | AddText(string.Format("Hub connection redirected to '{0}' with Access Token: '{1}'", hub.Uri, hub.NegotiationResult.AccessToken)); 126 | } 127 | 128 | /// 129 | /// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. 130 | /// 131 | private void Hub_OnConnected(HubConnection hub) 132 | { 133 | AddText(string.Format("Hub Connected with {0} transport using the {1} encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); 134 | SetButtons(false, true); 135 | 136 | // Call a parameterless function. We expect a string return value. 137 | hub.Invoke("Echo", "Message from the client") 138 | .OnSuccess(ret => AddText(string.Format("'Echo' returned: '{0}'", ret)).AddLeftPadding(20)); 139 | 140 | AddText("'Message from the client' sent!") 141 | .AddLeftPadding(20); 142 | } 143 | 144 | /// 145 | /// This is called when the hub is closed after a StartClose() call. 146 | /// 147 | private void Hub_OnClosed(HubConnection hub) 148 | { 149 | AddText("Hub Closed"); 150 | SetButtons(true, false); 151 | } 152 | 153 | /// 154 | /// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. 155 | /// 156 | private void Hub_OnError(HubConnection hub, string error) 157 | { 158 | AddText(string.Format("Hub Error: {0}", error)); 159 | SetButtons(true, false); 160 | } 161 | 162 | private void SetButtons(bool connect, bool close) 163 | { 164 | if (this._connectButton != null) 165 | this._connectButton.interactable = connect; 166 | 167 | if (this._closeButton != null) 168 | this._closeButton.interactable = close; 169 | } 170 | 171 | private TextListItem AddText(string text) 172 | { 173 | return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 174 | } 175 | } 176 | } 177 | 178 | #endif 179 | -------------------------------------------------------------------------------- /Helpers/TextListItem.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1001 &100100000 4 | Prefab: 5 | m_ObjectHideFlags: 1 6 | serializedVersion: 2 7 | m_Modification: 8 | m_TransformParent: {fileID: 0} 9 | m_Modifications: [] 10 | m_RemovedComponents: [] 11 | m_ParentPrefab: {fileID: 0} 12 | m_RootGameObject: {fileID: 1727935926237334} 13 | m_IsPrefabParent: 1 14 | --- !u!1 &1503879635514882 15 | GameObject: 16 | m_ObjectHideFlags: 0 17 | m_PrefabParentObject: {fileID: 0} 18 | m_PrefabInternal: {fileID: 100100000} 19 | serializedVersion: 5 20 | m_Component: 21 | - component: {fileID: 224106021874867618} 22 | - component: {fileID: 222738347872656220} 23 | - component: {fileID: 114900463745114476} 24 | m_Layer: 5 25 | m_Name: Text 26 | m_TagString: Untagged 27 | m_Icon: {fileID: 0} 28 | m_NavMeshLayer: 0 29 | m_StaticEditorFlags: 0 30 | m_IsActive: 1 31 | --- !u!1 &1727935926237334 32 | GameObject: 33 | m_ObjectHideFlags: 0 34 | m_PrefabParentObject: {fileID: 0} 35 | m_PrefabInternal: {fileID: 100100000} 36 | serializedVersion: 5 37 | m_Component: 38 | - component: {fileID: 224916463173798366} 39 | - component: {fileID: 222969618919079142} 40 | - component: {fileID: 114878452276715702} 41 | - component: {fileID: 114769119028883068} 42 | - component: {fileID: 114599056085522236} 43 | - component: {fileID: 114144824129317776} 44 | m_Layer: 5 45 | m_Name: TextListItem 46 | m_TagString: Untagged 47 | m_Icon: {fileID: 0} 48 | m_NavMeshLayer: 0 49 | m_StaticEditorFlags: 0 50 | m_IsActive: 1 51 | --- !u!114 &114144824129317776 52 | MonoBehaviour: 53 | m_ObjectHideFlags: 1 54 | m_PrefabParentObject: {fileID: 0} 55 | m_PrefabInternal: {fileID: 100100000} 56 | m_GameObject: {fileID: 1727935926237334} 57 | m_Enabled: 1 58 | m_EditorHideFlags: 0 59 | m_Script: {fileID: 11500000, guid: 05975660b0231b84f849693106b207d1, type: 3} 60 | m_Name: 61 | m_EditorClassIdentifier: 62 | _text: {fileID: 114900463745114476} 63 | --- !u!114 &114599056085522236 64 | MonoBehaviour: 65 | m_ObjectHideFlags: 1 66 | m_PrefabParentObject: {fileID: 0} 67 | m_PrefabInternal: {fileID: 100100000} 68 | m_GameObject: {fileID: 1727935926237334} 69 | m_Enabled: 1 70 | m_EditorHideFlags: 0 71 | m_Script: {fileID: 1741964061, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 72 | m_Name: 73 | m_EditorClassIdentifier: 74 | m_HorizontalFit: 0 75 | m_VerticalFit: 2 76 | --- !u!114 &114769119028883068 77 | MonoBehaviour: 78 | m_ObjectHideFlags: 1 79 | m_PrefabParentObject: {fileID: 0} 80 | m_PrefabInternal: {fileID: 100100000} 81 | m_GameObject: {fileID: 1727935926237334} 82 | m_Enabled: 1 83 | m_EditorHideFlags: 0 84 | m_Script: {fileID: 1297475563, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 85 | m_Name: 86 | m_EditorClassIdentifier: 87 | m_Padding: 88 | m_Left: 5 89 | m_Right: 2 90 | m_Top: 2 91 | m_Bottom: 2 92 | m_ChildAlignment: 0 93 | m_Spacing: 2 94 | m_ChildForceExpandWidth: 1 95 | m_ChildForceExpandHeight: 1 96 | m_ChildControlWidth: 1 97 | m_ChildControlHeight: 1 98 | --- !u!114 &114878452276715702 99 | MonoBehaviour: 100 | m_ObjectHideFlags: 1 101 | m_PrefabParentObject: {fileID: 0} 102 | m_PrefabInternal: {fileID: 100100000} 103 | m_GameObject: {fileID: 1727935926237334} 104 | m_Enabled: 1 105 | m_EditorHideFlags: 0 106 | m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 107 | m_Name: 108 | m_EditorClassIdentifier: 109 | m_Material: {fileID: 0} 110 | m_Color: {r: 1, g: 1, b: 1, a: 0.39215687} 111 | m_RaycastTarget: 0 112 | m_OnCullStateChanged: 113 | m_PersistentCalls: 114 | m_Calls: [] 115 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 116 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 117 | m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} 118 | m_Type: 1 119 | m_PreserveAspect: 0 120 | m_FillCenter: 1 121 | m_FillMethod: 4 122 | m_FillAmount: 1 123 | m_FillClockwise: 1 124 | m_FillOrigin: 0 125 | --- !u!114 &114900463745114476 126 | MonoBehaviour: 127 | m_ObjectHideFlags: 1 128 | m_PrefabParentObject: {fileID: 0} 129 | m_PrefabInternal: {fileID: 100100000} 130 | m_GameObject: {fileID: 1503879635514882} 131 | m_Enabled: 1 132 | m_EditorHideFlags: 0 133 | m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 134 | m_Name: 135 | m_EditorClassIdentifier: 136 | m_Material: {fileID: 0} 137 | m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} 138 | m_RaycastTarget: 0 139 | m_OnCullStateChanged: 140 | m_PersistentCalls: 141 | m_Calls: [] 142 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 143 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 144 | m_FontData: 145 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 146 | m_FontSize: 10 147 | m_FontStyle: 0 148 | m_BestFit: 0 149 | m_MinSize: 1 150 | m_MaxSize: 40 151 | m_Alignment: 0 152 | m_AlignByGeometry: 0 153 | m_RichText: 1 154 | m_HorizontalOverflow: 0 155 | m_VerticalOverflow: 0 156 | m_LineSpacing: 1 157 | m_Text: New Text 158 | --- !u!222 &222738347872656220 159 | CanvasRenderer: 160 | m_ObjectHideFlags: 1 161 | m_PrefabParentObject: {fileID: 0} 162 | m_PrefabInternal: {fileID: 100100000} 163 | m_GameObject: {fileID: 1503879635514882} 164 | --- !u!222 &222969618919079142 165 | CanvasRenderer: 166 | m_ObjectHideFlags: 1 167 | m_PrefabParentObject: {fileID: 0} 168 | m_PrefabInternal: {fileID: 100100000} 169 | m_GameObject: {fileID: 1727935926237334} 170 | --- !u!224 &224106021874867618 171 | RectTransform: 172 | m_ObjectHideFlags: 1 173 | m_PrefabParentObject: {fileID: 0} 174 | m_PrefabInternal: {fileID: 100100000} 175 | m_GameObject: {fileID: 1503879635514882} 176 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 177 | m_LocalPosition: {x: 0, y: 0, z: 0} 178 | m_LocalScale: {x: 1, y: 1, z: 1} 179 | m_Children: [] 180 | m_Father: {fileID: 224916463173798366} 181 | m_RootOrder: 0 182 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 183 | m_AnchorMin: {x: 0, y: 0} 184 | m_AnchorMax: {x: 0, y: 0} 185 | m_AnchoredPosition: {x: 0, y: 0} 186 | m_SizeDelta: {x: 0, y: 0} 187 | m_Pivot: {x: 0.5, y: 0.5} 188 | --- !u!224 &224916463173798366 189 | RectTransform: 190 | m_ObjectHideFlags: 1 191 | m_PrefabParentObject: {fileID: 0} 192 | m_PrefabInternal: {fileID: 100100000} 193 | m_GameObject: {fileID: 1727935926237334} 194 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 195 | m_LocalPosition: {x: 0, y: 0, z: 0} 196 | m_LocalScale: {x: 1, y: 1, z: 1} 197 | m_Children: 198 | - {fileID: 224106021874867618} 199 | m_Father: {fileID: 0} 200 | m_RootOrder: 0 201 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 202 | m_AnchorMin: {x: 0, y: 0} 203 | m_AnchorMax: {x: 0, y: 0} 204 | m_AnchoredPosition: {x: 400, y: 0} 205 | m_SizeDelta: {x: 800, y: 0} 206 | m_Pivot: {x: 0.5, y: 0.5} 207 | -------------------------------------------------------------------------------- /HTTP/UploadStream.cs: -------------------------------------------------------------------------------- 1 | using BestHTTP; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | 6 | namespace BestHTTP.Examples 7 | { 8 | public sealed class UploadStream : Stream 9 | { 10 | #region Private Fields 11 | 12 | /// 13 | /// Buffer for reads 14 | /// 15 | MemoryStream ReadBuffer = new MemoryStream(); 16 | 17 | /// 18 | /// Buffer for writes 19 | /// 20 | MemoryStream WriteBuffer = new MemoryStream(); 21 | 22 | /// 23 | /// Indicates that we will not write more data to this stream 24 | /// 25 | bool noMoreData; 26 | 27 | /// 28 | /// For thread synchronization 29 | /// 30 | AutoResetEvent ARE = new AutoResetEvent(false); 31 | 32 | /// 33 | /// For thread synchronization 34 | /// 35 | object locker = new object(); 36 | 37 | #endregion 38 | 39 | #region Properties 40 | 41 | /// 42 | /// Name of this stream for easier debugging 43 | /// 44 | public string Name { get; private set; } 45 | 46 | /// 47 | /// true if we are read all data from the read buffer 48 | /// 49 | private bool IsReadBufferEmpty { get { lock (locker) return ReadBuffer.Position == ReadBuffer.Length; } } 50 | 51 | #endregion 52 | 53 | #region Constructors 54 | 55 | public UploadStream(string name) 56 | : this() 57 | { 58 | this.Name = name; 59 | } 60 | 61 | public UploadStream() 62 | { 63 | this.ReadBuffer = new MemoryStream(); 64 | this.WriteBuffer = new MemoryStream(); 65 | this.Name = string.Empty; 66 | } 67 | 68 | #endregion 69 | 70 | #region Stream Implementation 71 | 72 | public override int Read(byte[] buffer, int offset, int count) 73 | { 74 | // We will not push more data to the write buffer 75 | if (noMoreData) 76 | { 77 | // No data left in the read buffer 78 | if (ReadBuffer.Position == ReadBuffer.Length) 79 | { 80 | // Is there any data in the write buffer? If so, switch the buffers 81 | if (WriteBuffer.Length > 0) 82 | SwitchBuffers(); 83 | else 84 | { 85 | HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Read - End Of Stream", this.Name)); 86 | return -1; 87 | } 88 | } 89 | else 90 | return ReadBuffer.Read(buffer, offset, count); 91 | } 92 | 93 | // There are no more data in the read buffer? Wait for it. 94 | if (IsReadBufferEmpty) 95 | { 96 | ARE.WaitOne(); 97 | 98 | lock (locker) 99 | if (IsReadBufferEmpty && WriteBuffer.Length > 0) 100 | SwitchBuffers(); 101 | } 102 | 103 | int read = -1; 104 | 105 | lock (locker) 106 | read = ReadBuffer.Read(buffer, offset, count); 107 | 108 | return read; 109 | } 110 | 111 | public override void Write(byte[] buffer, int offset, int count) 112 | { 113 | if (noMoreData) 114 | throw new System.ArgumentException("noMoreData already set!"); 115 | 116 | lock (locker) 117 | { 118 | WriteBuffer.Write(buffer, offset, count); 119 | 120 | SwitchBuffers(); 121 | } 122 | 123 | ARE.Set(); 124 | } 125 | 126 | public override void Flush() 127 | { 128 | Finish(); 129 | } 130 | 131 | #endregion 132 | 133 | #region Dispose Implementation 134 | 135 | protected override void Dispose(bool disposing) 136 | { 137 | if (disposing) 138 | { 139 | HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Dispose", this.Name)); 140 | 141 | ReadBuffer.Dispose(); 142 | ReadBuffer = null; 143 | 144 | WriteBuffer.Dispose(); 145 | WriteBuffer = null; 146 | 147 | #if NETFX_CORE 148 | ARE.Dispose(); 149 | #else 150 | ARE.Close(); 151 | #endif 152 | ARE = null; 153 | } 154 | 155 | base.Dispose(disposing); 156 | } 157 | 158 | #endregion 159 | 160 | #region Helper Functions 161 | 162 | public void Finish() 163 | { 164 | if (noMoreData) 165 | throw new System.ArgumentException("noMoreData already set!"); 166 | 167 | HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Finish", this.Name)); 168 | 169 | noMoreData = true; 170 | 171 | ARE.Set(); 172 | } 173 | 174 | private bool SwitchBuffers() 175 | { 176 | // Switch the buffers only when all data are consumed from our read buffer 177 | lock (locker) 178 | { 179 | if (ReadBuffer.Position == ReadBuffer.Length) 180 | { 181 | // This buffer will be the read buffer, we need to seek back to the beginning 182 | WriteBuffer.Seek(0, SeekOrigin.Begin); 183 | 184 | // This will be the write buffer, set the length to zero 185 | ReadBuffer.SetLength(0); 186 | 187 | // switch the two buffers 188 | MemoryStream tmp = WriteBuffer; 189 | WriteBuffer = ReadBuffer; 190 | ReadBuffer = tmp; 191 | 192 | return true; 193 | } 194 | } 195 | 196 | return false; 197 | } 198 | 199 | #endregion 200 | 201 | #region Not Implemented Functions and Properties 202 | 203 | public override bool CanRead { get { throw new NotImplementedException(); } } 204 | public override bool CanSeek { get { throw new NotImplementedException(); } } 205 | public override bool CanWrite { get { throw new NotImplementedException(); } } 206 | 207 | public override long Length { get { throw new NotImplementedException(); } } 208 | public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } 209 | 210 | public override long Seek(long offset, SeekOrigin origin) 211 | { 212 | throw new NotImplementedException(); 213 | } 214 | 215 | public override void SetLength(long value) 216 | { 217 | throw new NotImplementedException(); 218 | } 219 | 220 | #endregion 221 | } 222 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /HTTP/MultipartFormDataStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using BestHTTP.Extensions; 5 | using BestHTTP.PlatformSupport.Memory; 6 | 7 | namespace BestHTTP 8 | { 9 | /// 10 | /// Stream based implementation of the multipart/form-data Content-Type. Using this class reading a whole file into memory can be avoided. 11 | /// This implementation expects that all streams has a final, accessible Length. 12 | /// 13 | public sealed class MultipartFormDataStream : System.IO.Stream 14 | { 15 | public override bool CanRead { get { return true; } } 16 | 17 | public override bool CanSeek { get { return false; } } 18 | 19 | public override bool CanWrite { get { return false; } } 20 | 21 | public override long Length 22 | { 23 | get 24 | { 25 | // multipart/form-data requires a leading boundary that we can add when all streams are added. 26 | // This final preparation could be user initiated, but we can do it automatically too when the HTTPRequest 27 | // first access the Length property. 28 | if (!this.prepared) 29 | { 30 | this.prepared = true; 31 | this.Prepare(); 32 | } 33 | 34 | return this._length; 35 | } 36 | } 37 | private long _length; 38 | 39 | public override long Position { get; set; } 40 | /// 41 | /// A random boundary generated in the constructor. 42 | /// 43 | private string boundary; 44 | 45 | private Queue fields = new Queue(1); 46 | private StreamList currentField; 47 | private bool prepared; 48 | 49 | public MultipartFormDataStream(HTTPRequest request) 50 | { 51 | this.boundary = "BestHTTP_MultipartFormDataStream_" + this.GetHashCode().ToString("X2"); 52 | 53 | request.SetHeader("Content-Type", "multipart/form-data; boundary=" + boundary); 54 | request.UploadStream = this; 55 | request.UseUploadStreamLength = true; 56 | } 57 | 58 | public void AddField(string fieldName, string value) 59 | { 60 | AddField(fieldName, value, System.Text.Encoding.UTF8); 61 | } 62 | 63 | public void AddField(string fieldName, string value, System.Text.Encoding encoding) 64 | { 65 | var enc = encoding ?? System.Text.Encoding.UTF8; 66 | var byteCount = enc.GetByteCount(value); 67 | var buffer = BufferPool.Get(byteCount, true); 68 | var stream = new BufferPoolMemoryStream(); 69 | 70 | enc.GetBytes(value, 0, value.Length, buffer, 0); 71 | 72 | stream.Write(buffer, 0, byteCount); 73 | 74 | stream.Position = 0; 75 | 76 | string mime = encoding != null ? "text/plain; charset=" + encoding.WebName : null; 77 | AddStreamField(stream, fieldName, null, mime); 78 | } 79 | 80 | public void AddStreamField(System.IO.Stream stream, string fieldName) 81 | { 82 | AddStreamField(stream, fieldName, null, null); 83 | } 84 | 85 | public void AddStreamField(System.IO.Stream stream, string fieldName, string fileName) 86 | { 87 | AddStreamField(stream, fieldName, fileName, null); 88 | } 89 | 90 | public void AddStreamField(System.IO.Stream stream, string fieldName, string fileName, string mimeType) 91 | { 92 | var header = new BufferPoolMemoryStream(); 93 | header.WriteLine("--" + this.boundary); 94 | header.WriteLine("Content-Disposition: form-data; name=\"" + fieldName + "\"" + (!string.IsNullOrEmpty(fileName) ? "; filename=\"" + fileName + "\"" : string.Empty)); 95 | // Set up Content-Type head for the form. 96 | if (!string.IsNullOrEmpty(mimeType)) 97 | header.WriteLine("Content-Type: " + mimeType); 98 | //header.WriteLine("Content-Length: " + stream.Length.ToString()); 99 | header.WriteLine(); 100 | header.Position = 0; 101 | 102 | var footer = new BufferPoolMemoryStream(); 103 | footer.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length); 104 | footer.Position = 0; 105 | 106 | // all wrapped streams going to be disposed by the StreamList wrapper. 107 | var wrapper = new StreamList(header, stream, footer); 108 | 109 | try 110 | { 111 | if (this._length >= 0) 112 | this._length += wrapper.Length; 113 | } 114 | catch 115 | { 116 | this._length = -1; 117 | } 118 | 119 | this.fields.Enqueue(wrapper); 120 | } 121 | 122 | /// 123 | /// Adds the final boundary. 124 | /// 125 | private void Prepare() 126 | { 127 | var boundaryStream = new BufferPoolMemoryStream(); 128 | boundaryStream.WriteLine("--" + this.boundary + "--"); 129 | boundaryStream.Position = 0; 130 | 131 | this.fields.Enqueue(new StreamList(boundaryStream)); 132 | 133 | if (this._length >= 0) 134 | this._length += boundaryStream.Length; 135 | } 136 | 137 | public override int Read(byte[] buffer, int offset, int length) 138 | { 139 | if (this.currentField == null && this.fields.Count == 0) 140 | return -1; 141 | 142 | if (this.currentField == null && this.fields.Count > 0) 143 | this.currentField = this.fields.Dequeue(); 144 | 145 | int readCount = 0; 146 | 147 | do 148 | { 149 | // read from the current stream 150 | int count = this.currentField.Read(buffer, offset + readCount, length - readCount); 151 | 152 | if (count > 0) 153 | readCount += count; 154 | else 155 | { 156 | // if the current field's stream is empty, go for the next one. 157 | 158 | // dispose the current one first 159 | try 160 | { 161 | this.currentField.Dispose(); 162 | } 163 | catch 164 | { } 165 | 166 | // no more fields/streams? exit 167 | if (this.fields.Count == 0) 168 | break; 169 | 170 | // grab the next one 171 | this.currentField = this.fields.Dequeue(); 172 | } 173 | 174 | // exit when we reach the length goal, or there's no more streams to read from 175 | } while (readCount < length && this.fields.Count > 0); 176 | 177 | return readCount; 178 | } 179 | 180 | public override long Seek(long offset, SeekOrigin origin) 181 | { 182 | throw new NotImplementedException(); 183 | } 184 | 185 | public override void SetLength(long value) 186 | { 187 | throw new NotImplementedException(); 188 | } 189 | 190 | public override void Write(byte[] buffer, int offset, int count) 191 | { 192 | throw new NotImplementedException(); 193 | } 194 | 195 | public override void Flush() { } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /HTTP/AssetBundleSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | using UnityEngine; 6 | using UnityEngine.UI; 7 | using BestHTTP; 8 | 9 | namespace BestHTTP.Examples.HTTP 10 | { 11 | public sealed class AssetBundleSample : BestHTTP.Examples.Helpers.SampleBase 12 | { 13 | #pragma warning disable 0649 14 | 15 | [Tooltip("The url of the resource to download")] 16 | [SerializeField] 17 | private string _path = "/AssetBundles/WebGL/demobundle.assetbundle"; 18 | 19 | [SerializeField] 20 | private string _assetnameInBundle = "9443182_orig"; 21 | 22 | [SerializeField] 23 | private Text _statusText; 24 | 25 | [SerializeField] 26 | private RawImage _rawImage; 27 | 28 | [SerializeField] 29 | private Button _downloadButton; 30 | 31 | #pragma warning restore 32 | 33 | #region Private Fields 34 | 35 | /// 36 | /// Reference to the request to be able to call Abort on it. 37 | /// 38 | HTTPRequest request; 39 | 40 | /// 41 | /// The downloaded and cached AssetBundle 42 | /// 43 | AssetBundle cachedBundle; 44 | 45 | #endregion 46 | 47 | #region Unity Events 48 | 49 | protected override void Start() 50 | { 51 | base.Start(); 52 | 53 | this._statusText.text = "Waiting for user interaction"; 54 | } 55 | 56 | void OnDestroy() 57 | { 58 | if (this.request != null) 59 | this.request.Abort(); 60 | this.request = null; 61 | 62 | UnloadBundle(); 63 | } 64 | 65 | /// 66 | /// GUI button callback 67 | /// 68 | public void OnStartDownloadButton() 69 | { 70 | this._downloadButton.enabled = false; 71 | UnloadBundle(); 72 | 73 | StartCoroutine(DownloadAssetBundle()); 74 | } 75 | 76 | #endregion 77 | 78 | #region Private Helper Functions 79 | 80 | IEnumerator DownloadAssetBundle() 81 | { 82 | // Create and send our request 83 | request = new HTTPRequest(new Uri(this.sampleSelector.BaseURL + this._path)).Send(); 84 | 85 | this._statusText.text = "Download started"; 86 | 87 | // Wait while it's finishes and add some fancy dots to display something while the user waits for it. 88 | // A simple "yield return StartCoroutine(request);" would do the job too. 89 | while (request.State < HTTPRequestStates.Finished) 90 | { 91 | yield return new WaitForSeconds(0.1f); 92 | 93 | this._statusText.text += "."; 94 | } 95 | 96 | // Check the outcome of our request. 97 | switch (request.State) 98 | { 99 | // The request finished without any problem. 100 | case HTTPRequestStates.Finished: 101 | 102 | if (request.Response.IsSuccess) 103 | { 104 | #if !BESTHTTP_DISABLE_CACHING 105 | if (request.Response.IsFromCache) 106 | this._statusText.text = "Loaded from local cache!"; 107 | else 108 | this._statusText.text = "Downloaded!"; 109 | #else 110 | this._statusText.text = "Downloaded!"; 111 | #endif 112 | 113 | // Start creating the downloaded asset bundle 114 | AssetBundleCreateRequest async = 115 | #if UNITY_5_3_OR_NEWER 116 | AssetBundle.LoadFromMemoryAsync(request.Response.Data); 117 | #else 118 | AssetBundle.CreateFromMemory(request.Response.Data); 119 | #endif 120 | 121 | // wait for it 122 | yield return async; 123 | 124 | BestHTTP.PlatformSupport.Memory.BufferPool.Release(request.Response.Data); 125 | 126 | // And process the bundle 127 | yield return StartCoroutine(ProcessAssetBundle(async.assetBundle)); 128 | } 129 | else 130 | { 131 | this._statusText.text = string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", 132 | request.Response.StatusCode, 133 | request.Response.Message, 134 | request.Response.DataAsText); 135 | Debug.LogWarning(this._statusText.text); 136 | } 137 | 138 | break; 139 | 140 | // The request finished with an unexpected error. The request's Exception property may contain more info about the error. 141 | case HTTPRequestStates.Error: 142 | this._statusText.text = "Request Finished with Error! " + (request.Exception != null ? (request.Exception.Message + "\n" + request.Exception.StackTrace) : "No Exception"); 143 | Debug.LogError(this._statusText.text); 144 | break; 145 | 146 | // The request aborted, initiated by the user. 147 | case HTTPRequestStates.Aborted: 148 | this._statusText.text = "Request Aborted!"; 149 | Debug.LogWarning(this._statusText.text); 150 | break; 151 | 152 | // Connecting to the server is timed out. 153 | case HTTPRequestStates.ConnectionTimedOut: 154 | this._statusText.text = "Connection Timed Out!"; 155 | Debug.LogError(this._statusText.text); 156 | break; 157 | 158 | // The request didn't finished in the given time. 159 | case HTTPRequestStates.TimedOut: 160 | this._statusText.text = "Processing the request Timed Out!"; 161 | Debug.LogError(this._statusText.text); 162 | break; 163 | } 164 | 165 | this._downloadButton.enabled = true; 166 | } 167 | 168 | /// 169 | /// In this function we can do whatever we want with the freshly downloaded bundle. 170 | /// In this example we will cache it for later use, and we will load a texture from it. 171 | /// 172 | IEnumerator ProcessAssetBundle(AssetBundle bundle) 173 | { 174 | if (bundle == null) 175 | yield break; 176 | 177 | // Save the bundle for future use 178 | cachedBundle = bundle; 179 | 180 | // Start loading the asset from the bundle 181 | var asyncAsset = 182 | #if UNITY_5_1 || UNITY_5_2 || UNITY_5_3_OR_NEWER 183 | cachedBundle.LoadAssetAsync(this._assetnameInBundle, typeof(Texture2D)); 184 | #else 185 | 186 | cachedBundle.LoadAsync(this._assetnameInBundle, typeof(Texture2D)); 187 | #endif 188 | 189 | // wait til load 190 | yield return asyncAsset; 191 | 192 | // get the texture 193 | this._rawImage.texture = asyncAsset.asset as Texture2D; 194 | } 195 | 196 | void UnloadBundle() 197 | { 198 | this._rawImage.texture = null; 199 | 200 | if (cachedBundle != null) 201 | { 202 | cachedBundle.Unload(true); 203 | cachedBundle = null; 204 | } 205 | } 206 | 207 | #endregion 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Helpers/SelectorUI/ExampleListItem.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1001 &100100000 4 | Prefab: 5 | m_ObjectHideFlags: 1 6 | serializedVersion: 2 7 | m_Modification: 8 | m_TransformParent: {fileID: 0} 9 | m_Modifications: [] 10 | m_RemovedComponents: [] 11 | m_ParentPrefab: {fileID: 0} 12 | m_RootGameObject: {fileID: 1972462828926934} 13 | m_IsPrefabParent: 1 14 | --- !u!1 &1706707392201452 15 | GameObject: 16 | m_ObjectHideFlags: 0 17 | m_PrefabParentObject: {fileID: 0} 18 | m_PrefabInternal: {fileID: 100100000} 19 | serializedVersion: 5 20 | m_Component: 21 | - component: {fileID: 224973040142512786} 22 | - component: {fileID: 222441293650573372} 23 | - component: {fileID: 114389340898110916} 24 | m_Layer: 5 25 | m_Name: Text 26 | m_TagString: Untagged 27 | m_Icon: {fileID: 0} 28 | m_NavMeshLayer: 0 29 | m_StaticEditorFlags: 0 30 | m_IsActive: 1 31 | --- !u!1 &1972462828926934 32 | GameObject: 33 | m_ObjectHideFlags: 0 34 | m_PrefabParentObject: {fileID: 0} 35 | m_PrefabInternal: {fileID: 100100000} 36 | serializedVersion: 5 37 | m_Component: 38 | - component: {fileID: 224145737800144754} 39 | - component: {fileID: 222972952305363732} 40 | - component: {fileID: 114266276035656386} 41 | - component: {fileID: 114629809360314506} 42 | - component: {fileID: 114416681848148688} 43 | m_Layer: 5 44 | m_Name: ExampleListItem 45 | m_TagString: Untagged 46 | m_Icon: {fileID: 0} 47 | m_NavMeshLayer: 0 48 | m_StaticEditorFlags: 0 49 | m_IsActive: 1 50 | --- !u!114 &114266276035656386 51 | MonoBehaviour: 52 | m_ObjectHideFlags: 1 53 | m_PrefabParentObject: {fileID: 0} 54 | m_PrefabInternal: {fileID: 100100000} 55 | m_GameObject: {fileID: 1972462828926934} 56 | m_Enabled: 1 57 | m_EditorHideFlags: 0 58 | m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 59 | m_Name: 60 | m_EditorClassIdentifier: 61 | m_Material: {fileID: 0} 62 | m_Color: {r: 1, g: 1, b: 1, a: 1} 63 | m_RaycastTarget: 1 64 | m_OnCullStateChanged: 65 | m_PersistentCalls: 66 | m_Calls: [] 67 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 68 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 69 | m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} 70 | m_Type: 1 71 | m_PreserveAspect: 0 72 | m_FillCenter: 1 73 | m_FillMethod: 4 74 | m_FillAmount: 1 75 | m_FillClockwise: 1 76 | m_FillOrigin: 0 77 | --- !u!114 &114389340898110916 78 | MonoBehaviour: 79 | m_ObjectHideFlags: 1 80 | m_PrefabParentObject: {fileID: 0} 81 | m_PrefabInternal: {fileID: 100100000} 82 | m_GameObject: {fileID: 1706707392201452} 83 | m_Enabled: 1 84 | m_EditorHideFlags: 0 85 | m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 86 | m_Name: 87 | m_EditorClassIdentifier: 88 | m_Material: {fileID: 0} 89 | m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} 90 | m_RaycastTarget: 1 91 | m_OnCullStateChanged: 92 | m_PersistentCalls: 93 | m_Calls: [] 94 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 95 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 96 | m_FontData: 97 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 98 | m_FontSize: 14 99 | m_FontStyle: 0 100 | m_BestFit: 0 101 | m_MinSize: 10 102 | m_MaxSize: 40 103 | m_Alignment: 4 104 | m_AlignByGeometry: 0 105 | m_RichText: 1 106 | m_HorizontalOverflow: 0 107 | m_VerticalOverflow: 0 108 | m_LineSpacing: 1 109 | m_Text: Button 110 | --- !u!114 &114416681848148688 111 | MonoBehaviour: 112 | m_ObjectHideFlags: 1 113 | m_PrefabParentObject: {fileID: 0} 114 | m_PrefabInternal: {fileID: 100100000} 115 | m_GameObject: {fileID: 1972462828926934} 116 | m_Enabled: 1 117 | m_EditorHideFlags: 0 118 | m_Script: {fileID: 11500000, guid: 376fb4eb0776e41479fb60f520aaa1f3, type: 3} 119 | m_Name: 120 | m_EditorClassIdentifier: 121 | _text: {fileID: 114389340898110916} 122 | --- !u!114 &114629809360314506 123 | MonoBehaviour: 124 | m_ObjectHideFlags: 1 125 | m_PrefabParentObject: {fileID: 0} 126 | m_PrefabInternal: {fileID: 100100000} 127 | m_GameObject: {fileID: 1972462828926934} 128 | m_Enabled: 1 129 | m_EditorHideFlags: 0 130 | m_Script: {fileID: 1392445389, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 131 | m_Name: 132 | m_EditorClassIdentifier: 133 | m_Navigation: 134 | m_Mode: 3 135 | m_SelectOnUp: {fileID: 0} 136 | m_SelectOnDown: {fileID: 0} 137 | m_SelectOnLeft: {fileID: 0} 138 | m_SelectOnRight: {fileID: 0} 139 | m_Transition: 1 140 | m_Colors: 141 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 142 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 143 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 144 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 145 | m_ColorMultiplier: 1 146 | m_FadeDuration: 0.1 147 | m_SpriteState: 148 | m_HighlightedSprite: {fileID: 0} 149 | m_PressedSprite: {fileID: 0} 150 | m_DisabledSprite: {fileID: 0} 151 | m_AnimationTriggers: 152 | m_NormalTrigger: Normal 153 | m_HighlightedTrigger: Highlighted 154 | m_PressedTrigger: Pressed 155 | m_DisabledTrigger: Disabled 156 | m_Interactable: 1 157 | m_TargetGraphic: {fileID: 114266276035656386} 158 | m_OnClick: 159 | m_PersistentCalls: 160 | m_Calls: 161 | - m_Target: {fileID: 114416681848148688} 162 | m_MethodName: OnButton 163 | m_Mode: 1 164 | m_Arguments: 165 | m_ObjectArgument: {fileID: 0} 166 | m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine 167 | m_IntArgument: 0 168 | m_FloatArgument: 0 169 | m_StringArgument: 170 | m_BoolArgument: 0 171 | m_CallState: 2 172 | m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, 173 | Culture=neutral, PublicKeyToken=null 174 | --- !u!222 &222441293650573372 175 | CanvasRenderer: 176 | m_ObjectHideFlags: 1 177 | m_PrefabParentObject: {fileID: 0} 178 | m_PrefabInternal: {fileID: 100100000} 179 | m_GameObject: {fileID: 1706707392201452} 180 | --- !u!222 &222972952305363732 181 | CanvasRenderer: 182 | m_ObjectHideFlags: 1 183 | m_PrefabParentObject: {fileID: 0} 184 | m_PrefabInternal: {fileID: 100100000} 185 | m_GameObject: {fileID: 1972462828926934} 186 | --- !u!224 &224145737800144754 187 | RectTransform: 188 | m_ObjectHideFlags: 1 189 | m_PrefabParentObject: {fileID: 0} 190 | m_PrefabInternal: {fileID: 100100000} 191 | m_GameObject: {fileID: 1972462828926934} 192 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 193 | m_LocalPosition: {x: 0, y: 0, z: 0} 194 | m_LocalScale: {x: 1, y: 1, z: 1} 195 | m_Children: 196 | - {fileID: 224973040142512786} 197 | m_Father: {fileID: 0} 198 | m_RootOrder: 0 199 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 200 | m_AnchorMin: {x: 0, y: 0} 201 | m_AnchorMax: {x: 0, y: 0} 202 | m_AnchoredPosition: {x: 0, y: 0} 203 | m_SizeDelta: {x: 160, y: 30} 204 | m_Pivot: {x: 0.5, y: 0.5} 205 | --- !u!224 &224973040142512786 206 | RectTransform: 207 | m_ObjectHideFlags: 1 208 | m_PrefabParentObject: {fileID: 0} 209 | m_PrefabInternal: {fileID: 100100000} 210 | m_GameObject: {fileID: 1706707392201452} 211 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 212 | m_LocalPosition: {x: 0, y: 0, z: 0} 213 | m_LocalScale: {x: 1, y: 1, z: 1} 214 | m_Children: [] 215 | m_Father: {fileID: 224145737800144754} 216 | m_RootOrder: 0 217 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 218 | m_AnchorMin: {x: 0, y: 0} 219 | m_AnchorMax: {x: 1, y: 1} 220 | m_AnchoredPosition: {x: 0, y: 0} 221 | m_SizeDelta: {x: 0, y: 0} 222 | m_Pivot: {x: 0.5, y: 0.5} 223 | -------------------------------------------------------------------------------- /SignalRCore/RedirectSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using BestHTTP; 4 | using BestHTTP.Connections; 5 | using BestHTTP.Examples.Helpers; 6 | using BestHTTP.SignalRCore; 7 | using BestHTTP.SignalRCore.Encoders; 8 | using System; 9 | using UnityEngine; 10 | using UnityEngine.UI; 11 | 12 | namespace BestHTTP.Examples 13 | { 14 | /// 15 | /// This sample demonstrates redirection capabilities. The server will redirect a few times the client before 16 | /// routing it to the final endpoint. 17 | /// 18 | public sealed class RedirectSample : BestHTTP.Examples.Helpers.SampleBase 19 | { 20 | #pragma warning disable 0649 21 | 22 | [SerializeField] 23 | private string _path = "/redirect_sample"; 24 | 25 | [SerializeField] 26 | private ScrollRect _scrollRect; 27 | 28 | [SerializeField] 29 | private RectTransform _contentRoot; 30 | 31 | [SerializeField] 32 | private TextListItem _listItemPrefab; 33 | 34 | [SerializeField] 35 | private int _maxListItemEntries = 100; 36 | 37 | [SerializeField] 38 | private Button _connectButton; 39 | 40 | [SerializeField] 41 | private Button _closeButton; 42 | 43 | #pragma warning restore 44 | 45 | // Instance of the HubConnection 46 | public HubConnection hub; 47 | 48 | protected override void Start() 49 | { 50 | base.Start(); 51 | 52 | SetButtons(true, false); 53 | } 54 | 55 | void OnDestroy() 56 | { 57 | if (hub != null) 58 | { 59 | hub.StartClose(); 60 | } 61 | } 62 | 63 | public void OnConnectButton() 64 | { 65 | // Server side of this example can be found here: 66 | // https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/ 67 | 68 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 69 | try 70 | { 71 | MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( 72 | MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, 73 | MessagePack.Unity.UnityResolver.Instance, 74 | //MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, 75 | //MessagePack.Resolvers.StandardResolver.Instance, 76 | MessagePack.Resolvers.ContractlessStandardResolver.Instance 77 | ); 78 | 79 | var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); 80 | MessagePack.MessagePackSerializer.DefaultOptions = options; 81 | } 82 | catch 83 | { } 84 | #endif 85 | 86 | IProtocol protocol = null; 87 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 88 | protocol = new MessagePackCSharpProtocol(); 89 | #elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK 90 | protocol = new MessagePackProtocol(); 91 | #else 92 | protocol = new JsonProtocol(new LitJsonEncoder()); 93 | #endif 94 | 95 | // Crete the HubConnection 96 | hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._path), protocol); 97 | hub.AuthenticationProvider = new RedirectLoggerAccessTokenAuthenticator(hub); 98 | 99 | // Subscribe to hub events 100 | hub.OnConnected += Hub_OnConnected; 101 | hub.OnError += Hub_OnError; 102 | hub.OnClosed += Hub_OnClosed; 103 | 104 | hub.OnRedirected += Hub_Redirected; 105 | 106 | hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport({0}) event: {1}", transport.TransportType, ev)); 107 | 108 | // And finally start to connect to the server 109 | hub.StartConnect(); 110 | 111 | AddText("StartConnect called"); 112 | SetButtons(false, false); 113 | } 114 | 115 | public void OnCloseButton() 116 | { 117 | if (hub != null) 118 | { 119 | AddText("Calling StartClose"); 120 | 121 | hub.StartClose(); 122 | 123 | SetButtons(false, false); 124 | } 125 | } 126 | 127 | private void Hub_Redirected(HubConnection hub, Uri oldUri, Uri newUri) 128 | { 129 | AddText(string.Format("Hub connection redirected to '{0}'!", hub.Uri)); 130 | } 131 | 132 | /// 133 | /// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. 134 | /// 135 | private void Hub_OnConnected(HubConnection hub) 136 | { 137 | AddText(string.Format("Hub Connected with {0} transport using the {1} encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); 138 | 139 | // Call a parameterless function. We expect a string return value. 140 | hub.Invoke("Echo", "Message from the client") 141 | .OnSuccess(ret => AddText(string.Format(" 'Echo' returned: '{0}'", ret))); 142 | 143 | SetButtons(false, true); 144 | } 145 | 146 | /// 147 | /// This is called when the hub is closed after a StartClose() call. 148 | /// 149 | private void Hub_OnClosed(HubConnection hub) 150 | { 151 | AddText("Hub Closed"); 152 | SetButtons(true, false); 153 | } 154 | 155 | /// 156 | /// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. 157 | /// 158 | private void Hub_OnError(HubConnection hub, string error) 159 | { 160 | AddText(string.Format("Hub Error: {0}", error)); 161 | SetButtons(true, false); 162 | } 163 | 164 | private void SetButtons(bool connect, bool close) 165 | { 166 | if (this._connectButton != null) 167 | this._connectButton.interactable = connect; 168 | 169 | if (this._closeButton != null) 170 | this._closeButton.interactable = close; 171 | } 172 | 173 | private void AddText(string text) 174 | { 175 | GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 176 | } 177 | } 178 | 179 | public sealed class RedirectLoggerAccessTokenAuthenticator : IAuthenticationProvider 180 | { 181 | /// 182 | /// No pre-auth step required for this type of authentication 183 | /// 184 | public bool IsPreAuthRequired { get { return false; } } 185 | 186 | #pragma warning disable 0067 187 | /// 188 | /// Not used event as IsPreAuthRequired is false 189 | /// 190 | public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; 191 | 192 | /// 193 | /// Not used event as IsPreAuthRequired is false 194 | /// 195 | public event OnAuthenticationFailedDelegate OnAuthenticationFailed; 196 | 197 | #pragma warning restore 0067 198 | 199 | private HubConnection _connection; 200 | 201 | public RedirectLoggerAccessTokenAuthenticator(HubConnection connection) 202 | { 203 | this._connection = connection; 204 | } 205 | 206 | /// 207 | /// Not used as IsPreAuthRequired is false 208 | /// 209 | public void StartAuthentication() 210 | { } 211 | 212 | /// 213 | /// Prepares the request by adding two headers to it 214 | /// 215 | public void PrepareRequest(BestHTTP.HTTPRequest request) 216 | { 217 | request.SetHeader("x-redirect-count", _connection.RedirectCount.ToString()); 218 | 219 | if (HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri) == SupportedProtocols.HTTP) 220 | request.Uri = PrepareUri(request.Uri); 221 | } 222 | 223 | public Uri PrepareUri(Uri uri) 224 | { 225 | if (this._connection.NegotiationResult != null && !string.IsNullOrEmpty(this._connection.NegotiationResult.AccessToken)) 226 | { 227 | string query = string.IsNullOrEmpty(uri.Query) ? "?" : uri.Query + "&"; 228 | UriBuilder uriBuilder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, query + "access_token=" + this._connection.NegotiationResult.AccessToken); 229 | return uriBuilder.Uri; 230 | } 231 | else 232 | return uri; 233 | } 234 | 235 | public void Cancel() 236 | { } 237 | } 238 | } 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /HTTP/StreamingSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | using BestHTTP; 7 | 8 | namespace BestHTTP.Examples.HTTP 9 | { 10 | public class StreamingSample : BestHTTP.Examples.Helpers.SampleBase 11 | { 12 | #pragma warning disable 0649 13 | 14 | [Tooltip("The url of the resource to download")] 15 | [SerializeField] 16 | protected string _downloadPath = "/test100mb.dat"; 17 | 18 | [Header("Streaming Setup")] 19 | 20 | [SerializeField] 21 | protected RectTransform _streamingSetupRoot; 22 | 23 | [SerializeField] 24 | protected Slider _fragmentSizeSlider; 25 | 26 | [SerializeField] 27 | protected Text _fragmentSizeText; 28 | 29 | [SerializeField] 30 | protected Toggle _disableCacheToggle; 31 | 32 | [Header("Reporting")] 33 | 34 | [SerializeField] 35 | protected RectTransform _reportingRoot; 36 | 37 | [SerializeField] 38 | protected Slider _downloadProgressSlider; 39 | 40 | [SerializeField] 41 | protected Text _downloadProgressText; 42 | 43 | [SerializeField] 44 | protected Slider _processedDataSlider; 45 | 46 | [SerializeField] 47 | protected Text _processedDataText; 48 | 49 | [SerializeField] 50 | protected Text _statusText; 51 | 52 | [SerializeField] 53 | protected Button _startDownload; 54 | 55 | [SerializeField] 56 | protected Button _cancelDownload; 57 | 58 | #pragma warning restore 59 | 60 | /// 61 | /// Cached request to be able to abort it 62 | /// 63 | protected HTTPRequest request; 64 | 65 | /// 66 | /// Download(processing) progress. Its range is between [0..1] 67 | /// 68 | protected float progress; 69 | 70 | /// 71 | /// The fragment size that we will set to the request 72 | /// 73 | protected int fragmentSize = HTTPResponse.MinReadBufferSize; 74 | 75 | protected virtual long DownloadLength { get; set; } 76 | 77 | protected virtual long ProcessedBytes { get; set; } 78 | 79 | protected override void Start() 80 | { 81 | base.Start(); 82 | 83 | this._streamingSetupRoot.gameObject.SetActive(true); 84 | this._reportingRoot.gameObject.SetActive(false); 85 | 86 | this._startDownload.interactable = true; 87 | this._cancelDownload.interactable = false; 88 | 89 | this._fragmentSizeSlider.value = (1024 * 1024 - HTTPResponse.MinReadBufferSize) / 1024; 90 | this._fragmentSizeText.text = GUIHelper.GetBytesStr(1024 * 1024, 1); 91 | } 92 | 93 | protected void OnDestroy() 94 | { 95 | // Stop the download if we are leaving this example 96 | if (request != null && request.State < HTTPRequestStates.Finished) 97 | { 98 | request.OnDownloadProgress = null; 99 | request.Callback = null; 100 | request.Abort(); 101 | } 102 | } 103 | 104 | public void OnFragmentSizeSliderChanged(float value) 105 | { 106 | this.fragmentSize = HTTPResponse.MinReadBufferSize + (int)value * 1024; 107 | this._fragmentSizeText.text = GUIHelper.GetBytesStr(this.fragmentSize, 1); 108 | } 109 | 110 | public void Cancel() 111 | { 112 | if (this.request != null) 113 | this.request.Abort(); 114 | } 115 | 116 | protected virtual void SetupRequest() 117 | { 118 | request = new HTTPRequest(new Uri(base.sampleSelector.BaseURL + this._downloadPath), OnRequestFinished); 119 | 120 | #if !BESTHTTP_DISABLE_CACHING 121 | // If we are writing our own file set it to true(disable), so don't duplicate it on the file-system 122 | request.DisableCache = this._disableCacheToggle.isOn; 123 | #endif 124 | 125 | request.StreamFragmentSize = fragmentSize; 126 | 127 | request.Tag = DateTime.Now; 128 | 129 | request.OnHeadersReceived += OnHeadersReceived; 130 | request.OnDownloadProgress += OnDownloadProgress; 131 | request.OnStreamingData += OnDataDownloaded; 132 | } 133 | 134 | public virtual void StartStreaming() 135 | { 136 | SetupRequest(); 137 | 138 | // Start Processing the request 139 | request.Send(); 140 | 141 | this._statusText.text = "Download started!"; 142 | 143 | // UI 144 | this._streamingSetupRoot.gameObject.SetActive(false); 145 | this._reportingRoot.gameObject.SetActive(true); 146 | 147 | this._startDownload.interactable = false; 148 | this._cancelDownload.interactable = true; 149 | 150 | ResetProcessedValues(); 151 | } 152 | 153 | private void OnHeadersReceived(HTTPRequest req, HTTPResponse resp, Dictionary> newHeaders) 154 | { 155 | var range = resp.GetRange(); 156 | if (range != null) 157 | this.DownloadLength = range.ContentLength; 158 | else 159 | { 160 | var contentLength = resp.GetFirstHeaderValue("content-length"); 161 | if (contentLength != null) 162 | { 163 | long length = 0; 164 | if (long.TryParse(contentLength, out length)) 165 | this.DownloadLength = length; 166 | } 167 | } 168 | } 169 | 170 | protected virtual void OnRequestFinished(HTTPRequest req, HTTPResponse resp) 171 | { 172 | switch (req.State) 173 | { 174 | // The request finished without any problem. 175 | case HTTPRequestStates.Finished: 176 | if (resp.IsSuccess) 177 | { 178 | DateTime downloadStarted = (DateTime)req.Tag; 179 | TimeSpan diff = DateTime.Now - downloadStarted; 180 | 181 | this._statusText.text = string.Format("Streaming finished in {0:N0}ms", diff.TotalMilliseconds); 182 | } 183 | else 184 | { 185 | this._statusText.text = string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", 186 | resp.StatusCode, 187 | resp.Message, 188 | resp.DataAsText); 189 | Debug.LogWarning(this._statusText.text); 190 | 191 | request = null; 192 | } 193 | break; 194 | 195 | // The request finished with an unexpected error. The request's Exception property may contain more info about the error. 196 | case HTTPRequestStates.Error: 197 | this._statusText.text = "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"); 198 | Debug.LogError(this._statusText.text); 199 | 200 | request = null; 201 | break; 202 | 203 | // The request aborted, initiated by the user. 204 | case HTTPRequestStates.Aborted: 205 | this._statusText.text = "Request Aborted!"; 206 | Debug.LogWarning(this._statusText.text); 207 | 208 | request = null; 209 | break; 210 | 211 | // Connecting to the server is timed out. 212 | case HTTPRequestStates.ConnectionTimedOut: 213 | this._statusText.text = "Connection Timed Out!"; 214 | Debug.LogError(this._statusText.text); 215 | 216 | request = null; 217 | break; 218 | 219 | // The request didn't finished in the given time. 220 | case HTTPRequestStates.TimedOut: 221 | this._statusText.text = "Processing the request Timed Out!"; 222 | Debug.LogError(this._statusText.text); 223 | 224 | request = null; 225 | break; 226 | } 227 | 228 | // UI 229 | 230 | this._streamingSetupRoot.gameObject.SetActive(true); 231 | this._reportingRoot.gameObject.SetActive(false); 232 | 233 | this._startDownload.interactable = true; 234 | this._cancelDownload.interactable = false; 235 | request = null; 236 | } 237 | 238 | protected virtual void OnDownloadProgress(HTTPRequest originalRequest, long downloaded, long downloadLength) 239 | { 240 | double downloadPercent = (downloaded / (double)downloadLength) * 100; 241 | this._downloadProgressSlider.value = (float)downloadPercent; 242 | this._downloadProgressText.text = string.Format("{0:F1}%", downloadPercent); 243 | } 244 | 245 | protected virtual bool OnDataDownloaded(HTTPRequest request, HTTPResponse response, byte[] dataFragment, int dataFragmentLength) 246 | { 247 | this.ProcessedBytes += dataFragmentLength; 248 | SetDataProcessedUI(this.ProcessedBytes, this.DownloadLength); 249 | 250 | // Use downloaded data 251 | 252 | // Return true if dataFrament is processed so the plugin can recycle the byte[] 253 | return true; 254 | } 255 | 256 | protected void SetDataProcessedUI(long processed, long length) 257 | { 258 | float processedPercent = (processed / (float)length) * 100f; 259 | 260 | this._processedDataSlider.value = processedPercent; 261 | this._processedDataText.text = GUIHelper.GetBytesStr(processed, 0); 262 | } 263 | 264 | protected virtual void ResetProcessedValues() 265 | { 266 | this.ProcessedBytes = 0; 267 | this.DownloadLength = 0; 268 | 269 | SetDataProcessedUI(this.ProcessedBytes, this.DownloadLength); 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /SocketIO3/ChatSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using BestHTTP.Examples.Helpers; 5 | using BestHTTP.SocketIO3; 6 | using BestHTTP.SocketIO3.Events; 7 | 8 | using UnityEngine; 9 | using UnityEngine.UI; 10 | 11 | namespace BestHTTP.Examples.SocketIO3 12 | { 13 | #pragma warning disable 0649 14 | [PlatformSupport.IL2CPP.Preserve] 15 | class LoginData 16 | { 17 | [PlatformSupport.IL2CPP.Preserve] public int numUsers; 18 | } 19 | 20 | [PlatformSupport.IL2CPP.Preserve] 21 | sealed class NewMessageData 22 | { 23 | [PlatformSupport.IL2CPP.Preserve] public string username; 24 | [PlatformSupport.IL2CPP.Preserve] public string message; 25 | } 26 | 27 | [PlatformSupport.IL2CPP.Preserve] 28 | sealed class UserJoinedData : LoginData 29 | { 30 | [PlatformSupport.IL2CPP.Preserve] public string username; 31 | } 32 | 33 | [PlatformSupport.IL2CPP.Preserve] 34 | sealed class TypingData 35 | { 36 | [PlatformSupport.IL2CPP.Preserve] public string username; 37 | } 38 | 39 | #pragma warning restore 40 | 41 | public sealed class ChatSample : BestHTTP.Examples.Helpers.SampleBase 42 | { 43 | private readonly TimeSpan TYPING_TIMER_LENGTH = TimeSpan.FromMilliseconds(700); 44 | 45 | #pragma warning disable 0649, 0414 46 | 47 | [SerializeField] 48 | [Tooltip("The Socket.IO service address to connect to")] 49 | private string address = "https://socket-io-3-chat-5ae3v.ondigitalocean.app"; 50 | 51 | [Header("Login Details")] 52 | [SerializeField] 53 | private RectTransform _loginRoot; 54 | 55 | [SerializeField] 56 | private InputField _userNameInput; 57 | 58 | [Header("Chat Setup")] 59 | 60 | [SerializeField] 61 | private RectTransform _chatRoot; 62 | 63 | [SerializeField] 64 | private Text _participantsText; 65 | 66 | [SerializeField] 67 | private ScrollRect _scrollRect; 68 | 69 | [SerializeField] 70 | private RectTransform _contentRoot; 71 | 72 | [SerializeField] 73 | private TextListItem _listItemPrefab; 74 | 75 | [SerializeField] 76 | private int _maxListItemEntries = 100; 77 | 78 | [SerializeField] 79 | private Text _typingUsersText; 80 | 81 | [SerializeField] 82 | private InputField _input; 83 | 84 | [Header("Buttons")] 85 | 86 | [SerializeField] 87 | private Button _connectButton; 88 | 89 | [SerializeField] 90 | private Button _closeButton; 91 | 92 | #pragma warning restore 93 | 94 | /// 95 | /// The Socket.IO manager instance. 96 | /// 97 | private SocketManager Manager; 98 | 99 | /// 100 | /// True if the user is currently typing 101 | /// 102 | private bool typing; 103 | 104 | /// 105 | /// When the message changed. 106 | /// 107 | private DateTime lastTypingTime = DateTime.MinValue; 108 | 109 | /// 110 | /// Users that typing. 111 | /// 112 | private List typingUsers = new List(); 113 | 114 | 115 | #region Unity Events 116 | 117 | protected override void Start() 118 | { 119 | base.Start(); 120 | 121 | this._userNameInput.text = PlayerPrefs.GetString("SocketIO3ChatSample_UserName"); 122 | SetButtons(!string.IsNullOrEmpty(this._userNameInput.text), false); 123 | SetPanels(true); 124 | } 125 | 126 | void OnDestroy() 127 | { 128 | if (this.Manager != null) 129 | { 130 | // Leaving this sample, close the socket 131 | this.Manager.Close(); 132 | this.Manager = null; 133 | } 134 | } 135 | 136 | public void OnUserNameInputChanged(string userName) 137 | { 138 | SetButtons(!string.IsNullOrEmpty(userName), false); 139 | } 140 | 141 | public void OnUserNameInputSubmit(string userName) 142 | { 143 | if (Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Return)) 144 | OnConnectButton(); 145 | } 146 | 147 | public void UpdateTyping() 148 | { 149 | if (!typing) 150 | { 151 | typing = true; 152 | Manager.Socket.Emit("typing"); 153 | } 154 | 155 | lastTypingTime = DateTime.UtcNow; 156 | } 157 | 158 | public void OnMessageInput(string textToSend) 159 | { 160 | if ((!Input.GetKeyDown(KeyCode.KeypadEnter) && !Input.GetKeyDown(KeyCode.Return)) || string.IsNullOrEmpty(textToSend)) 161 | return; 162 | 163 | Manager.Socket.Emit("new message", textToSend); 164 | 165 | AddText(string.Format("{0}: {1}", this._userNameInput.text, textToSend)); 166 | } 167 | 168 | public void OnConnectButton() 169 | { 170 | SetPanels(false); 171 | 172 | PlayerPrefs.SetString("SocketIO3ChatSample_UserName", this._userNameInput.text); 173 | 174 | AddText("Connecting..."); 175 | 176 | // Create the Socket.IO manager 177 | Manager = new SocketManager(new Uri(this.address)); 178 | 179 | Manager.Socket.On(SocketIOEventTypes.Connect, OnConnected); 180 | Manager.Socket.On(SocketIOEventTypes.Disconnect, OnDisconnected); 181 | 182 | Manager.Socket.On("login", OnLogin); 183 | Manager.Socket.On("new message", OnNewMessage); 184 | Manager.Socket.On("user joined", OnUserJoined); 185 | Manager.Socket.On("user left", OnUserLeft); 186 | Manager.Socket.On("typing", OnTyping); 187 | Manager.Socket.On("stop typing", OnStopTyping); 188 | 189 | SetButtons(false, true); 190 | } 191 | 192 | public void OnCloseButton() 193 | { 194 | SetButtons(false, false); 195 | this.Manager.Close(); 196 | } 197 | 198 | void Update() 199 | { 200 | if (typing) 201 | { 202 | var typingTimer = DateTime.UtcNow; 203 | var timeDiff = typingTimer - lastTypingTime; 204 | if (timeDiff >= TYPING_TIMER_LENGTH) 205 | { 206 | Manager.Socket.Emit("stop typing"); 207 | typing = false; 208 | } 209 | } 210 | } 211 | 212 | #endregion 213 | 214 | #region SocketIO Events 215 | 216 | private void OnConnected(ConnectResponse resp) 217 | { 218 | AddText("Connected! Socket.IO SID: " + resp.sid); 219 | 220 | Manager.Socket.Emit("add user", this._userNameInput.text); 221 | this._input.interactable = true; 222 | } 223 | 224 | private void OnDisconnected() 225 | { 226 | AddText("Disconnected!"); 227 | 228 | SetPanels(true); 229 | SetButtons(true, false); 230 | } 231 | 232 | private void OnLogin(LoginData data) 233 | { 234 | AddText("Welcome to Socket.IO Chat"); 235 | 236 | if (data.numUsers == 1) 237 | this._participantsText.text = "there's 1 participant"; 238 | else 239 | this._participantsText.text = "there are " + data.numUsers + " participants"; 240 | } 241 | 242 | private void OnNewMessage(NewMessageData data) 243 | { 244 | AddText(string.Format("{0}: {1}", data.username, data.message)); 245 | } 246 | 247 | private void OnUserJoined(UserJoinedData data) 248 | { 249 | AddText(string.Format("{0} joined", data.username)); 250 | 251 | if (data.numUsers == 1) 252 | this._participantsText.text = "there's 1 participant"; 253 | else 254 | this._participantsText.text = "there are " + data.numUsers + " participants"; 255 | } 256 | 257 | private void OnUserLeft(UserJoinedData data) 258 | { 259 | AddText(string.Format("{0} left", data.username)); 260 | 261 | if (data.numUsers == 1) 262 | this._participantsText.text = "there's 1 participant"; 263 | else 264 | this._participantsText.text = "there are " + data.numUsers + " participants"; 265 | } 266 | 267 | private void OnTyping(TypingData data) 268 | { 269 | int idx = typingUsers.FindIndex((name) => name.Equals(data.username)); 270 | if (idx == -1) 271 | typingUsers.Add(data.username); 272 | 273 | SetTypingUsers(); 274 | } 275 | 276 | private void OnStopTyping(TypingData data) 277 | { 278 | int idx = typingUsers.FindIndex((name) => name.Equals(data.username)); 279 | if (idx != -1) 280 | typingUsers.RemoveAt(idx); 281 | 282 | SetTypingUsers(); 283 | } 284 | 285 | #endregion 286 | 287 | private void AddText(string text) 288 | { 289 | GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 290 | } 291 | 292 | private void SetTypingUsers() 293 | { 294 | if (this.typingUsers.Count > 0) 295 | { 296 | System.Text.StringBuilder sb = new System.Text.StringBuilder(this.typingUsers[0], this.typingUsers.Count + 1); 297 | 298 | for (int i = 1; i < this.typingUsers.Count; ++i) 299 | sb.AppendFormat(", {0}", this.typingUsers[i]); 300 | 301 | if (this.typingUsers.Count == 1) 302 | sb.Append(" is typing!"); 303 | else 304 | sb.Append(" are typing!"); 305 | 306 | this._typingUsersText.text = sb.ToString(); 307 | } 308 | else 309 | this._typingUsersText.text = string.Empty; 310 | } 311 | 312 | private void SetPanels(bool login) 313 | { 314 | if (login) 315 | { 316 | this._loginRoot.gameObject.SetActive(true); 317 | this._chatRoot.gameObject.SetActive(false); 318 | this._input.interactable = false; 319 | } 320 | else 321 | { 322 | this._loginRoot.gameObject.SetActive(false); 323 | this._chatRoot.gameObject.SetActive(true); 324 | this._input.interactable = true; 325 | } 326 | } 327 | 328 | private void SetButtons(bool connect, bool close) 329 | { 330 | if (this._connectButton != null) 331 | this._connectButton.interactable = connect; 332 | 333 | if (this._closeButton != null) 334 | this._closeButton.interactable = close; 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /SignalRCore/AsyncTestHubSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using System; 4 | using UnityEngine; 5 | using BestHTTP.SignalRCore; 6 | using BestHTTP.SignalRCore.Encoders; 7 | using UnityEngine.UI; 8 | using BestHTTP.Examples.Helpers; 9 | #if CSHARP_7_OR_LATER 10 | using System.Threading.Tasks; 11 | #endif 12 | 13 | namespace BestHTTP.Examples 14 | { 15 | // Server side of this example can be found here: 16 | // https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/TestHub.cs 17 | public class AsyncTestHubSample : BestHTTP.Examples.Helpers.SampleBase 18 | { 19 | #pragma warning disable 0649 20 | #pragma warning disable 0414 21 | [SerializeField] 22 | private string _path = "/TestHub"; 23 | 24 | [SerializeField] 25 | private ScrollRect _scrollRect; 26 | 27 | [SerializeField] 28 | private RectTransform _contentRoot; 29 | 30 | [SerializeField] 31 | private TextListItem _listItemPrefab; 32 | 33 | [SerializeField] 34 | private int _maxListItemEntries = 100; 35 | 36 | [SerializeField] 37 | private Button _connectButton; 38 | 39 | [SerializeField] 40 | private Button _closeButton; 41 | 42 | #pragma warning restore 43 | 44 | // Instance of the HubConnection 45 | HubConnection hub; 46 | 47 | protected override void Start() 48 | { 49 | base.Start(); 50 | 51 | #if !CSHARP_7_OR_LATER 52 | AddText("This sample can work only when at least c# 7.3 is supported!"); 53 | SetButtons(false, false); 54 | #else 55 | SetButtons(true, false); 56 | #endif 57 | } 58 | 59 | #if CSHARP_7_OR_LATER 60 | async void OnDestroy() 61 | { 62 | await hub?.CloseAsync(); 63 | } 64 | #endif 65 | 66 | /// 67 | /// GUI button callback 68 | /// 69 | public 70 | #if CSHARP_7_OR_LATER 71 | async 72 | #endif 73 | void OnConnectButton() 74 | { 75 | #if CSHARP_7_OR_LATER 76 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 77 | try 78 | { 79 | MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( 80 | MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, 81 | MessagePack.Unity.UnityResolver.Instance, 82 | //MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, 83 | //MessagePack.Resolvers.StandardResolver.Instance, 84 | MessagePack.Resolvers.ContractlessStandardResolver.Instance 85 | ); 86 | 87 | var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); 88 | MessagePack.MessagePackSerializer.DefaultOptions = options; 89 | } 90 | catch 91 | { } 92 | #endif 93 | 94 | IProtocol protocol = null; 95 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 96 | protocol = new MessagePackCSharpProtocol(); 97 | #elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK 98 | protocol = new MessagePackProtocol(); 99 | #else 100 | protocol = new JsonProtocol(new LitJsonEncoder()); 101 | #endif 102 | // Crete the HubConnection 103 | hub = new HubConnection(new Uri(this.sampleSelector.BaseURL + this._path), protocol); 104 | 105 | // Subscribe to hub events 106 | hub.OnError += Hub_OnError; 107 | 108 | hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport({0}) event: {1}", transport.TransportType, ev)); 109 | 110 | // Set up server callable functions 111 | hub.On("Send", (string arg) => AddText(string.Format("On 'Send': '{0}'", arg)).AddLeftPadding(20)); 112 | hub.On("Person", (person) => AddText(string.Format("On 'Person': '{0}'", person)).AddLeftPadding(20)); 113 | hub.On("TwoPersons", (person1, person2) => AddText(string.Format("On 'TwoPersons': '{0}', '{1}'", person1, person2)).AddLeftPadding(20)); 114 | 115 | AddText("StartConnect called"); 116 | 117 | SetButtons(false, false); 118 | 119 | // And finally start to connect to the server 120 | await hub.ConnectAsync(); 121 | 122 | SetButtons(false, true); 123 | AddText(string.Format("Hub Connected with {0} transport using the {1} encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); 124 | 125 | // Call a server function with a string param. We expect no return value. 126 | await hub.SendAsync("Send", "my message"); 127 | 128 | // Call a parameterless function. We expect a string return value. 129 | try 130 | { 131 | string result = await hub.InvokeAsync("NoParam"); 132 | 133 | AddText(string.Format("'NoParam' returned: '{0}'", result)) 134 | .AddLeftPadding(20); 135 | } 136 | catch (Exception ex) 137 | { 138 | AddText(string.Format("'NoParam' error: '{0}'", ex.Message)).AddLeftPadding(20); 139 | } 140 | 141 | // Call a function on the server to add two numbers. OnSuccess will be called with the result and OnError if there's an error. 142 | var addResult = await hub.InvokeAsync("Add", 10, 20); 143 | AddText(string.Format("'Add(10, 20)' returned: '{0}'", addResult)).AddLeftPadding(20); 144 | 145 | var nullabelTestResult = await hub.InvokeAsync("NullableTest", 10); 146 | AddText(string.Format("'NullableTest(10)' returned: '{0}'", nullabelTestResult)).AddLeftPadding(20); 147 | 148 | // Call a function that will return a Person object constructed from the function's parameters. 149 | var getPersonResult = await hub.InvokeAsync("GetPerson", "Mr. Smith", 26); 150 | AddText(string.Format("'GetPerson(\"Mr. Smith\", 26)' returned: '{0}'", getPersonResult)).AddLeftPadding(20); 151 | 152 | // To test errors/exceptions this call always throws an exception on the server side resulting in an OnError call. 153 | // OnError expected here! 154 | 155 | try 156 | { 157 | var singleResultFailureResult = await hub.InvokeAsync("SingleResultFailure", 10, 20); 158 | AddText(string.Format("'SingleResultFailure(10, 20)' returned: '{0}'", singleResultFailureResult)).AddLeftPadding(20); 159 | } 160 | catch (Exception ex) 161 | { 162 | AddText(string.Format("'SingleResultFailure(10, 20)' error: '{0}'", ex.Message)).AddLeftPadding(20); 163 | } 164 | 165 | // This call demonstrates IEnumerable<> functions, result will be the yielded numbers. 166 | var batchedResult = await hub.InvokeAsync("Batched", 10); 167 | AddText(string.Format("'Batched(10)' returned items: '{0}'", batchedResult.Length)).AddLeftPadding(20); 168 | 169 | // OnItem is called for a streaming request for every items returned by the server. OnSuccess will still be called with all the items. 170 | hub.GetDownStreamController("ObservableCounter", 10, 1000) 171 | .OnItem(result => AddText(string.Format("'ObservableCounter(10, 1000)' OnItem: '{0}'", result)).AddLeftPadding(20)) 172 | .OnSuccess(result => AddText("'ObservableCounter(10, 1000)' OnSuccess.").AddLeftPadding(20)) 173 | .OnError(error => AddText(string.Format("'ObservableCounter(10, 1000)' error: '{0}'", error)).AddLeftPadding(20)); 174 | 175 | // A stream request can be cancelled any time. 176 | var controller = hub.GetDownStreamController("ChannelCounter", 10, 1000); 177 | 178 | controller.OnItem(result => AddText(string.Format("'ChannelCounter(10, 1000)' OnItem: '{0}'", result)).AddLeftPadding(20)) 179 | .OnSuccess(result => AddText("'ChannelCounter(10, 1000)' OnSuccess.").AddLeftPadding(20)) 180 | .OnError(error => AddText(string.Format("'ChannelCounter(10, 1000)' error: '{0}'", error)).AddLeftPadding(20)); 181 | 182 | // a stream can be cancelled by calling the controller's Cancel method 183 | controller.Cancel(); 184 | 185 | // This call will stream strongly typed objects 186 | hub.GetDownStreamController("GetRandomPersons", 20, 2000) 187 | .OnItem(result => AddText(string.Format("'GetRandomPersons(20, 1000)' OnItem: '{0}'", result)).AddLeftPadding(20)) 188 | .OnSuccess(result => AddText("'GetRandomPersons(20, 1000)' OnSuccess.").AddLeftPadding(20)); 189 | #endif 190 | } 191 | 192 | /// 193 | /// GUI button callback 194 | /// 195 | public 196 | #if CSHARP_7_OR_LATER 197 | async 198 | #endif 199 | void OnCloseButton() 200 | { 201 | #if CSHARP_7_OR_LATER 202 | if (this.hub != null) 203 | { 204 | AddText("Calling CloseAsync"); 205 | SetButtons(false, false); 206 | 207 | await this.hub.CloseAsync(); 208 | 209 | SetButtons(true, false); 210 | AddText("Hub Closed"); 211 | } 212 | #endif 213 | } 214 | 215 | /// 216 | /// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. 217 | /// 218 | private void Hub_OnError(HubConnection hub, string error) 219 | { 220 | SetButtons(true, false); 221 | AddText(string.Format("Hub Error: {0}", error)); 222 | } 223 | 224 | private void SetButtons(bool connect, bool close) 225 | { 226 | if (this._connectButton != null) 227 | this._connectButton.interactable = connect; 228 | 229 | if (this._closeButton != null) 230 | this._closeButton.interactable = close; 231 | } 232 | 233 | private TextListItem AddText(string text) 234 | { 235 | return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 236 | } 237 | } 238 | } 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /SocketIO/SocketIOChatSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SOCKETIO 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | using UnityEngine; 7 | using BestHTTP.SocketIO; 8 | using UnityEngine.UI; 9 | using BestHTTP.Examples.Helpers; 10 | 11 | namespace BestHTTP.Examples 12 | { 13 | public sealed class SocketIOChatSample : BestHTTP.Examples.Helpers.SampleBase 14 | { 15 | private readonly TimeSpan TYPING_TIMER_LENGTH = TimeSpan.FromMilliseconds(700); 16 | 17 | #pragma warning disable 0649, 0414 18 | 19 | [SerializeField] 20 | [Tooltip("The WebSocket address to connect")] 21 | private string address = "https://socketio-chat-h9jt.herokuapp.com/socket.io/"; 22 | 23 | [Header("Login Details")] 24 | [SerializeField] 25 | private RectTransform _loginRoot; 26 | 27 | [SerializeField] 28 | private InputField _userNameInput; 29 | 30 | [Header("Chat Setup")] 31 | 32 | [SerializeField] 33 | private RectTransform _chatRoot; 34 | 35 | [SerializeField] 36 | private Text _participantsText; 37 | 38 | [SerializeField] 39 | private ScrollRect _scrollRect; 40 | 41 | [SerializeField] 42 | private RectTransform _contentRoot; 43 | 44 | [SerializeField] 45 | private TextListItem _listItemPrefab; 46 | 47 | [SerializeField] 48 | private int _maxListItemEntries = 100; 49 | 50 | [SerializeField] 51 | private Text _typingUsersText; 52 | 53 | [SerializeField] 54 | private InputField _input; 55 | 56 | [Header("Buttons")] 57 | 58 | [SerializeField] 59 | private Button _connectButton; 60 | 61 | [SerializeField] 62 | private Button _closeButton; 63 | 64 | #pragma warning restore 65 | 66 | /// 67 | /// The Socket.IO manager instance. 68 | /// 69 | private SocketManager Manager; 70 | 71 | /// 72 | /// True if the user is currently typing 73 | /// 74 | private bool typing; 75 | 76 | /// 77 | /// When the message changed. 78 | /// 79 | private DateTime lastTypingTime = DateTime.MinValue; 80 | 81 | /// 82 | /// Users that typing. 83 | /// 84 | private List typingUsers = new List(); 85 | 86 | #region Unity Events 87 | 88 | protected override void Start() 89 | { 90 | base.Start(); 91 | 92 | this._userNameInput.text = PlayerPrefs.GetString("SocketIOChatSample_UserName"); 93 | SetButtons(!string.IsNullOrEmpty(this._userNameInput.text), false); 94 | SetPanels(true); 95 | } 96 | 97 | void OnDestroy() 98 | { 99 | if (this.Manager != null) 100 | { 101 | // Leaving this sample, close the socket 102 | this.Manager.Close(); 103 | this.Manager = null; 104 | } 105 | } 106 | 107 | public void OnUserNameInputChanged(string userName) 108 | { 109 | SetButtons(!string.IsNullOrEmpty(userName), false); 110 | } 111 | 112 | public void OnUserNameInputSubmit(string userName) 113 | { 114 | if (Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Return)) 115 | OnConnectButton(); 116 | } 117 | 118 | public void OnConnectButton() 119 | { 120 | SetPanels(false); 121 | 122 | PlayerPrefs.SetString("SocketIOChatSample_UserName", this._userNameInput.text); 123 | 124 | AddText("Connecting..."); 125 | 126 | // Create the Socket.IO manager 127 | Manager = new SocketManager(new Uri(address)); 128 | 129 | Manager.Socket.On(SocketIOEventTypes.Connect, (s, p, a) => 130 | { 131 | AddText("Connected!"); 132 | 133 | Manager.Socket.Emit("add user", this._userNameInput.text); 134 | this._input.interactable = true; 135 | }); 136 | 137 | Manager.Socket.On(SocketIOEventTypes.Disconnect, (s, p, a) => 138 | { 139 | AddText("Disconnected!"); 140 | 141 | SetPanels(true); 142 | SetButtons(true, false); 143 | }); 144 | 145 | // Set up custom chat events 146 | Manager.Socket.On("login", OnLogin); 147 | Manager.Socket.On("new message", OnNewMessage); 148 | Manager.Socket.On("user joined", OnUserJoined); 149 | Manager.Socket.On("user left", OnUserLeft); 150 | Manager.Socket.On("typing", OnTyping); 151 | Manager.Socket.On("stop typing", OnStopTyping); 152 | 153 | // The argument will be an Error object. 154 | Manager.Socket.On(SocketIOEventTypes.Error, (socket, packet, args) => { 155 | Debug.LogError(string.Format("Error: {0}", args[0].ToString())); 156 | }); 157 | 158 | SetButtons(false, true); 159 | } 160 | 161 | public void OnCloseButton() 162 | { 163 | SetButtons(false, false); 164 | this.Manager.Close(); 165 | } 166 | 167 | void Update() 168 | { 169 | if (typing) 170 | { 171 | var typingTimer = DateTime.UtcNow; 172 | var timeDiff = typingTimer - lastTypingTime; 173 | if (timeDiff >= TYPING_TIMER_LENGTH) 174 | { 175 | Manager.Socket.Emit("stop typing"); 176 | typing = false; 177 | } 178 | } 179 | } 180 | 181 | #endregion 182 | 183 | #region Chat Logic 184 | 185 | public void OnMessageInput(string textToSend) 186 | { 187 | if ((!Input.GetKeyDown(KeyCode.KeypadEnter) && !Input.GetKeyDown(KeyCode.Return)) || string.IsNullOrEmpty(textToSend)) 188 | return; 189 | 190 | Manager.Socket.Emit("new message", textToSend); 191 | 192 | AddText(string.Format("{0}: {1}", this._userNameInput.text, textToSend)); 193 | } 194 | 195 | public void UpdateTyping(string textToSend) 196 | { 197 | if (!typing) 198 | { 199 | typing = true; 200 | Manager.Socket.Emit("typing"); 201 | } 202 | 203 | lastTypingTime = DateTime.UtcNow; 204 | } 205 | 206 | void addParticipantsMessage(Dictionary data) 207 | { 208 | int numUsers = Convert.ToInt32(data["numUsers"]); 209 | 210 | if (numUsers == 1) 211 | this._participantsText.text = "there's 1 participant"; 212 | else 213 | this._participantsText.text = "there are " + numUsers + " participants"; 214 | } 215 | 216 | void addChatMessage(Dictionary data) 217 | { 218 | var username = data["username"] as string; 219 | var msg = data["message"] as string; 220 | 221 | AddText(string.Format("{0}: {1}", username, msg)); 222 | } 223 | 224 | void AddChatTyping(Dictionary data) 225 | { 226 | var username = data["username"] as string; 227 | 228 | int idx = typingUsers.FindIndex((name) => name.Equals(username)); 229 | if (idx == -1) 230 | typingUsers.Add(username); 231 | 232 | SetTypingUsers(); 233 | } 234 | 235 | void RemoveChatTyping(Dictionary data) 236 | { 237 | var username = data["username"] as string; 238 | 239 | int idx = typingUsers.FindIndex((name) => name.Equals(username)); 240 | if (idx != -1) 241 | typingUsers.RemoveAt(idx); 242 | 243 | SetTypingUsers(); 244 | } 245 | 246 | #endregion 247 | 248 | #region Custom SocketIO Events 249 | 250 | void OnLogin(Socket socket, Packet packet, params object[] args) 251 | { 252 | AddText("Welcome to Socket.IO Chat"); 253 | 254 | addParticipantsMessage(args[0] as Dictionary); 255 | } 256 | 257 | void OnNewMessage(Socket socket, Packet packet, params object[] args) 258 | { 259 | addChatMessage(args[0] as Dictionary); 260 | } 261 | 262 | void OnUserJoined(Socket socket, Packet packet, params object[] args) 263 | { 264 | var data = args[0] as Dictionary; 265 | 266 | var username = data["username"] as string; 267 | 268 | AddText(string.Format("{0} joined", username)); 269 | 270 | addParticipantsMessage(data); 271 | } 272 | 273 | void OnUserLeft(Socket socket, Packet packet, params object[] args) 274 | { 275 | var data = args[0] as Dictionary; 276 | 277 | var username = data["username"] as string; 278 | 279 | AddText(string.Format("{0} left", username)); 280 | 281 | addParticipantsMessage(data); 282 | } 283 | 284 | void OnTyping(Socket socket, Packet packet, params object[] args) 285 | { 286 | AddChatTyping(args[0] as Dictionary); 287 | } 288 | 289 | void OnStopTyping(Socket socket, Packet packet, params object[] args) 290 | { 291 | RemoveChatTyping(args[0] as Dictionary); 292 | } 293 | 294 | #endregion 295 | 296 | private void AddText(string text) 297 | { 298 | GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 299 | } 300 | 301 | private void SetTypingUsers() 302 | { 303 | if (this.typingUsers.Count > 0) 304 | { 305 | System.Text.StringBuilder sb = new System.Text.StringBuilder(this.typingUsers[0], this.typingUsers.Count + 1); 306 | 307 | for (int i = 1; i < this.typingUsers.Count; ++i) 308 | sb.AppendFormat(", {0}", this.typingUsers[i]); 309 | 310 | if (this.typingUsers.Count == 1) 311 | sb.Append(" is typing!"); 312 | else 313 | sb.Append(" are typing!"); 314 | 315 | this._typingUsersText.text = sb.ToString(); 316 | } 317 | else 318 | this._typingUsersText.text = string.Empty; 319 | } 320 | 321 | private void SetPanels(bool login) 322 | { 323 | if (login) 324 | { 325 | this._loginRoot.gameObject.SetActive(true); 326 | this._chatRoot.gameObject.SetActive(false); 327 | this._input.interactable = false; 328 | } 329 | else 330 | { 331 | this._loginRoot.gameObject.SetActive(false); 332 | this._chatRoot.gameObject.SetActive(true); 333 | this._input.interactable = true; 334 | } 335 | } 336 | 337 | private void SetButtons(bool connect, bool close) 338 | { 339 | if (this._connectButton != null) 340 | this._connectButton.interactable = connect; 341 | 342 | if (this._closeButton != null) 343 | this._closeButton.interactable = close; 344 | } 345 | } 346 | } 347 | 348 | #endif 349 | -------------------------------------------------------------------------------- /SignalRCore/TestHubSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using System; 4 | using UnityEngine; 5 | using BestHTTP.SignalRCore; 6 | using BestHTTP.SignalRCore.Encoders; 7 | using UnityEngine.UI; 8 | using BestHTTP.Examples.Helpers; 9 | 10 | namespace BestHTTP.Examples 11 | { 12 | public enum MyEnum : int 13 | { 14 | None, 15 | One, 16 | Two 17 | } 18 | 19 | public sealed class Metadata 20 | { 21 | public string strData; 22 | public int intData; 23 | public MyEnum myEnum; 24 | } 25 | 26 | // Server side of this example can be found here: 27 | // https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/TestHub.cs 28 | public class TestHubSample : BestHTTP.Examples.Helpers.SampleBase 29 | { 30 | #pragma warning disable 0649 31 | [SerializeField] 32 | private string _path = "/TestHub"; 33 | 34 | [SerializeField] 35 | private ScrollRect _scrollRect; 36 | 37 | [SerializeField] 38 | private RectTransform _contentRoot; 39 | 40 | [SerializeField] 41 | private TextListItem _listItemPrefab; 42 | 43 | [SerializeField] 44 | private int _maxListItemEntries = 100; 45 | 46 | [SerializeField] 47 | private Button _connectButton; 48 | 49 | [SerializeField] 50 | private Button _closeButton; 51 | 52 | #pragma warning restore 53 | 54 | // Instance of the HubConnection 55 | HubConnection hub; 56 | 57 | protected override void Start() 58 | { 59 | base.Start(); 60 | 61 | SetButtons(true, false); 62 | } 63 | 64 | void OnDestroy() 65 | { 66 | if (hub != null) 67 | hub.StartClose(); 68 | } 69 | 70 | /// 71 | /// GUI button callback 72 | /// 73 | public void OnConnectButton() 74 | { 75 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 76 | try 77 | { 78 | MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( 79 | MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, 80 | MessagePack.Unity.UnityResolver.Instance, 81 | //MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, 82 | //MessagePack.Resolvers.StandardResolver.Instance, 83 | MessagePack.Resolvers.ContractlessStandardResolver.Instance 84 | ); 85 | 86 | var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); 87 | MessagePack.MessagePackSerializer.DefaultOptions = options; 88 | } 89 | catch 90 | { } 91 | #endif 92 | 93 | IProtocol protocol = null; 94 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 95 | protocol = new MessagePackCSharpProtocol(); 96 | #elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK 97 | protocol = new MessagePackProtocol(); 98 | #else 99 | protocol = new JsonProtocol(new LitJsonEncoder()); 100 | #endif 101 | // Crete the HubConnection 102 | hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._path), protocol); 103 | 104 | // Optionally add an authenticator 105 | //hub.AuthenticationProvider = new BestHTTP.SignalRCore.Authentication.HeaderAuthenticator(""); 106 | 107 | // Subscribe to hub events 108 | hub.OnConnected += Hub_OnConnected; 109 | hub.OnError += Hub_OnError; 110 | hub.OnClosed += Hub_OnClosed; 111 | 112 | hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport({0}) event: {1}", transport.TransportType, ev)); 113 | 114 | // Set up server callable functions 115 | hub.On("Send", (string arg) => AddText(string.Format("On 'Send': '{0}'", arg)).AddLeftPadding(20)); 116 | hub.On("Person", (person) => AddText(string.Format("On 'Person': '{0}'", person)).AddLeftPadding(20)); 117 | hub.On("TwoPersons", (person1, person2) => AddText(string.Format("On 'TwoPersons': '{0}', '{1}'", person1, person2)).AddLeftPadding(20)); 118 | 119 | // And finally start to connect to the server 120 | hub.StartConnect(); 121 | 122 | AddText("StartConnect called"); 123 | 124 | SetButtons(false, false); 125 | } 126 | 127 | /// 128 | /// GUI button callback 129 | /// 130 | public void OnCloseButton() 131 | { 132 | if (this.hub != null) 133 | { 134 | this.hub.StartClose(); 135 | 136 | AddText("StartClose called"); 137 | SetButtons(false, false); 138 | } 139 | } 140 | 141 | /// 142 | /// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. 143 | /// 144 | private void Hub_OnConnected(HubConnection hub) 145 | { 146 | SetButtons(false, true); 147 | AddText(string.Format("Hub Connected with {0} transport using the {1} encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); 148 | 149 | hub.Send("SendMetadata", new Metadata() { intData = 123, strData = "meta data", myEnum = MyEnum.One }); 150 | 151 | // Call a server function with a string param. We expect no return value. 152 | hub.Send("Send", "my message"); 153 | 154 | // Call a parameterless function. We expect a string return value. 155 | hub.Invoke("NoParam") 156 | .OnSuccess(ret => AddText(string.Format("'NoParam' returned: '{0}'", ret)).AddLeftPadding(20)) 157 | .OnError(error => AddText(string.Format("'NoParam' error: '{0}'", error)).AddLeftPadding(20)); 158 | 159 | // Call a function on the server to add two numbers. OnSuccess will be called with the result and OnError if there's an error. 160 | hub.Invoke("Add", 10, 20) 161 | .OnSuccess(result => AddText(string.Format("'Add(10, 20)' returned: '{0}'", result)).AddLeftPadding(20)) 162 | .OnError(error => AddText(string.Format("'Add(10, 20)' error: '{0}'", error)).AddLeftPadding(20)); 163 | 164 | hub.Invoke("NullableTest", 10) 165 | .OnSuccess(result => AddText(string.Format("'NullableTest(10)' returned: '{0}'", result)).AddLeftPadding(20)) 166 | .OnError(error => AddText(string.Format("'NullableTest(10)' error: '{0}'", error)).AddLeftPadding(20)); 167 | 168 | // Call a function that will return a Person object constructed from the function's parameters. 169 | hub.Invoke("GetPerson", "Mr. Smith", 26) 170 | .OnSuccess(result => AddText(string.Format("'GetPerson(\"Mr. Smith\", 26)' returned: '{0}'", result)).AddLeftPadding(20)) 171 | .OnError(error => AddText(string.Format("'GetPerson(\"Mr. Smith\", 26)' error: '{0}'", error)).AddLeftPadding(20)); 172 | 173 | // To test errors/exceptions this call always throws an exception on the server side resulting in an OnError call. 174 | // OnError expected here! 175 | hub.Invoke("SingleResultFailure", 10, 20) 176 | .OnSuccess(result => AddText(string.Format("'SingleResultFailure(10, 20)' returned: '{0}'", result)).AddLeftPadding(20)) 177 | .OnError(error => AddText(string.Format("'SingleResultFailure(10, 20)' error: '{0}'", error)).AddLeftPadding(20)); 178 | 179 | // This call demonstrates IEnumerable<> functions, result will be the yielded numbers. 180 | hub.Invoke("Batched", 10) 181 | .OnSuccess(result => AddText(string.Format("'Batched(10)' returned items: '{0}'", result.Length)).AddLeftPadding(20)) 182 | .OnError(error => AddText(string.Format("'Batched(10)' error: '{0}'", error)).AddLeftPadding(20)); 183 | 184 | // OnItem is called for a streaming request for every items returned by the server. OnSuccess will still be called with all the items. 185 | hub.GetDownStreamController("ObservableCounter", 10, 1000) 186 | .OnItem(result => AddText(string.Format("'ObservableCounter(10, 1000)' OnItem: '{0}'", result)).AddLeftPadding(20)) 187 | .OnSuccess(result => AddText("'ObservableCounter(10, 1000)' OnSuccess.").AddLeftPadding(20)) 188 | .OnError(error => AddText(string.Format("'ObservableCounter(10, 1000)' error: '{0}'", error)).AddLeftPadding(20)); 189 | 190 | // A stream request can be cancelled any time. 191 | var controller = hub.GetDownStreamController("ChannelCounter", 10, 1000); 192 | 193 | controller.OnItem(result => AddText(string.Format("'ChannelCounter(10, 1000)' OnItem: '{0}'", result)).AddLeftPadding(20)) 194 | .OnSuccess(result => AddText("'ChannelCounter(10, 1000)' OnSuccess.").AddLeftPadding(20)) 195 | .OnError(error => AddText(string.Format("'ChannelCounter(10, 1000)' error: '{0}'", error)).AddLeftPadding(20)); 196 | 197 | // a stream can be cancelled by calling the controller's Cancel method 198 | controller.Cancel(); 199 | 200 | // This call will stream strongly typed objects 201 | hub.GetDownStreamController("GetRandomPersons", 20, 2000) 202 | .OnItem(result => AddText(string.Format("'GetRandomPersons(20, 1000)' OnItem: '{0}'", result)).AddLeftPadding(20)) 203 | .OnSuccess(result => AddText("'GetRandomPersons(20, 1000)' OnSuccess.").AddLeftPadding(20)); 204 | } 205 | 206 | /// 207 | /// This is called when the hub is closed after a StartClose() call. 208 | /// 209 | private void Hub_OnClosed(HubConnection hub) 210 | { 211 | SetButtons(true, false); 212 | AddText("Hub Closed"); 213 | } 214 | 215 | /// 216 | /// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. 217 | /// 218 | private void Hub_OnError(HubConnection hub, string error) 219 | { 220 | SetButtons(true, false); 221 | AddText(string.Format("Hub Error: {0}", error)); 222 | } 223 | 224 | private void SetButtons(bool connect, bool close) 225 | { 226 | if (this._connectButton != null) 227 | this._connectButton.interactable = connect; 228 | 229 | if (this._closeButton != null) 230 | this._closeButton.interactable = close; 231 | } 232 | 233 | private TextListItem AddText(string text) 234 | { 235 | return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 236 | } 237 | } 238 | } 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /SignalRCore/HubWithPreAuthorizationSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using BestHTTP; 4 | using BestHTTP.Connections; 5 | using BestHTTP.Examples.Helpers; 6 | using BestHTTP.SignalRCore; 7 | using BestHTTP.SignalRCore.Encoders; 8 | using System; 9 | using UnityEngine; 10 | using UnityEngine.UI; 11 | 12 | namespace BestHTTP.Examples 13 | { 14 | public sealed class HubWithPreAuthorizationSample : BestHTTP.Examples.Helpers.SampleBase 15 | { 16 | #pragma warning disable 0649 17 | 18 | [SerializeField] 19 | private string _hubPath = "/HubWithAuthorization"; 20 | 21 | [SerializeField] 22 | private string _jwtTokenPath = "/generateJwtToken"; 23 | 24 | [SerializeField] 25 | private ScrollRect _scrollRect; 26 | 27 | [SerializeField] 28 | private RectTransform _contentRoot; 29 | 30 | [SerializeField] 31 | private TextListItem _listItemPrefab; 32 | 33 | [SerializeField] 34 | private int _maxListItemEntries = 100; 35 | 36 | [SerializeField] 37 | private Button _connectButton; 38 | 39 | [SerializeField] 40 | private Button _closeButton; 41 | 42 | #pragma warning restore 43 | 44 | // Instance of the HubConnection 45 | HubConnection hub; 46 | 47 | protected override void Start() 48 | { 49 | base.Start(); 50 | 51 | SetButtons(true, false); 52 | } 53 | 54 | void OnDestroy() 55 | { 56 | if (hub != null) 57 | hub.StartClose(); 58 | } 59 | 60 | public void OnConnectButton() 61 | { 62 | // Server side of this example can be found here: 63 | // https://github.com/Benedicht/BestHTTP_DemoSite/blob/master/BestHTTP_DemoSite/Hubs/ 64 | 65 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 66 | try 67 | { 68 | MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( 69 | MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, 70 | MessagePack.Unity.UnityResolver.Instance, 71 | //MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, 72 | //MessagePack.Resolvers.StandardResolver.Instance, 73 | MessagePack.Resolvers.ContractlessStandardResolver.Instance 74 | ); 75 | 76 | var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); 77 | MessagePack.MessagePackSerializer.DefaultOptions = options; 78 | } 79 | catch 80 | { } 81 | #endif 82 | 83 | IProtocol protocol = null; 84 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 85 | protocol = new MessagePackCSharpProtocol(); 86 | #elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK 87 | protocol = new MessagePackProtocol(); 88 | #else 89 | protocol = new JsonProtocol(new LitJsonEncoder()); 90 | #endif 91 | 92 | // Crete the HubConnection 93 | hub = new HubConnection(new Uri(base.sampleSelector.BaseURL + this._hubPath), protocol); 94 | 95 | hub.AuthenticationProvider = new PreAuthAccessTokenAuthenticator(new Uri(base.sampleSelector.BaseURL + this._jwtTokenPath)); 96 | 97 | hub.AuthenticationProvider.OnAuthenticationSucceded += AuthenticationProvider_OnAuthenticationSucceded; 98 | hub.AuthenticationProvider.OnAuthenticationFailed += AuthenticationProvider_OnAuthenticationFailed; 99 | 100 | // Subscribe to hub events 101 | hub.OnConnected += Hub_OnConnected; 102 | hub.OnError += Hub_OnError; 103 | hub.OnClosed += Hub_OnClosed; 104 | 105 | hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport({0}) event: {1}", transport.TransportType, ev)); 106 | 107 | // And finally start to connect to the server 108 | hub.StartConnect(); 109 | 110 | AddText("StartConnect called"); 111 | SetButtons(false, false); 112 | } 113 | 114 | public void OnCloseButton() 115 | { 116 | if (hub != null) 117 | { 118 | hub.StartClose(); 119 | 120 | AddText("StartClose called"); 121 | SetButtons(false, false); 122 | } 123 | } 124 | 125 | private void AuthenticationProvider_OnAuthenticationSucceded(IAuthenticationProvider provider) 126 | { 127 | string str = string.Format("Pre-Authentication Succeded! Token: '{0}' ", (hub.AuthenticationProvider as PreAuthAccessTokenAuthenticator).Token); 128 | 129 | AddText(str); 130 | } 131 | 132 | private void AuthenticationProvider_OnAuthenticationFailed(IAuthenticationProvider provider, string reason) 133 | { 134 | AddText(string.Format("Authentication Failed! Reason: '{0}'", reason)); 135 | } 136 | 137 | /// 138 | /// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. 139 | /// 140 | private void Hub_OnConnected(HubConnection hub) 141 | { 142 | AddText(string.Format("Hub Connected with {0} transport using the {1} encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); 143 | SetButtons(false, true); 144 | 145 | // Call a parameterless function. We expect a string return value. 146 | hub.Invoke("Echo", "Message from the client") 147 | .OnSuccess(ret => AddText(string.Format("'Echo' returned: '{0}'", ret)).AddLeftPadding(20)); 148 | 149 | AddText("'Message from the client' sent!") 150 | .AddLeftPadding(20); 151 | } 152 | 153 | /// 154 | /// This is called when the hub is closed after a StartClose() call. 155 | /// 156 | private void Hub_OnClosed(HubConnection hub) 157 | { 158 | AddText("Hub Closed"); 159 | SetButtons(true, false); 160 | } 161 | 162 | /// 163 | /// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. 164 | /// 165 | private void Hub_OnError(HubConnection hub, string error) 166 | { 167 | AddText(string.Format("Hub Error: {0}", error)); 168 | SetButtons(true, false); 169 | } 170 | 171 | private void SetButtons(bool connect, bool close) 172 | { 173 | if (this._connectButton != null) 174 | this._connectButton.interactable = connect; 175 | 176 | if (this._closeButton != null) 177 | this._closeButton.interactable = close; 178 | } 179 | 180 | private TextListItem AddText(string text) 181 | { 182 | return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 183 | } 184 | } 185 | 186 | public sealed class PreAuthAccessTokenAuthenticator : IAuthenticationProvider 187 | { 188 | /// 189 | /// No pre-auth step required for this type of authentication 190 | /// 191 | public bool IsPreAuthRequired { get { return true; } } 192 | 193 | #pragma warning disable 0067 194 | /// 195 | /// Not used event as IsPreAuthRequired is false 196 | /// 197 | public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; 198 | 199 | /// 200 | /// Not used event as IsPreAuthRequired is false 201 | /// 202 | public event OnAuthenticationFailedDelegate OnAuthenticationFailed; 203 | 204 | #pragma warning restore 0067 205 | 206 | public string Token { get; private set; } 207 | 208 | private Uri authenticationUri; 209 | 210 | private HTTPRequest authenticationRequest; 211 | private bool isCancellationRequested; 212 | 213 | public PreAuthAccessTokenAuthenticator(Uri authUri) 214 | { 215 | this.authenticationUri = authUri; 216 | } 217 | 218 | public void StartAuthentication() 219 | { 220 | this.authenticationRequest = new HTTPRequest(this.authenticationUri, OnAuthenticationRequestFinished); 221 | this.authenticationRequest.Send(); 222 | } 223 | 224 | private void OnAuthenticationRequestFinished(HTTPRequest req, HTTPResponse resp) 225 | { 226 | switch (req.State) 227 | { 228 | // The request finished without any problem. 229 | case HTTPRequestStates.Finished: 230 | if (resp.IsSuccess) 231 | { 232 | this.authenticationRequest = null; 233 | this.Token = resp.DataAsText; 234 | if (this.OnAuthenticationSucceded != null) 235 | this.OnAuthenticationSucceded(this); 236 | } 237 | else // Internal server error? 238 | AuthenticationFailed(string.Format("Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}", 239 | resp.StatusCode, 240 | resp.Message, 241 | resp.DataAsText)); 242 | break; 243 | 244 | // The request finished with an unexpected error. The request's Exception property may contain more info about the error. 245 | case HTTPRequestStates.Error: 246 | AuthenticationFailed("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "" + req.Exception.StackTrace) : "No Exception")); 247 | break; 248 | 249 | // The request aborted, initiated by the user. 250 | case HTTPRequestStates.Aborted: 251 | AuthenticationFailed("Request Aborted!"); 252 | break; 253 | 254 | // Connecting to the server is timed out. 255 | case HTTPRequestStates.ConnectionTimedOut: 256 | AuthenticationFailed("Connection Timed Out!"); 257 | break; 258 | 259 | // The request didn't finished in the given time. 260 | case HTTPRequestStates.TimedOut: 261 | AuthenticationFailed("Processing the request Timed Out!"); 262 | break; 263 | } 264 | } 265 | 266 | private void AuthenticationFailed(string reason) 267 | { 268 | this.authenticationRequest = null; 269 | 270 | if (this.isCancellationRequested) 271 | return; 272 | 273 | if (this.OnAuthenticationFailed != null) 274 | this.OnAuthenticationFailed(this, reason); 275 | } 276 | 277 | /// 278 | /// Prepares the request by adding two headers to it 279 | /// 280 | public void PrepareRequest(BestHTTP.HTTPRequest request) 281 | { 282 | if (HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri) == SupportedProtocols.HTTP) 283 | request.Uri = PrepareUri(request.Uri); 284 | } 285 | 286 | public Uri PrepareUri(Uri uri) 287 | { 288 | if (!string.IsNullOrEmpty(this.Token)) 289 | { 290 | string query = string.IsNullOrEmpty(uri.Query) ? "?" : uri.Query + "&"; 291 | UriBuilder uriBuilder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, query + "access_token=" + this.Token); 292 | return uriBuilder.Uri; 293 | } 294 | else 295 | return uri; 296 | } 297 | 298 | public void Cancel() 299 | { 300 | this.isCancellationRequested = true; 301 | if (this.authenticationRequest != null) 302 | this.authenticationRequest.Abort(); 303 | } 304 | } 305 | } 306 | 307 | #endif 308 | -------------------------------------------------------------------------------- /SignalRCore/UploadHubSample.cs: -------------------------------------------------------------------------------- 1 | #if !BESTHTTP_DISABLE_SIGNALR_CORE 2 | 3 | using BestHTTP; 4 | using BestHTTP.Examples.Helpers; 5 | using BestHTTP.SignalRCore; 6 | using BestHTTP.SignalRCore.Encoders; 7 | using System; 8 | using System.Collections; 9 | using UnityEngine; 10 | using UnityEngine.UI; 11 | 12 | namespace BestHTTP.Examples 13 | { 14 | /// 15 | /// This sample demonstrates redirection capabilities. The server will redirect a few times the client before 16 | /// routing it to the final endpoint. 17 | /// 18 | public sealed class UploadHubSample : BestHTTP.Examples.Helpers.SampleBase 19 | { 20 | #pragma warning disable 0649 21 | 22 | [SerializeField] 23 | private string _path = "/uploading"; 24 | 25 | [SerializeField] 26 | private ScrollRect _scrollRect; 27 | 28 | [SerializeField] 29 | private RectTransform _contentRoot; 30 | 31 | [SerializeField] 32 | private TextListItem _listItemPrefab; 33 | 34 | [SerializeField] 35 | private int _maxListItemEntries = 100; 36 | 37 | [SerializeField] 38 | private Button _connectButton; 39 | 40 | [SerializeField] 41 | private Button _closeButton; 42 | 43 | [SerializeField] 44 | private float _yieldWaitTime = 0.1f; 45 | 46 | #pragma warning restore 47 | 48 | // Instance of the HubConnection 49 | private HubConnection hub; 50 | 51 | protected override void Start() 52 | { 53 | base.Start(); 54 | 55 | SetButtons(true, false); 56 | } 57 | 58 | void OnDestroy() 59 | { 60 | if (hub != null) 61 | { 62 | hub.StartClose(); 63 | } 64 | } 65 | 66 | public void OnConnectButton() 67 | { 68 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 69 | try 70 | { 71 | MessagePack.Resolvers.StaticCompositeResolver.Instance.Register( 72 | MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance, 73 | MessagePack.Unity.UnityResolver.Instance, 74 | //MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance, 75 | //MessagePack.Resolvers.StandardResolver.Instance, 76 | MessagePack.Resolvers.ContractlessStandardResolver.Instance 77 | ); 78 | 79 | var options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.StaticCompositeResolver.Instance); 80 | MessagePack.MessagePackSerializer.DefaultOptions = options; 81 | } 82 | catch 83 | { } 84 | #endif 85 | 86 | IProtocol protocol = null; 87 | #if BESTHTTP_SIGNALR_CORE_ENABLE_MESSAGEPACK_CSHARP 88 | protocol = new MessagePackCSharpProtocol(); 89 | #elif BESTHTTP_SIGNALR_CORE_ENABLE_GAMEDEVWARE_MESSAGEPACK 90 | protocol = new MessagePackProtocol(); 91 | #else 92 | protocol = new JsonProtocol(new LitJsonEncoder()); 93 | #endif 94 | 95 | // Crete the HubConnection 96 | hub = new HubConnection(new Uri(this.sampleSelector.BaseURL + this._path), protocol); 97 | 98 | // Subscribe to hub events 99 | hub.OnConnected += Hub_OnConnected; 100 | hub.OnError += Hub_OnError; 101 | hub.OnClosed += Hub_OnClosed; 102 | 103 | hub.OnRedirected += Hub_Redirected; 104 | 105 | hub.OnTransportEvent += (hub, transport, ev) => AddText(string.Format("Transport({0}) event: {1}", transport.TransportType, ev)); 106 | 107 | // And finally start to connect to the server 108 | hub.StartConnect(); 109 | 110 | AddText("StartConnect called"); 111 | 112 | SetButtons(false, false); 113 | } 114 | 115 | public void OnCloseButton() 116 | { 117 | if (this.hub != null) 118 | { 119 | this.hub.StartClose(); 120 | 121 | AddText("StartClose called"); 122 | SetButtons(false, false); 123 | } 124 | } 125 | 126 | private void Hub_Redirected(HubConnection hub, Uri oldUri, Uri newUri) 127 | { 128 | AddText(string.Format("Hub connection redirected to '{0}'!", hub.Uri)); 129 | } 130 | 131 | /// 132 | /// This callback is called when the plugin is connected to the server successfully. Messages can be sent to the server after this point. 133 | /// 134 | private void Hub_OnConnected(HubConnection hub) 135 | { 136 | AddText(string.Format("Hub Connected with {0} transport using the {1} encoder.", hub.Transport.TransportType.ToString(), hub.Protocol.Name)); 137 | 138 | StartCoroutine(UploadWord()); 139 | 140 | SetButtons(false, true); 141 | } 142 | 143 | private IEnumerator UploadWord() 144 | { 145 | AddText("UploadWord:"); 146 | 147 | var controller = hub.GetUpStreamController("UploadWord"); 148 | controller.OnSuccess(result => 149 | { 150 | AddText(string.Format("UploadWord completed, result: '{0}'", result)) 151 | .AddLeftPadding(20); 152 | AddText(""); 153 | 154 | StartCoroutine(ScoreTracker()); 155 | }); 156 | 157 | yield return new WaitForSeconds(_yieldWaitTime); 158 | controller.UploadParam("Hello "); 159 | 160 | AddText("'Hello ' uploaded!") 161 | .AddLeftPadding(20); 162 | 163 | yield return new WaitForSeconds(_yieldWaitTime); 164 | controller.UploadParam("World"); 165 | 166 | AddText("'World' uploaded!") 167 | .AddLeftPadding(20); 168 | 169 | yield return new WaitForSeconds(_yieldWaitTime); 170 | controller.UploadParam("!!"); 171 | 172 | AddText("'!!' uploaded!") 173 | .AddLeftPadding(20); 174 | 175 | yield return new WaitForSeconds(_yieldWaitTime); 176 | 177 | controller.Finish(); 178 | 179 | AddText("Sent upload finished message.") 180 | .AddLeftPadding(20); 181 | 182 | yield return new WaitForSeconds(_yieldWaitTime); 183 | } 184 | 185 | private IEnumerator ScoreTracker() 186 | { 187 | AddText("ScoreTracker:"); 188 | var controller = hub.GetUpStreamController("ScoreTracker"); 189 | 190 | controller.OnSuccess(result => 191 | { 192 | AddText(string.Format("ScoreTracker completed, result: '{0}'", result)) 193 | .AddLeftPadding(20); 194 | AddText(""); 195 | 196 | StartCoroutine(ScoreTrackerWithParameterChannels()); 197 | }); 198 | 199 | const int numScores = 5; 200 | for (int i = 0; i < numScores; i++) 201 | { 202 | yield return new WaitForSeconds(_yieldWaitTime); 203 | 204 | int p1 = UnityEngine.Random.Range(0, 10); 205 | int p2 = UnityEngine.Random.Range(0, 10); 206 | controller.UploadParam(p1, p2); 207 | 208 | AddText(string.Format("Score({0}/{1}) uploaded! p1's score: {2} p2's score: {3}", i + 1, numScores, p1, p2)) 209 | .AddLeftPadding(20); 210 | } 211 | 212 | yield return new WaitForSeconds(_yieldWaitTime); 213 | controller.Finish(); 214 | 215 | AddText("Sent upload finished message.") 216 | .AddLeftPadding(20); 217 | 218 | yield return new WaitForSeconds(_yieldWaitTime); 219 | } 220 | 221 | private IEnumerator ScoreTrackerWithParameterChannels() 222 | { 223 | AddText("ScoreTracker using upload channels:"); 224 | 225 | using (var controller = hub.GetUpStreamController("ScoreTracker")) 226 | { 227 | controller.OnSuccess(result => 228 | { 229 | AddText(string.Format("ScoreTracker completed, result: '{0}'", result)) 230 | .AddLeftPadding(20); 231 | AddText(""); 232 | 233 | StartCoroutine(StreamEcho()); 234 | }); 235 | 236 | const int numScores = 5; 237 | 238 | // While the server's ScoreTracker has two parameters, we can upload those parameters separately 239 | // So here we 240 | 241 | using (var player1param = controller.GetUploadChannel(0)) 242 | { 243 | for (int i = 0; i < numScores; i++) 244 | { 245 | yield return new WaitForSeconds(_yieldWaitTime); 246 | 247 | int score = UnityEngine.Random.Range(0, 10); 248 | player1param.Upload(score); 249 | 250 | AddText(string.Format("Player 1's score({0}/{1}) uploaded! Score: {2}", i + 1, numScores, score)) 251 | .AddLeftPadding(20); 252 | } 253 | } 254 | 255 | AddText(""); 256 | 257 | using (var player2param = controller.GetUploadChannel(1)) 258 | { 259 | for (int i = 0; i < numScores; i++) 260 | { 261 | yield return new WaitForSeconds(_yieldWaitTime); 262 | 263 | int score = UnityEngine.Random.Range(0, 10); 264 | player2param.Upload(score); 265 | 266 | AddText(string.Format("Player 2's score({0}/{1}) uploaded! Score: {2}", i + 1, numScores, score)) 267 | .AddLeftPadding(20); 268 | } 269 | } 270 | 271 | AddText("All scores uploaded!") 272 | .AddLeftPadding(20); 273 | } 274 | yield return new WaitForSeconds(_yieldWaitTime); 275 | } 276 | 277 | private IEnumerator StreamEcho() 278 | { 279 | AddText("StreamEcho:"); 280 | using (var controller = hub.GetUpAndDownStreamController("StreamEcho")) 281 | { 282 | controller.OnSuccess(result => 283 | { 284 | AddText("StreamEcho completed!") 285 | .AddLeftPadding(20); 286 | AddText(""); 287 | 288 | StartCoroutine(PersonEcho()); 289 | }); 290 | 291 | controller.OnItem(item => 292 | { 293 | AddText(string.Format("Received from server: '{0}'", item)) 294 | .AddLeftPadding(20); 295 | }); 296 | 297 | const int numMessages = 5; 298 | for (int i = 0; i < numMessages; i++) 299 | { 300 | yield return new WaitForSeconds(_yieldWaitTime); 301 | 302 | string message = string.Format("Message from client {0}/{1}", i + 1, numMessages); 303 | controller.UploadParam(message); 304 | 305 | AddText(string.Format("Sent message to the server: {0}", message)) 306 | .AddLeftPadding(20); 307 | } 308 | 309 | yield return new WaitForSeconds(_yieldWaitTime); 310 | } 311 | 312 | AddText("Upload finished!") 313 | .AddLeftPadding(20); 314 | 315 | yield return new WaitForSeconds(_yieldWaitTime); 316 | } 317 | 318 | /// 319 | /// This is basically the same as the previous StreamEcho, but it's streaming a complex object (Person 320 | /// 321 | private IEnumerator PersonEcho() 322 | { 323 | AddText("PersonEcho:"); 324 | 325 | using (var controller = hub.GetUpAndDownStreamController("PersonEcho")) 326 | { 327 | controller.OnSuccess(result => 328 | { 329 | AddText("PersonEcho completed!") 330 | .AddLeftPadding(20); 331 | AddText(""); 332 | AddText("All Done!"); 333 | }); 334 | 335 | controller.OnItem(item => 336 | { 337 | AddText(string.Format("Received from server: '{0}'", item)) 338 | .AddLeftPadding(20); 339 | }); 340 | 341 | const int numMessages = 5; 342 | for (int i = 0; i < numMessages; i++) 343 | { 344 | yield return new WaitForSeconds(_yieldWaitTime); 345 | 346 | Person person = new Person() 347 | { 348 | Name = "Mr. Smith", 349 | Age = 20 + i * 2 350 | }; 351 | 352 | controller.UploadParam(person); 353 | 354 | AddText(string.Format("Sent person to the server: {0}", person)) 355 | .AddLeftPadding(20); 356 | } 357 | 358 | yield return new WaitForSeconds(_yieldWaitTime); 359 | } 360 | AddText("Upload finished!") 361 | .AddLeftPadding(20); 362 | 363 | yield return new WaitForSeconds(_yieldWaitTime); 364 | } 365 | 366 | /// 367 | /// This is called when the hub is closed after a StartClose() call. 368 | /// 369 | private void Hub_OnClosed(HubConnection hub) 370 | { 371 | AddText("Hub Closed"); 372 | 373 | SetButtons(true, false); 374 | } 375 | 376 | /// 377 | /// Called when an unrecoverable error happen. After this event the hub will not send or receive any messages. 378 | /// 379 | private void Hub_OnError(HubConnection hub, string error) 380 | { 381 | AddText(string.Format("Hub Error: {0}", error)); 382 | 383 | SetButtons(true, false); 384 | } 385 | 386 | private void SetButtons(bool connect, bool close) 387 | { 388 | if (this._connectButton != null) 389 | this._connectButton.interactable = connect; 390 | 391 | if (this._closeButton != null) 392 | this._closeButton.interactable = close; 393 | } 394 | 395 | private TextListItem AddText(string text) 396 | { 397 | return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect); 398 | } 399 | } 400 | } 401 | 402 | #endif 403 | -------------------------------------------------------------------------------- /Helpers/SelectorUI/ExampleInfo.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1001 &100100000 4 | Prefab: 5 | m_ObjectHideFlags: 1 6 | serializedVersion: 2 7 | m_Modification: 8 | m_TransformParent: {fileID: 0} 9 | m_Modifications: [] 10 | m_RemovedComponents: [] 11 | m_ParentPrefab: {fileID: 0} 12 | m_RootGameObject: {fileID: 1844362866936956} 13 | m_IsPrefabParent: 1 14 | --- !u!1 &1164538047482000 15 | GameObject: 16 | m_ObjectHideFlags: 0 17 | m_PrefabParentObject: {fileID: 0} 18 | m_PrefabInternal: {fileID: 100100000} 19 | serializedVersion: 5 20 | m_Component: 21 | - component: {fileID: 224919186422011706} 22 | - component: {fileID: 222389287056714158} 23 | - component: {fileID: 114764438814610556} 24 | - component: {fileID: 114425745104630664} 25 | - component: {fileID: 114698872193548206} 26 | - component: {fileID: 114431572224555080} 27 | m_Layer: 5 28 | m_Name: Execute Sample Button 29 | m_TagString: Untagged 30 | m_Icon: {fileID: 0} 31 | m_NavMeshLayer: 0 32 | m_StaticEditorFlags: 0 33 | m_IsActive: 1 34 | --- !u!1 &1248744110497022 35 | GameObject: 36 | m_ObjectHideFlags: 1 37 | m_PrefabParentObject: {fileID: 0} 38 | m_PrefabInternal: {fileID: 100100000} 39 | serializedVersion: 5 40 | m_Component: 41 | - component: {fileID: 224829437101102402} 42 | - component: {fileID: 222702223361243722} 43 | - component: {fileID: 114859111753421280} 44 | m_Layer: 5 45 | m_Name: Text 46 | m_TagString: Untagged 47 | m_Icon: {fileID: 0} 48 | m_NavMeshLayer: 0 49 | m_StaticEditorFlags: 0 50 | m_IsActive: 1 51 | --- !u!1 &1397735183102074 52 | GameObject: 53 | m_ObjectHideFlags: 0 54 | m_PrefabParentObject: {fileID: 0} 55 | m_PrefabInternal: {fileID: 100100000} 56 | serializedVersion: 5 57 | m_Component: 58 | - component: {fileID: 224216400874628136} 59 | - component: {fileID: 222516365031537928} 60 | - component: {fileID: 114059492809184142} 61 | - component: {fileID: 114493726555860862} 62 | m_Layer: 5 63 | m_Name: Description 64 | m_TagString: Untagged 65 | m_Icon: {fileID: 0} 66 | m_NavMeshLayer: 0 67 | m_StaticEditorFlags: 0 68 | m_IsActive: 1 69 | --- !u!1 &1531245648659982 70 | GameObject: 71 | m_ObjectHideFlags: 0 72 | m_PrefabParentObject: {fileID: 0} 73 | m_PrefabInternal: {fileID: 100100000} 74 | serializedVersion: 5 75 | m_Component: 76 | - component: {fileID: 224368887501416132} 77 | - component: {fileID: 222882642833739976} 78 | - component: {fileID: 114666879149101946} 79 | m_Layer: 5 80 | m_Name: Header 81 | m_TagString: Untagged 82 | m_Icon: {fileID: 0} 83 | m_NavMeshLayer: 0 84 | m_StaticEditorFlags: 0 85 | m_IsActive: 1 86 | --- !u!1 &1844362866936956 87 | GameObject: 88 | m_ObjectHideFlags: 0 89 | m_PrefabParentObject: {fileID: 0} 90 | m_PrefabInternal: {fileID: 100100000} 91 | serializedVersion: 5 92 | m_Component: 93 | - component: {fileID: 224811306304922960} 94 | - component: {fileID: 114988937263723000} 95 | - component: {fileID: 114645756897919032} 96 | - component: {fileID: 114471653971156696} 97 | m_Layer: 5 98 | m_Name: ExampleInfo 99 | m_TagString: Untagged 100 | m_Icon: {fileID: 0} 101 | m_NavMeshLayer: 0 102 | m_StaticEditorFlags: 0 103 | m_IsActive: 1 104 | --- !u!114 &114059492809184142 105 | MonoBehaviour: 106 | m_ObjectHideFlags: 1 107 | m_PrefabParentObject: {fileID: 0} 108 | m_PrefabInternal: {fileID: 100100000} 109 | m_GameObject: {fileID: 1397735183102074} 110 | m_Enabled: 1 111 | m_EditorHideFlags: 0 112 | m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 113 | m_Name: 114 | m_EditorClassIdentifier: 115 | m_Material: {fileID: 0} 116 | m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} 117 | m_RaycastTarget: 1 118 | m_OnCullStateChanged: 119 | m_PersistentCalls: 120 | m_Calls: [] 121 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 122 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 123 | m_FontData: 124 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 125 | m_FontSize: 14 126 | m_FontStyle: 0 127 | m_BestFit: 0 128 | m_MinSize: 10 129 | m_MaxSize: 40 130 | m_Alignment: 0 131 | m_AlignByGeometry: 0 132 | m_RichText: 1 133 | m_HorizontalOverflow: 0 134 | m_VerticalOverflow: 0 135 | m_LineSpacing: 1 136 | m_Text: Description 137 | --- !u!114 &114425745104630664 138 | MonoBehaviour: 139 | m_ObjectHideFlags: 1 140 | m_PrefabParentObject: {fileID: 0} 141 | m_PrefabInternal: {fileID: 100100000} 142 | m_GameObject: {fileID: 1164538047482000} 143 | m_Enabled: 1 144 | m_EditorHideFlags: 0 145 | m_Script: {fileID: 1392445389, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 146 | m_Name: 147 | m_EditorClassIdentifier: 148 | m_Navigation: 149 | m_Mode: 3 150 | m_SelectOnUp: {fileID: 0} 151 | m_SelectOnDown: {fileID: 0} 152 | m_SelectOnLeft: {fileID: 0} 153 | m_SelectOnRight: {fileID: 0} 154 | m_Transition: 1 155 | m_Colors: 156 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 157 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 158 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 159 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 160 | m_ColorMultiplier: 1 161 | m_FadeDuration: 0.1 162 | m_SpriteState: 163 | m_HighlightedSprite: {fileID: 0} 164 | m_PressedSprite: {fileID: 0} 165 | m_DisabledSprite: {fileID: 0} 166 | m_AnimationTriggers: 167 | m_NormalTrigger: Normal 168 | m_HighlightedTrigger: Highlighted 169 | m_PressedTrigger: Pressed 170 | m_DisabledTrigger: Disabled 171 | m_Interactable: 1 172 | m_TargetGraphic: {fileID: 114764438814610556} 173 | m_OnClick: 174 | m_PersistentCalls: 175 | m_Calls: 176 | - m_Target: {fileID: 114471653971156696} 177 | m_MethodName: OnExecuteExample 178 | m_Mode: 1 179 | m_Arguments: 180 | m_ObjectArgument: {fileID: 0} 181 | m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine 182 | m_IntArgument: 0 183 | m_FloatArgument: 0 184 | m_StringArgument: 185 | m_BoolArgument: 0 186 | m_CallState: 2 187 | m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, 188 | Culture=neutral, PublicKeyToken=null 189 | --- !u!114 &114431572224555080 190 | MonoBehaviour: 191 | m_ObjectHideFlags: 1 192 | m_PrefabParentObject: {fileID: 0} 193 | m_PrefabInternal: {fileID: 100100000} 194 | m_GameObject: {fileID: 1164538047482000} 195 | m_Enabled: 1 196 | m_EditorHideFlags: 0 197 | m_Script: {fileID: 1741964061, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 198 | m_Name: 199 | m_EditorClassIdentifier: 200 | m_HorizontalFit: 0 201 | m_VerticalFit: 2 202 | --- !u!114 &114471653971156696 203 | MonoBehaviour: 204 | m_ObjectHideFlags: 1 205 | m_PrefabParentObject: {fileID: 0} 206 | m_PrefabInternal: {fileID: 100100000} 207 | m_GameObject: {fileID: 1844362866936956} 208 | m_Enabled: 1 209 | m_EditorHideFlags: 0 210 | m_Script: {fileID: 11500000, guid: fd424cc4259865c4ba2fb4d9d68d0272, type: 3} 211 | m_Name: 212 | m_EditorClassIdentifier: 213 | _header: {fileID: 114666879149101946} 214 | _description: {fileID: 114059492809184142} 215 | --- !u!114 &114493726555860862 216 | MonoBehaviour: 217 | m_ObjectHideFlags: 1 218 | m_PrefabParentObject: {fileID: 0} 219 | m_PrefabInternal: {fileID: 100100000} 220 | m_GameObject: {fileID: 1397735183102074} 221 | m_Enabled: 1 222 | m_EditorHideFlags: 0 223 | m_Script: {fileID: 1679637790, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 224 | m_Name: 225 | m_EditorClassIdentifier: 226 | m_IgnoreLayout: 0 227 | m_MinWidth: -1 228 | m_MinHeight: -1 229 | m_PreferredWidth: -1 230 | m_PreferredHeight: -1 231 | m_FlexibleWidth: 1 232 | m_FlexibleHeight: 1 233 | m_LayoutPriority: 1 234 | --- !u!114 &114645756897919032 235 | MonoBehaviour: 236 | m_ObjectHideFlags: 1 237 | m_PrefabParentObject: {fileID: 0} 238 | m_PrefabInternal: {fileID: 100100000} 239 | m_GameObject: {fileID: 1844362866936956} 240 | m_Enabled: 1 241 | m_EditorHideFlags: 0 242 | m_Script: {fileID: 1679637790, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 243 | m_Name: 244 | m_EditorClassIdentifier: 245 | m_IgnoreLayout: 0 246 | m_MinWidth: -1 247 | m_MinHeight: -1 248 | m_PreferredWidth: -1 249 | m_PreferredHeight: -1 250 | m_FlexibleWidth: 3 251 | m_FlexibleHeight: 1 252 | m_LayoutPriority: 1 253 | --- !u!114 &114666879149101946 254 | MonoBehaviour: 255 | m_ObjectHideFlags: 1 256 | m_PrefabParentObject: {fileID: 0} 257 | m_PrefabInternal: {fileID: 100100000} 258 | m_GameObject: {fileID: 1531245648659982} 259 | m_Enabled: 1 260 | m_EditorHideFlags: 0 261 | m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 262 | m_Name: 263 | m_EditorClassIdentifier: 264 | m_Material: {fileID: 0} 265 | m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} 266 | m_RaycastTarget: 0 267 | m_OnCullStateChanged: 268 | m_PersistentCalls: 269 | m_Calls: [] 270 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 271 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 272 | m_FontData: 273 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 274 | m_FontSize: 24 275 | m_FontStyle: 0 276 | m_BestFit: 0 277 | m_MinSize: 2 278 | m_MaxSize: 40 279 | m_Alignment: 1 280 | m_AlignByGeometry: 0 281 | m_RichText: 1 282 | m_HorizontalOverflow: 0 283 | m_VerticalOverflow: 0 284 | m_LineSpacing: 1 285 | m_Text: Header 286 | --- !u!114 &114698872193548206 287 | MonoBehaviour: 288 | m_ObjectHideFlags: 1 289 | m_PrefabParentObject: {fileID: 0} 290 | m_PrefabInternal: {fileID: 100100000} 291 | m_GameObject: {fileID: 1164538047482000} 292 | m_Enabled: 1 293 | m_EditorHideFlags: 0 294 | m_Script: {fileID: 1297475563, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 295 | m_Name: 296 | m_EditorClassIdentifier: 297 | m_Padding: 298 | m_Left: 5 299 | m_Right: 5 300 | m_Top: 5 301 | m_Bottom: 5 302 | m_ChildAlignment: 0 303 | m_Spacing: 0 304 | m_ChildForceExpandWidth: 1 305 | m_ChildForceExpandHeight: 0 306 | m_ChildControlWidth: 1 307 | m_ChildControlHeight: 1 308 | --- !u!114 &114764438814610556 309 | MonoBehaviour: 310 | m_ObjectHideFlags: 1 311 | m_PrefabParentObject: {fileID: 0} 312 | m_PrefabInternal: {fileID: 100100000} 313 | m_GameObject: {fileID: 1164538047482000} 314 | m_Enabled: 1 315 | m_EditorHideFlags: 0 316 | m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 317 | m_Name: 318 | m_EditorClassIdentifier: 319 | m_Material: {fileID: 0} 320 | m_Color: {r: 1, g: 1, b: 1, a: 1} 321 | m_RaycastTarget: 1 322 | m_OnCullStateChanged: 323 | m_PersistentCalls: 324 | m_Calls: [] 325 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 326 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 327 | m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} 328 | m_Type: 1 329 | m_PreserveAspect: 0 330 | m_FillCenter: 1 331 | m_FillMethod: 4 332 | m_FillAmount: 1 333 | m_FillClockwise: 1 334 | m_FillOrigin: 0 335 | --- !u!114 &114859111753421280 336 | MonoBehaviour: 337 | m_ObjectHideFlags: 1 338 | m_PrefabParentObject: {fileID: 0} 339 | m_PrefabInternal: {fileID: 100100000} 340 | m_GameObject: {fileID: 1248744110497022} 341 | m_Enabled: 1 342 | m_EditorHideFlags: 0 343 | m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 344 | m_Name: 345 | m_EditorClassIdentifier: 346 | m_Material: {fileID: 0} 347 | m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} 348 | m_RaycastTarget: 1 349 | m_OnCullStateChanged: 350 | m_PersistentCalls: 351 | m_Calls: [] 352 | m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, 353 | Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 354 | m_FontData: 355 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 356 | m_FontSize: 14 357 | m_FontStyle: 0 358 | m_BestFit: 0 359 | m_MinSize: 10 360 | m_MaxSize: 40 361 | m_Alignment: 4 362 | m_AlignByGeometry: 0 363 | m_RichText: 1 364 | m_HorizontalOverflow: 0 365 | m_VerticalOverflow: 0 366 | m_LineSpacing: 1 367 | m_Text: Execute 368 | --- !u!114 &114988937263723000 369 | MonoBehaviour: 370 | m_ObjectHideFlags: 1 371 | m_PrefabParentObject: {fileID: 0} 372 | m_PrefabInternal: {fileID: 100100000} 373 | m_GameObject: {fileID: 1844362866936956} 374 | m_Enabled: 1 375 | m_EditorHideFlags: 0 376 | m_Script: {fileID: 1297475563, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} 377 | m_Name: 378 | m_EditorClassIdentifier: 379 | m_Padding: 380 | m_Left: 5 381 | m_Right: 5 382 | m_Top: 5 383 | m_Bottom: 5 384 | m_ChildAlignment: 0 385 | m_Spacing: 5 386 | m_ChildForceExpandWidth: 1 387 | m_ChildForceExpandHeight: 0 388 | m_ChildControlWidth: 1 389 | m_ChildControlHeight: 1 390 | --- !u!222 &222389287056714158 391 | CanvasRenderer: 392 | m_ObjectHideFlags: 1 393 | m_PrefabParentObject: {fileID: 0} 394 | m_PrefabInternal: {fileID: 100100000} 395 | m_GameObject: {fileID: 1164538047482000} 396 | --- !u!222 &222516365031537928 397 | CanvasRenderer: 398 | m_ObjectHideFlags: 1 399 | m_PrefabParentObject: {fileID: 0} 400 | m_PrefabInternal: {fileID: 100100000} 401 | m_GameObject: {fileID: 1397735183102074} 402 | --- !u!222 &222702223361243722 403 | CanvasRenderer: 404 | m_ObjectHideFlags: 1 405 | m_PrefabParentObject: {fileID: 0} 406 | m_PrefabInternal: {fileID: 100100000} 407 | m_GameObject: {fileID: 1248744110497022} 408 | --- !u!222 &222882642833739976 409 | CanvasRenderer: 410 | m_ObjectHideFlags: 1 411 | m_PrefabParentObject: {fileID: 0} 412 | m_PrefabInternal: {fileID: 100100000} 413 | m_GameObject: {fileID: 1531245648659982} 414 | --- !u!224 &224216400874628136 415 | RectTransform: 416 | m_ObjectHideFlags: 1 417 | m_PrefabParentObject: {fileID: 0} 418 | m_PrefabInternal: {fileID: 100100000} 419 | m_GameObject: {fileID: 1397735183102074} 420 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} 421 | m_LocalPosition: {x: 0, y: 0, z: 0} 422 | m_LocalScale: {x: 1, y: 1, z: 1} 423 | m_Children: [] 424 | m_Father: {fileID: 224811306304922960} 425 | m_RootOrder: 1 426 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 427 | m_AnchorMin: {x: 0, y: 0} 428 | m_AnchorMax: {x: 0, y: 0} 429 | m_AnchoredPosition: {x: 0, y: 0} 430 | m_SizeDelta: {x: 0, y: 0} 431 | m_Pivot: {x: 0.5, y: 0.5} 432 | --- !u!224 &224368887501416132 433 | RectTransform: 434 | m_ObjectHideFlags: 1 435 | m_PrefabParentObject: {fileID: 0} 436 | m_PrefabInternal: {fileID: 100100000} 437 | m_GameObject: {fileID: 1531245648659982} 438 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 439 | m_LocalPosition: {x: 0, y: 0, z: 0} 440 | m_LocalScale: {x: 1, y: 1, z: 1} 441 | m_Children: [] 442 | m_Father: {fileID: 224811306304922960} 443 | m_RootOrder: 0 444 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 445 | m_AnchorMin: {x: 0, y: 0} 446 | m_AnchorMax: {x: 0, y: 0} 447 | m_AnchoredPosition: {x: 0, y: 0} 448 | m_SizeDelta: {x: 0, y: 0} 449 | m_Pivot: {x: 0.5, y: 0.5} 450 | --- !u!224 &224811306304922960 451 | RectTransform: 452 | m_ObjectHideFlags: 1 453 | m_PrefabParentObject: {fileID: 0} 454 | m_PrefabInternal: {fileID: 100100000} 455 | m_GameObject: {fileID: 1844362866936956} 456 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 457 | m_LocalPosition: {x: 0, y: 0, z: 0} 458 | m_LocalScale: {x: 1, y: 1, z: 1} 459 | m_Children: 460 | - {fileID: 224368887501416132} 461 | - {fileID: 224216400874628136} 462 | - {fileID: 224919186422011706} 463 | m_Father: {fileID: 0} 464 | m_RootOrder: 0 465 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 466 | m_AnchorMin: {x: 0, y: 0} 467 | m_AnchorMax: {x: 0, y: 0} 468 | m_AnchoredPosition: {x: 0, y: 0} 469 | m_SizeDelta: {x: 0, y: 0} 470 | m_Pivot: {x: 0.5, y: 0.5} 471 | --- !u!224 &224829437101102402 472 | RectTransform: 473 | m_ObjectHideFlags: 1 474 | m_PrefabParentObject: {fileID: 0} 475 | m_PrefabInternal: {fileID: 100100000} 476 | m_GameObject: {fileID: 1248744110497022} 477 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 478 | m_LocalPosition: {x: 0, y: 0, z: 0} 479 | m_LocalScale: {x: 1, y: 1, z: 1} 480 | m_Children: [] 481 | m_Father: {fileID: 224919186422011706} 482 | m_RootOrder: 0 483 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 484 | m_AnchorMin: {x: 0, y: 0} 485 | m_AnchorMax: {x: 0, y: 0} 486 | m_AnchoredPosition: {x: 0, y: 0} 487 | m_SizeDelta: {x: 0, y: 0} 488 | m_Pivot: {x: 0.5, y: 0.5} 489 | --- !u!224 &224919186422011706 490 | RectTransform: 491 | m_ObjectHideFlags: 1 492 | m_PrefabParentObject: {fileID: 0} 493 | m_PrefabInternal: {fileID: 100100000} 494 | m_GameObject: {fileID: 1164538047482000} 495 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 496 | m_LocalPosition: {x: 0, y: 0, z: 0} 497 | m_LocalScale: {x: 1, y: 1, z: 1} 498 | m_Children: 499 | - {fileID: 224829437101102402} 500 | m_Father: {fileID: 224811306304922960} 501 | m_RootOrder: 2 502 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 503 | m_AnchorMin: {x: 0, y: 0} 504 | m_AnchorMax: {x: 0, y: 0} 505 | m_AnchoredPosition: {x: 300, y: 0} 506 | m_SizeDelta: {x: 590, y: 0} 507 | m_Pivot: {x: 0.5, y: 0.5} 508 | --------------------------------------------------------------------------------