Search Unity

Best HTTP Released

Discussion in 'Assets and Asset Store' started by BestHTTP, Sep 11, 2013.

  1. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    Did you have any luck looking at the server sent events yet? Would be great to know because we have an upcoming client demo where, because we like to have fun, we are showing on UWP and WebGL.
     
  2. vuluu

    vuluu

    Joined:
    Jun 19, 2019
    Posts:
    11
    Hello @BestHTTP
    How can I get latest version of your great asset? It's still 2.0.3 in Asset Store. Can you share me a private link to latest version? Thank you
     
    Last edited: Feb 6, 2020
  3. cetester001

    cetester001

    Joined:
    Aug 17, 2015
    Posts:
    1
    Issue.jpg Hi @BestHTTP,
    I had encountered an issue regarding the new version of this asset.
    I had a Linux headless server which will make a ping api call every 2 seconds, and each ping is a new HTTPRequest.
    When using BestHTTP v1.11.0, the cpu usage is stable.
    When using BestHTTP/2 v2.0.3, the cpu usage will keep increasing slowly over time, and will reach 100% after several hours.
    My Unity version is 2019.2.18f1.
     
  4. wicea

    wicea

    Joined:
    Jan 23, 2016
    Posts:
    18
    Hi @BestHTTP!
    I have integrated your asset in project, seems everything is good. But I'm receiving reports from my users with such errors.
    Code (CSharp):
    1.  
    2. request.State = Error
    3. request.Exception.Message = Connection closed unexpectedly
    4.  
    Could you please explain me, what's the proper way of handling these errors? Should I just repeat request in this case? And if yes, how to determine this error? Is it right solution to detect error by parsing message for comparing with "Connection closed unexpectedly"?

    I'm using 20 seconds timeout for connection and request. And I don't think that I should increase these values.
    I assume these errors occur when sending a request after opening the application from the background.

    Thanks!
     
  5. appsscity1

    appsscity1

    Joined:
    Feb 6, 2020
    Posts:
    20
    Hi @BestHTTP!
    we have used the below custom extension method to encode the data since the emit method with callback feature
    could send Jsonobject as data parameters, the error we received while using deafaultencoder when using json objetcs
    as data parameters is given below and the custom extension method as well.

    Can you please providve the callback extension method for our custom extension method written below.

    ERROR

    ArgumentException: Encoding the arguments to JSON failed!
    BestHTTP.SocketIO.Socket.Emit (System.String eventName,
    BestHTTP.SocketIO.Events.SocketIOAckCallback callback, System.Object[] args)
    (at Assets/Best HTTP/Source/SocketIO/Socket.cs:197)

    CUSTOM EXTENSION METHOD

    {
    JSONObject pack = new JSONObject(JSONObject.Type.ARRAY);
    pack.Add(eventName);
    pack.Add(jsonobj1);
    pack.Add(jsonobj2);
    UnityEngine.Debug.Log(pack);
    Packet packet = new Packet(TransportEventTypes.Message,
    SocketIOEventTypes.Event,
    socket.Namespace,
    pack.ToString(),
    0,
    0);

    (socket.Manager as IManager).SendPacket(packet);

    return socket;
    }
     
  6. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @t-bergbauer

    Yes, it was an intended behaviour. But, when do you receive this exception? Are you using the file:// protocol?
     
  7. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @ferretnt

    Thank you for your patience, just sent a new package in private.
     
  8. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @vuluu

    Unfortunately, there seems to be some problem on Unity's end as they are still sitting on my update...
     
    vuluu likes this.
  9. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @cetester001

    My first guess is that it might be able to use http/2 and for some unknown reason increases the server's cpu time. I'm going to send a link in private to the next version of the plugin, and it would be good to have a full log using this new one.
     
  10. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @wicea

    This error happens when the HTTP/2 processing thread is closing while there are requests to process. If it's possible, you can retry the requests.
     
  11. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @appsscity1

    If the original problem is the "Encoding the arguments to JSON failed!" error, than you can try out a more advanced json encoder:
    Code (CSharp):
    1. Manager = new SocketManager(new Uri(address));
    2. Manager.Encoder = new SocketIO.JsonEncoders.LitJsonEncoder();
     
  12. appsscity1

    appsscity1

    Joined:
    Feb 6, 2020
    Posts:
    20
    Hi @BestHTTP!

    the emit method we have used is :
    manager.Socket.Emit("custom event", OnAckCallback, "param 1", "param 2");

    and the json encoders we have tried are :
    1. defaultJsonEncoder
    2. LitJson
    3. SimpleJson
    4. NewtonSoftJson

    The problem is with emit function that expects a ackCallback.
     
  13. tomB2

    tomB2

    Joined:
    Mar 27, 2018
    Posts:
    3
    Thanks for the reply. Yes, i use the file:// protocol in the request. Below is an example to reproduce it.

    Code (CSharp):
    1.             var uri = new Uri("D:\\links.xlsx"); //uri.ToString() => file:///D:/links.xlsx
    2.             var httpRequest = new HTTPRequest(uri, true, true, (request, response) =>
    3.             {
    4.                 //when links.xlsx is opened by Excel
    5.                 //System.IO.IOException: Sharing violation on path D:\links.xlsx
    6.                 Debug.Log(request.Exception);
    7.  
    8.             });
    9.             httpRequest.Send();
     
  14. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412

    From a quick test, that now works correctly in Mono (at least in the editor, I didn't check UWP, iOS etc yet), but does not work in WebGL.

    Here is an updated (much simpler) gist - I removed all the UnityWebRequest and other stuff so there is only Best HTTP EventySource.

    https://gist.github.com/ferretnt/4eb61d17b6e8a45a3ea9c3d0b932ac4f

    If I run the sample in editor (replace REMOVED with the same database URL I sent previously) then I see, this in Unity's console window, which looks correct:

    Code (JavaScript):
    1. Message: <color=yellow>"put": "{"path":"/","data":{"a":34,"baz":"boo","c":35,"wobby":"wib"}}"</color>
    2. UnityEngine.Debug:Log(Object)
    3. GetRequest:OnMessage(EventSource, Message) (at Assets/GetRequest.cs:55)
    4. BestHTTP.ServerSentEvents.EventSource:OnMessageReceived(Message) (at Assets/Best HTTP/Source/ServerSentEvents/EventSource.cs:689)
    Which looks correct - the EventSource got its callback. I've also checked that updating the database (via the firebase Web interface or any other way) results in more events correctly being received.

    However, it still doesn't seem to work in WebGL (tested in Chrome). Interestingly, it looks like WebGL *does* get the callback for eventsource.Open(), but then never receives any OnMessage callback. In Chrome's debug console I see this:

    1 ES_Create(https://fir-testdatabase-<REMOVED>.firebaseio.com/foo.json, 0)
    82681bdf-1ffb-4925-9d52-a0994b52bed9:2 1 ES_Create - onOpen

    UnityLoader.js:4 EventSource.Open() callback​


    At the time of writing this email, the text should read: {"a":34,"baz":"boo","c":35,"wobby":"wib"}

    Thanks for the help so far. It would be great if you have time to take a look.

    Thank you for your patience, just sent a new package in private.[/QUOTE]
     
  15. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @appsscity1

    Could you send a repro-project? Tried out and using acknowledgement callbacks worked as expected, without any error (even with the default encoder).
     
    Last edited: Feb 8, 2020
  16. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @ferretnt

    Under WebGL OnMessage works slightly different, it will be called for messages that has no event name, but because your messages has event name ("put" in your test case), you have to subscribe to these events:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using BestHTTP;
    5. using BestHTTP.ServerSentEvents;
    6. using System;
    7.  
    8. public class GetRequest : MonoBehaviour
    9. {
    10.     // Start is called before the first frame update
    11.     void Start()
    12.     {
    13.         HTTPManager.Logger.Level = BestHTTP.Logger.Loglevels.All;
    14.  
    15.         string firebaseHost = "https://fir-testdatabase-<removed>.firebaseio.com/foo.json";
    16.  
    17.         Debug.Log("Opening Event Source");
    18.  
    19.         var eventSource = new EventSource(new Uri(firebaseHost), 1);
    20.         // Start to connect to the server
    21.  
    22.         eventSource.OnOpen += OnOpen;
    23.         eventSource.On("put", OnPutEvent);
    24.         eventSource.Open();
    25.     }
    26.  
    27.     private void OnPutEvent(EventSource eventSource, Message message)
    28.     {
    29.         Debug.Log(string.Format("'put' event: <color=yellow>{0}</color>", message));
    30.     }
    31.  
    32.     private void OnOpen(EventSource eventSource)
    33.     {
    34.         Debug.Log("EventSource.Open() callback");
    35.     }
    36. }
    37.  
     
  17. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @t-bergbauer

    You can write your own IO service to enable this scenario:
    Code (CSharp):
    1.  
    2. public sealed class SharingEnabledIOService : BestHTTP.PlatformSupport.FileSystem.IIOService
    3. {
    4.     public Stream CreateFileStream(string path, FileStreamModes mode)
    5.     {
    6.         if (HTTPManager.Logger.Level == BestHTTP.Logger.Loglevels.All)
    7.             HTTPManager.Logger.Verbose("SharingEnabledIOService", string.Format("CreateFileStream path: '{0}' mode: {1}", path, mode));
    8.  
    9.         switch (mode)
    10.         {
    11.             case FileStreamModes.Create:
    12.                 return new FileStream(path, FileMode.Create);
    13.             case FileStreamModes.Open:
    14.                 return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    15.             case FileStreamModes.Append:
    16.                 return new FileStream(path, FileMode.Append);
    17.         }
    18.  
    19.         throw new NotImplementedException("SharingEnabledIOService.CreateFileStream - mode not implemented: " + mode.ToString());
    20.     }
    21.  
    22.     public void DirectoryCreate(string path)
    23.     {
    24.         if (HTTPManager.Logger.Level == BestHTTP.Logger.Loglevels.All)
    25.             HTTPManager.Logger.Verbose("SharingEnabledIOService", string.Format("DirectoryCreate path: '{0}'", path));
    26.         Directory.CreateDirectory(path);
    27.     }
    28.  
    29.     public bool DirectoryExists(string path)
    30.     {
    31.         bool exists = Directory.Exists(path);
    32.  
    33.         if (HTTPManager.Logger.Level == BestHTTP.Logger.Loglevels.All)
    34.             HTTPManager.Logger.Verbose("SharingEnabledIOService", string.Format("DirectoryExists path: '{0}' exists: {1}", path, exists));
    35.  
    36.         return exists;
    37.     }
    38.  
    39.     public string[] GetFiles(string path)
    40.     {
    41.         var files = Directory.GetFiles(path);
    42.  
    43.         if (HTTPManager.Logger.Level == BestHTTP.Logger.Loglevels.All)
    44.             HTTPManager.Logger.Verbose("SharingEnabledIOService", string.Format("GetFiles path: '{0}' files count: {1}", path, files.Length));
    45.  
    46.         return files;
    47.     }
    48.  
    49.     public void FileDelete(string path)
    50.     {
    51.         if (HTTPManager.Logger.Level == BestHTTP.Logger.Loglevels.All)
    52.             HTTPManager.Logger.Verbose("SharingEnabledIOService", string.Format("FileDelete path: '{0}'", path));
    53.         File.Delete(path);
    54.     }
    55.  
    56.     public bool FileExists(string path)
    57.     {
    58.         bool exists = File.Exists(path);
    59.  
    60.         if (HTTPManager.Logger.Level == BestHTTP.Logger.Loglevels.All)
    61.             HTTPManager.Logger.Verbose("SharingEnabledIOService", string.Format("FileExists path: '{0}' exists: {1}", path, exists));
    62.  
    63.         return exists;
    64.     }
    65. }
    66.  
    Then you can use this instead of the default one:
    Code (CSharp):
    1. HTTPManager.IOService = new SharingEnabledIOService();
     
  18. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    Confirmed working. Thanks for all your work and help.


     
  19. tomB2

    tomB2

    Joined:
    Mar 27, 2018
    Posts:
    3
    Good to know. Thanks for your help

     
  20. iPedro

    iPedro

    Joined:
    Dec 28, 2011
    Posts:
    14
    Hi @BestHTTP

    Can you let us know when you will have support for the Azure SignalR Service? It appears that the SignalRCore implementation does not work with the Azure Service. Also any examples of connecting wuld be extra helpful as well.

    Thanks!
     
  21. iPedro

    iPedro

    Joined:
    Dec 28, 2011
    Posts:
    14
    @AG_Dan
    Where can I find the commit you are referencing? Is it on github somewhere? I want to be able to use Azure's SignalR Service as well and looks like those updates are still not available with the current version of this plugin. Thanks!
     
  22. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @iPedro

    The plugin implements/supports the protocol, not a service. Azure's SignalR Service is just a pre-auth step and then redirects to an actual SignalR Core endpoint. I knoq quite a few plugin users who use it for Azure's SignalR Service in production.
     
  23. iPedro

    iPedro

    Joined:
    Dec 28, 2011
    Posts:
    14
    @BestHTTP

    Thank you, this is great! Can you point me in the direction of some examples or more info on how I can use the plugin with this pre-auth step and redirect?
     
  24. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @iPedro

    1.) Checked out the samples from here: https://github.com/aspnet/AzureSignalR-samples
    2.) Created a new Azure SignalR Service just like in the 'Build Your First Azure SignalR Service Application' article
    3.) Executed the three commands from the bottom of the same article with the relevant connection string
    4.) Tried it out with browser's developer tools open
    5.) In the dev tools it can see what requests it sends out and where it places the access token (url or header)
    6.) Mimic the same with a new authenticator.
    7.) Profit! :)

    The protocol works out of the box, only had to figure out where the returned token goes.

    Here's my code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. using BestHTTP.SignalRCore;
    6. using System;
    7. using BestHTTP;
    8.  
    9. public class SignalRService : MonoBehaviour {
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.         //HTTPManager.Logger.Level = BestHTTP.Logger.Loglevels.All;
    14.         //HTTPManager.Proxy = new HTTPProxy(new Uri("http://localhost:8888"), null, true);
    15.  
    16.         var conn = new HubConnection(new Uri("http://localhost:5000/chat"), new JsonProtocol(new BestHTTP.SignalRCore.Encoders.LitJsonEncoder()));
    17.         conn.AuthenticationProvider = new AzureSignalRServiceAuthenticator(conn);
    18.         conn.OnConnected += OnConnected;
    19.         conn.On<string, string>("BroadcastMessage", OnBroadcastMessage);
    20.         conn.StartConnect();
    21.     }
    22.  
    23.     private void OnBroadcastMessage(string name, string message)
    24.     {
    25.         Debug.LogWarningFormat("[{0}]: {1}", name, message);
    26.     }
    27.  
    28.     private void OnConnected(HubConnection hub)
    29.     {
    30.         hub.Send("BroadcastMessage", "Best HTTP Client", "Hello World!");
    31.     }
    32. }
    33.  
    34. public sealed class AzureSignalRServiceAuthenticator : IAuthenticationProvider
    35. {
    36.     /// <summary>
    37.     /// No pre-auth step required for this type of authentication
    38.     /// </summary>
    39.     public bool IsPreAuthRequired { get { return false; } }
    40.  
    41. #pragma warning disable 0067
    42.     /// <summary>
    43.     /// Not used event as IsPreAuthRequired is false
    44.     /// </summary>
    45.     public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded;
    46.  
    47.     /// <summary>
    48.     /// Not used event as IsPreAuthRequired is false
    49.     /// </summary>
    50.     public event OnAuthenticationFailedDelegate OnAuthenticationFailed;
    51.  
    52. #pragma warning restore 0067
    53.  
    54.     private HubConnection _connection;
    55.  
    56.     public AzureSignalRServiceAuthenticator(HubConnection connection)
    57.     {
    58.         this._connection = connection;
    59.     }
    60.  
    61.     /// <summary>
    62.     /// Not used as IsPreAuthRequired is false
    63.     /// </summary>
    64.     public void StartAuthentication()
    65.     { }
    66.  
    67.     /// <summary>
    68.     /// Prepares the request by adding two headers to it
    69.     /// </summary>
    70.     public void PrepareRequest(BestHTTP.HTTPRequest request)
    71.     {
    72.         if (this._connection.NegotiationResult == null)
    73.             return;
    74.  
    75.         // Add Authorization header to http requests, add access_token param to the uri otherwise
    76.         if (BestHTTP.Connections.HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri) == BestHTTP.Connections.SupportedProtocols.HTTP)
    77.             request.SetHeader("Authorization", "Bearer " + this._connection.NegotiationResult.AccessToken);
    78.         else
    79.             request.Uri = PrepareUriImpl(request.Uri);
    80.     }
    81.  
    82.     public Uri PrepareUri(Uri uri)
    83.     {
    84.         if (uri.Query.StartsWith("??"))
    85.         {
    86.             UriBuilder builder = new UriBuilder(uri);
    87.             builder.Query = builder.Query.Substring(2);
    88.  
    89.             return builder.Uri;
    90.         }
    91.  
    92.         return uri;
    93.     }
    94.  
    95.     private Uri PrepareUriImpl(Uri uri)
    96.     {
    97.         string query = string.IsNullOrEmpty(uri.Query) ? "" : uri.Query + "&";
    98.         UriBuilder uriBuilder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, query + "access_token=" + this._connection.NegotiationResult.AccessToken);
    99.         return uriBuilder.Uri;
    100.     }
    101. }
    102.  
     
    iPedro likes this.
  25. pzy2017

    pzy2017

    Joined:
    May 9, 2019
    Posts:
    8
    Hi @BestHTTP!
    I am trying to get Server Sent Events with authentication. Is it possible to add a header to the request?
    I am trying to make a call like:
    curl -H "X-Api-Key: some_key" somewebsite/tags/tag

    thank you!
     
  26. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @pzy2017

    It's possible, but it's going to work under non-webgl platforms only:
    Code (CSharp):
    1. var eventSource = new EventSource(new Uri("somewebsite/tags/tag"));
    2. #if !UNITY_WEBGL || UNITY_EDITOR
    3. eventSource.InternalRequest.AddHeader("X-Api-Key", "some_key");
    4. #endif
     
  27. pzy2017

    pzy2017

    Joined:
    May 9, 2019
    Posts:
    8
    @BestHTTP
    That worked great, thank you.
    I have one more question. The SSE events are being received every 22 seconds or so, instead of being updated "immediately" as with the curl command. The curl command also begins to receive events immediately while BestHTTP also takes a while to "react". Is there any option that I should change to make this work differently?

    Thank you!
     
  28. umityayla

    umityayla

    Joined:
    May 21, 2019
    Posts:
    26
    Hello @BestHTTP ,

    We're testing out your BestHTTP framework with our Socket.IO application. But we have a problem, is there a way to use ACK callbacks synchronised on code?

    Like;

    Code (CSharp):
    1.  
    2. Emit(eventname, objectToSend, (response) =>
    3.         {
    4.             Debug.Log(response.ToString());
    5.         }
    6.     );
    7.  
     
    Last edited: Feb 14, 2020
  29. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @pzy2017

    v2.0.4 got released a few days ago, in using this version you can override the read buffer size. In cases where the server sends the content as-is without chunked encoding, it can speed up parsing and delivery as the plugin will not buffer up data.
    So, you could write this:
    Code (CSharp):
    1. var eventSource = new EventSource(new Uri("somewebsite/tags/tag"), 1);
    2. #if !UNITY_WEBGL || UNITY_EDITOR
    3. eventSource.InternalRequest.AddHeader("X-Api-Key", "some_key");
    4. #endif
     
  30. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @umityayla

    You can, but you have to specify the ack callback before the objects you want to send:
    Code (CSharp):
    1. // Send an event and define a callback function that will be called as an
    2. // acknowledgement of this event
    3. manager.Socket.Emit("custom event", OnAckCallback, "param 1", "param 2");
    4.  
    5. void OnAckCallback(Socket socket, Packet originalPacket, params object[] args)
    6. {
    7.     Debug.Log("OnAckCallback!");
    8. }
     
  31. umityayla

    umityayla

    Joined:
    May 21, 2019
    Posts:
    26
    @BestHTTP , thanks for replying;

    Well, the problem is this is async, we want to do this synchronised so we can catch errors/do something if we don't receive an ack. Because we are somewhat doing CRUD application with Websockets. So, acknowledgments are crucial and our architecture prevents us from extracting the code block from the body to method, can you tell me how can we keep the code block inside?

    Our current framework is this. And has default access to Actions, framework's base method;

    Code (CSharp):
    1. public void Emit(string ev, JSONObject data, Action<JSONObject> action)
    2.         {
    3.             EmitMessage(++packetId, string.Format("[\"{0}\",{1}]", ev, data));
    4.             ackList.Add(new Ack(packetId, action));
    5.         }
     
  32. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @umityayla

    Well, I'm sorry that I have to inform you, but that package is not synchronous either. But, if you want to make it feel the same, you can do it with my plugin too:

    Code (CSharp):
    1. manager.Socket.Emit("custom event", (Socket socket, Packet originalPacket, object[] args) =>
    2. {
    3.     Debug.Log("OnAckCallback!");
    4. }, "param 1", "param 2");
     
  33. pzy2017

    pzy2017

    Joined:
    May 9, 2019
    Posts:
    8
    @BestHTTP

    Perfect! Thanks again!!
     
  34. iPedro

    iPedro

    Joined:
    Dec 28, 2011
    Posts:
    14
    @BestHTTP

    Thank you so much for posting all this and the due diligence! Everything worked great!
     
  35. umityayla

    umityayla

    Joined:
    May 21, 2019
    Posts:
    26
    Thank you!
     
  36. SamuelGoldenbaum

    SamuelGoldenbaum

    Joined:
    May 21, 2017
    Posts:
    47
    Apologies if the topic has been covered, but it's too time-consuming paging through 63 pages and not getting relevant results from search.

    I am using the MessagePack encoding as suggested and having issues decoding generic lists on the client.

    Simple classes like the following are decoding correctly in the SignalR Core API, but Unity is throwing when receiving and decoding the same objects.

    Code (CSharp):
    1.  
    2. [Serializable]
    3. public class Move {
    4.     public int From;
    5.     public int To;
    6. }
    7.  
    8. [Serializable]
    9. public class Turn {
    10.     public string Player;
    11.     public List<Move> Moves;
    12. }
    I get the following error:
    Code (CSharp):
    1. [637177361995897220] Ex [WebSocketTransport]: OnMessage(byte[]) - Message: 1: Unexpected token readed 'StringLiteral' while 'BeginArray' is expected.   at GameDevWare.Serialization.Serializers.ArraySerializer.Deserialize (GameDevWare.Serialization.IJsonReader reader) [0x00041] in /Users/samuelgoldenbaum/Documents/gammon/BestHTTP Demo/Assets/Plugins/GameDevWare.Serialization/Serializers/ArraySerializer.cs:58
    API is set to use MessagePack and decodes correctly at API
    Code (CSharp):
    1. services.AddSignalR()
    2.                 .AddMessagePackProtocol();
    Unity Version 2019.2.12f1
    BestHTPP2 2.0.4
    Json & MessagePack 2.4.1

    Demo Projects here:

    API
    https://github.com/samuelgoldenbaum/BestHTTPDemoAPI

    Unity
    https://github.com/samuelgoldenbaum/BestHTTPDemo
     
  37. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @SamuelGoldenbaum

    Thanks for the repro-projects, just cloned them, i'm going to try out later this day. Could you delete or make private the BestHTTPDemo project? It contains the whole plugin and right now anyone could clone it for a free copy.
     
    SamuelGoldenbaum likes this.
  38. SamuelGoldenbaum

    SamuelGoldenbaum

    Joined:
    May 21, 2017
    Posts:
    47
    Apologies - shared those repos pretty late last night. I have updated access and added you as a collaborator.

    PS - I am also having issues encoding/decoding enums in that demo project and commented the property out for now:


    Code (CSharp):
    1. [Serializable]
    2. public enum TurnType {
    3.     Manual,
    4.     Automatic
    5. }
    6.  
    7. [Serializable]
    8. public class Turn {
    9.     public string Player;
    10.  
    11.     // public TurnType TurnType; // adding this results in API never being hit and no errors thrown
    12.     public List<Move> Moves;
    13. }
     
  39. SamuelGoldenbaum

    SamuelGoldenbaum

    Joined:
    May 21, 2017
    Posts:
    47
    After some debugging, it seems the issue is triggered when the API is sending the Send method name from the OnConnectedAsync event at the API and there is no handler for it on the client.

    Code (CSharp):
    1. public override async Task OnConnectedAsync () {
    2.             await Clients.All.SendAsync ("Send", $"{Context.ConnectionId} joined");
    3.         }
    For some reason, the client is trying to apply an array deserializer to the string literal parameter here. It's not actually the generic List<T> causing the issue which is really strange.

    If I add a handler for the Send event on the client, it works fine.
    Code (CSharp):
    1. hub.On("Send", (string arg) => {
    2.             Debug.Log($"On '<color=green>Send</color>': '<color=yellow>{arg}</color>'");
    3.         });
     
    Last edited: Feb 20, 2020
  40. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @SamuelGoldenbaum

    Send triggered an error because when the arguments are unknown it just tried to read the whole parameter array in one way but it's already called NextToken in preparation of the best case scenario. Fixed this issue, if you want i can send a bew package.

    The second issue is that "Test" callback isn't called, or you don't get any feedback about the "Test" invoke. If you add an error handler for you invoke call:
    Code (CSharp):
    1. hub.Invoke<Turn> ("Test", turn)
    2.     .OnError(Debug.LogException);
    3.  
    4. // or wrap your InvokeAsync with a try-catch block:
    5. try
    6. {
    7.     await hub.InvokeAsync<Turn>("Test", turn);
    8. }
    9. catch(Exception ex)
    10. {
    11.     Debug.LogException(ex);
    12. }
    In your case it's going to log out the next line:
    I'm not sure about this error, but it seems that if the server's version of Turn TurnType is int, the invocation succeeds:
    Code (CSharp):
    1. [Serializable]
    2. public class Turn {
    3.     public string Player;
    4.  
    5.     public int TurnType; // adding this results in API never being hit and bo errors thrown
    6.     public List<Move> Moves;
    7. }
    It could be how the client's and server's messagepack encoder/decoder disagree on how an enum should be encoded/decoded. The specification says that a custom enum should be encoded a messagepack fixint/int, and the GameDevWare's serializer encoding it the right way.
     
    Last edited: Feb 20, 2020
  41. SamuelGoldenbaum

    SamuelGoldenbaum

    Joined:
    May 21, 2017
    Posts:
    47
    Unable to Decode DateTime or DateTimeOffset objects using MessagePack with SignalR Core API.

    It does work correctly when using a simple C# Unit Test Project, but not Unity and BestHTPP2.

    Exception at the server when using BestHTPP2 and Message Pack:
    Code (CSharp):
    1. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[22]
    2.       Parameters to hub method 'Test' are incorrect.
    3. System.IO.InvalidDataException: Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.
    4. ---> System.IO.InvalidDataException: Deserializing object of the `Turn` type for 'argument' failed.
    5. ---> System.InvalidOperationException: code is invalid. code:216 format:fixext 16
    6.    at MessagePack.Decoders.InvalidArrayHeader.Read(Byte[] bytes, Int32 offset, Int32& readSize)
    7.    at MessagePack.Formatters.DateTimeOffsetFormatter.Deserialize(Byte[] bytes, Int32 offset, IFormatterResolver formatterResolver, Int32& readSize)
    8.    at MessagePack.Formatters.BestHTTPDemoAPI_TurnFormatter1.Deserialize(Byte[] , Int32 , IFormatterResolver , Int32& )
    9.    at MessagePack.MessagePackSerializer.Deserialize[T](ArraySegment`1 bytes, IFormatterResolver resolver)
    10.    at lambda_method(Closure , ArraySegment`1 , IFormatterResolver )
    11.    at MessagePack.MessagePackSerializer.NonGeneric.Deserialize(Type type, ArraySegment`1 bytes, IFormatterResolver resolver)
    12.    at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocol.DeserializeObject(Byte[] input, Int32& offset, Type type, String field, IFormatterResolver resolver)
    13.    --- End of inner exception stack trace ---
    14.    at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocol.DeserializeObject(Byte[] input, Int32& offset, Type type, String field, IFormatterResolver resolver)
    15.    at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocol.BindArguments(Byte[] input, Int32& offset, IReadOnlyList`1 parameterTypes, IFormatterResolver resolver)
    16.    --- End of inner exception stack trace ---
    17.    at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocol.BindArguments(Byte[] input, Int32& offset, IReadOnlyList`1 parameterTypes, IFormatterResolver resolver)
    18.    at Microsoft.AspNetCore.SignalR.Protocol.MessagePackHubProtocol.CreateInvocationMessage(Byte[] input, Int32& offset, IInvocationBinder binder, IFormatterResolver resolver, Int32 itemCount)
    19. dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[14]
    20.  

    Signal R Core API
    Code (CSharp):
    1.  
    2.     [Serializable]
    3.     [MessagePackObject(keyAsPropertyName: true)]
    4.     public class Turn {
    5.         public DateTimeOffset Time;
    6.     }
    7.  
    8.     public class GameHub : Hub {
    9.        public async Task Test (Turn turn) {
    10.           await Clients.Client (Context.ConnectionId).SendAsync ("Test", turn);
    11.        }
    12.    }
    13.  
    14. //...
    15.  
    16. public void ConfigureServices (IServiceCollection services) {
    17.     services.AddSignalR ()
    18.         .AddMessagePackProtocol (options => {
    19.             options.FormatterResolvers = new List<IFormatterResolver> {
    20.                 StandardResolver.Instance
    21.             };
    22.         });
    23. }
    24.  
    Unity Client (Causes Server to throw Exception):
    Code (CSharp):
    1.  
    2.        var hub = new HubConnection (new Uri ("https://localhost:5001/gameHub"), new MessagePackProtocol ());
    3.  
    4.         var turn = new Turn {
    5.             Time = DateTimeOffset.UtcNow
    6.         };
    7.  
    8.         hub.Invoke<Turn> ("Test", turn);
    9.  
    Test Fixture Class (Works Fine)
    Code (CSharp):
    1.   [
    2.     Serializable]
    3.     public class Turn {
    4.         public DateTimeOffset Time;
    5.     }
    6.  
    7.     [TestFixture]
    8.     public class Tests {
    9.         [Test]
    10.         public async Task Test1 () {
    11.             var connection = new HubConnectionBuilder()
    12.                 .WithUrl("https://localhost:5001/gameHub")
    13.                 .AddMessagePackProtocol()
    14.                 .Build();
    15.          
    16.             await connection.StartAsync ();
    17.  
    18.             var turn = new Turn {
    19.                 Time = DateTimeOffset.UtcNow
    20.             };
    21.  
    22.             connection.InvokeAsync<Turn> ("Test", turn);
    23.          
    24.             Assert.True (true);
    25.         }
    26.     }
     
    Last edited: Feb 20, 2020
  42. SamuelGoldenbaum

    SamuelGoldenbaum

    Joined:
    May 21, 2017
    Posts:
    47
    I fixed the enum decoding by specifying the StandardResolver FormatterResolver explicitly in Signal R as below. It should need the DynamicEnumAsStringResolver too but it seems OK without it when using BestHTTP2 and MessagePack.
    Code (CSharp):
    1.  
    2. public void ConfigureServices (IServiceCollection services) {
    3.             services.AddSignalR ()
    4.                 .AddMessagePackProtocol (options => {
    5.                     options.FormatterResolvers = new List<IFormatterResolver> {
    6.                         StandardResolver.Instance
    7.                     };
    8.                 });
    9.         }
    I suggest you update the docs and examples with more realistic data including enums, dates etc with the relevant configuration needed.
     
    Last edited: Feb 20, 2020
  43. sofianehamza

    sofianehamza

    Joined:
    Feb 13, 2017
    Posts:
    19
    I have bought this asset and in less than one year it got deprecated... I don't think this is fair...
    actually I have not even used it yet since I was trying to learn how to create playmaker actions for it when I have time
     
  44. SamuelGoldenbaum

    SamuelGoldenbaum

    Joined:
    May 21, 2017
    Posts:
    47
    kevdome3000 likes this.
  45. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @SamuelGoldenbaum

    You could try to use MessagePackTimestamp instead:
    Code (CSharp):
    1. [Serializable]
    2. public class Turn {
    3.     public string Player;
    4.  
    5.     public TurnType TurnType; // adding this results in API never being hit and no errors thrown
    6.     public List<Move> Moves;
    7.  
    8.     public MessagePackTimestamp Time;
    9. }
    10.  
    11. DateTime now = DateTime.UtcNow;
    12. var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    13.  
    14. var turn = new Turn {
    15.     Time = new MessagePackTimestamp((long)(now.ToUniversalTime() - epoch).TotalSeconds, 0),
    16. };
    17.  
    I looked for neuecc's MessagePack implementation, and while its api wasn't as straightforward as GameDevWare's that's something i can get used to, but setting it up for unity and AOT seems quite a bit frigthening. I had an almost working version, but decided to go with one that easier to set up for the users.

    But, my messagepack protocol is just one possible implementation, the protocol's interface is public and anyone who feels so can write a new one. Usually I'm also available as a contractor to extend the plugin for special needs.

    Edit: shouldn't your text fixture await on InvokeAsync?
     
    Last edited: Feb 21, 2020
  46. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @sofianehamza

    You can still use the old version. The new version got almost a year worth of development and has some a few non-backward compatible changes. Deprecating the old one became inevitable.
     
  47. unity1_unity125

    unity1_unity125

    Joined:
    Nov 13, 2018
    Posts:
    2
    Hello, I am facing a weird issue while net connection losses. While disconnecting the internet, Disconnecting callback is not calling as well as reconnecting attempt, reconnecting and reconnected also. So my code in this callbacks is not executing at that time so my game misbehaves from that point. It is not happening every time. It is occurring any time and it is bothering me. When it occurred, it continues with every time when I am disconnecting. When I restart the game then it will work fine again and all callback will be called in a proper manner.

    So can anybody help me with that issue ??

    Thanks in advance !!
     
  48. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @unity1_unity125

    TCP can detect that connection between two nodes are no longer available when it tries to transmit packets. If the client doesn't send data, connection lost can remain undetected. For this scenario, higher level protocols usually send out ping requests to test the connection. But, as everything it has a price, the more frequent these pings are the more overhead it generates. If these pings are less frequent, than connection problems are detected later.
    If you feel that this isn't your case, you can set the plugin's log level to All and send the logs to me so i can see what's going on.
     
  49. wicea

    wicea

    Joined:
    Jan 23, 2016
    Posts:
    18
    @BestHTTP

    Hi! Is it safe to access request.RawData after request finished? Does it store relevant original data that I passed before sending request?

    I detected strange situation, that after receiving request error req.RawData contains data from another requests, that are running in parallel.

    For Example.

    requestPostOne.Send();
    requestPostTwo.Send();
    requestPostThree.Send();

    ....

    If requestPostOne will fail then "Encoding.UTF8.GetString(requestPostOne.RawData);" can output some symbols from another requests - like headers, uri, encoding, request details etc.
     
  50. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @wicea

    This bug should be fixed in the latest version of the plugin (v2.0.4), so yes, you should be safe to keep a reference and use it later.