Search Unity

Best HTTP Released

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

  1. BestHTTP

    BestHTTP

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

    There are more possible solutions:
    1.) You can edit the \Best HTTP (Pro)\BestHTTP\JSON\JSON.cs around line 237. Don't know what number is it getting, how should it be converted. Maybe you could log out the codePoint variable and send it to me.

    2.) You can use a more advanced json parser. You can try out JSON .NET For Unity. You can create an encoder for it:
    Code (CSharp):
    1. public sealed class JsonDotNetEncoder : IJsonEncoder
    2. {
    3.     public List<object> Decode(string json)
    4.     {
    5.         return JsonConvert.DeserializeObject<List<object>>(json);
    6.     }
    7.  
    8.     public string Encode(List<object> obj)
    9.     {
    10.         return JsonConvert.SerializeObject(obj);
    11.     }
    12. }
    Then set it your socket.io manager:
    Code (CSharp):
    1. Manager = new SocketManager(new Uri("http://myserver/socket.io/"), options);
    2. Manager.Encoder = new JsonDotNetEncoder();
     
    cong91 likes this.
  2. BestHTTP

    BestHTTP

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

    There's no tutorials, but it's really easy to 'convert' from cURL to BestHTTP.
    -X switch describes the type of the request (POST, GET, etc). You can specify it thought the HTTPRequest constructor.

    A '-X POST' would be:
    Code (CSharp):
    1. var request = new HTTPRequest(uri, HTTPMethods.Post, callback);
    -H switches are custom headers, you can add them with the AddHeader function. An '-H "Content-Type: application/json"' would be:
    Code (CSharp):
    1. request.AddHeader("Content-Type", "application/json");
    -d switches are the data. It can be something like '-d '{ "username":"asdf", "password":"123" }'' '
    The corresponding BestHTTP code would be:
    Code (CSharp):
    1. string json = @"{""username"":""asdf"",""password"":""123"" }";
    2. request.RawData = Encoding.UTF8.GetBytes(json);
    if you see the -d switch in the form of '-d "key=value"' then you can use the AddField function:
    Code (CSharp):
    1. request.AddField("key", "value");
     
  3. BestHTTP

    BestHTTP

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

    But you can send the cURL request to my mail address(besthttp@gmail.com) any time, I will help in the translating process!
     
  4. fvde

    fvde

    Joined:
    May 15, 2014
    Posts:
    19
    Hey BestHttp, just bought and started using your library for Server Sent Events, looks promising so far! :)

    Connecting to my web server (which uses spring boot SSE) worked on first try. However, I don't receive any messages at all. Here's the code:
    Code (CSharp):
    1. public void RegisterForServerSentEventsForGame(int game)
    2.         {
    3.             if (EventSource != null && (EventSource.State == States.Open || EventSource.State == States.Connecting))
    4.             {
    5.                 return;
    6.             }
    7.  
    8.             EventSource = new EventSource(new Uri(Configuration.API_URL + "/messages/" + game));
    9.             EventSource.InternalRequest.SetHeader(TOKEN, token);
    10.             EventSource.Open();
    11.             EventSource.OnOpen += OnregisteredForSSE;
    12.             EventSource.OnMessage += OnReceivedMessage;
    13.             EventSource.On("Message", MessageReceived);
    14.         }
    15.  
    16.         void MessageReceived(EventSource source, Message msg)
    17.         {
    18.             Debug.Log(msg.Data);
    19.         }
    20.  
    21.         private void OnReceivedMessage(EventSource eventSource, Message message)
    22.         {
    23.             Debug.Log("Received message " + message);
    24.         }
    And attached is a screenshot of how the events look like when consuming them with curl.

    What am I doing wrong?


     

    Attached Files:

  5. fvde

    fvde

    Joined:
    May 15, 2014
    Posts:
    19
    NVM, pretty sure I got it. Looks like my server is not correctly sending the "content-type", "text/event-stream" header in its events. Curl doesn't seem to care about that. Will confirm once resolved. :)

    Edit: Hmm - still not receiving it :/
     
    Last edited: Aug 9, 2017
  6. luniac

    luniac

    Joined:
    Jan 12, 2011
    Posts:
    614
    Thanks for tryina explain, i just saw cURL on airtable API and was wondering if its possible to interact with it from this plugin.

    thanks again.
     
  7. Snul1

    Snul1

    Joined:
    Jun 14, 2017
    Posts:
    5
    Hi, I got an old version of BestHttp Pro for my WebSocket client and tested it to see if it works.
    Everything looked fine except that only OnError event got called when remote server closed the connection. As for me, I prefer to get this event handled in OnClosed rather than OnError and receive error code or enum indicating what error it is instead of just an error string.
    Besides, suspending a period of time results in the "connection timed out", which is logged out in OnError event when I attempt to use Send(). Also, OnClosed is not called.
    I want to know if the latest version of BestHttp Pro fixed or can offer solutions to the problem mentioned above. If so, I will very appreciate your help and be glad to buy the latest for use.
     
  8. BestHTTP

    BestHTTP

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

    Created a little SSE server in node.js:
    Code (CSharp):
    1. var app = require('http').createServer(handler)
    2.  
    3. app.listen(3000);
    4.  
    5. function handler(req, res) {
    6.  
    7.     res.setHeader("Content-Type", "text/event-stream");
    8.     res.writeHead(200);  
    9.  
    10.     res.write("id:9456321-654321321-654sdf-564asdf654\n");
    11.     res.write("event:Message\n");
    12.     res.write("data:A new day dawns...\n");
    13.     res.write("\n");
    14.  
    15.     res.write("id:9456321-654321321-654sdf-51236548654\n");
    16.     res.write("event:Message\n");
    17.     res.write("data:A new day dawns...\n");
    18.     res.write("\n");
    19.  
    20.     res.write("id:9456321-654321321-654sdf-asdf234asdf654\n");
    21.     res.write("event:Message\n");
    22.     res.write("data:A new day dawns...\n");
    23.     res.write("\n");
    24. }
    On client side i used the following code:
    Code (CSharp):
    1.  
    2. EventSource evSource = new EventSource(new Uri("http://localhost:3000/"));
    3. evSource.OnMessage +=
    4.             (ev, msg)
    5.                 => Debug.Log(string.Format("OnMessage - ({0}) [{1}]: {2}", msg.Id, msg.Event, msg.Data));
    6.  
    7. evSource.On("Message",
    8.             (ev, msg)
    9.                 => Debug.Log(string.Format("({0}) [{1}]: {2}", msg.Id, msg.Event, msg.Data)));
    10. evSource.Open();
    11.  
    And it logged out this (plugin's logging set to verbose):
    upload_2017-8-12_11-35-5.png

    And here's what my proxy captured:
    upload_2017-8-12_11-35-21.png
     
    fvde likes this.
  9. BestHTTP

    BestHTTP

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

    There were a fix for an issue like this in v1.10.1.
    You should be able to update the plugin through Unity's Asset Store window for free.
     
  10. PizzaProgram

    PizzaProgram

    Joined:
    Feb 12, 2014
    Posts:
    17
    Dear Support!
    I've bought BestHTTP PRO to handle timeouts and get http-requests without blocking the main Thread (UI).
    I had a code like this: (shortened a bit)

    Code (CSharp):
    1. if ( ! wwwGET( url , ref downloadedString)) return false;
    2.    /* ... do something with downloadedString  */
    3.    ...
    4.  
    5. public static bool wwwGET(string str_url, ref string downloadedString)
    6. {
    7.     bool success = false;
    8.     if (str_url != "")
    9.         var client = new System.Net.WebClient();
    10.         try {
    11.             downloadedString = client.DownloadString(str_url);
    12.             success = true;
    13.         }
    14.         catch { /*...*/       }
    15. a        client.Dispose();
    16.     }
    17.     return success;
    18. }
    _____________________________

    Since I've switched to HTTPRequest >> it jumps to the end of code immediately >> so it's always returning FALSE, and finishing download later. (Or dropping a timeout). I understand why it is happening, my question is:
    - how to WAIT for it until it's finished, and returning "success" after?

    I guess it has something to do with yield > but I can not insert a yield line without loosing the possibilities to use out/ref and give back a bool as result too.

    Searched through this forum 2 days long, red the BestHTTPDocumentation.pfd 3 times, but still don't know how to do that. No FULL example found.


    Also tried MEC, because it seems to me more logical than unity's built-in coroutines, but the comiler could not convert...
    Code (CSharp):
    1. yield return Timing.WaitUntilDone(Timing.RunCoroutine( request );
    Sorry for my low knowledge, thanks for any help!
    (I'm a Delphi programmer with 25 years of experience, so it's hard for me to understand this whole lambda/coroutine/running_every_frame... concept. Sorry.)
     
  11. BestHTTP

    BestHTTP

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

    Doing networking in blocking mode is a very, very bad idea.
    This is why the plugin uses callbacks. You can place your code that normally would be called after wwwGet in that callback, or pass a callback to it that will be called when the download finished:

    Code (CSharp):
    1.  
    2. public static bool wwwGET(string str_url, Action<string> onSuccess) {
    3.   HTTPRequest request = new HTTPRequest(new Uri(str_url), (req, resp) => {
    4.     // This is the callback the plugin will call when the download completes or when there's any error!, so we have to check the request's State:
    5.     switch (req.State) {
    6.       case HTTPRequestStates.Finished:
    7.         // we can call the originial callback:
    8.         onSuccess(resp.DataAsText);
    9.         break;
    10.  
    11.       default:
    12.          // There was an error!
    13.          break;
    14.     }
    15.   };
    16.  
    17.   request.Send();
    18. }
    19.  
    And you would use it something like this:
    Code (CSharp):
    1.  
    2. wwwGET(url, (downloadedString) => {
    3.   /* ... do something with downloadedString  */
    4.   ...
    5. });
    6.  
     
    fvde and PizzaProgram like this.
  12. PizzaProgram

    PizzaProgram

    Joined:
    Feb 12, 2014
    Posts:
    17

    Dear Support!

    Thank you very much for the detailed example. The idea calling a lambda from a lambda procedure was new to me, but logical at first site. So I've rewrote the calling part to that concept, and had to realized:
    - it just "delayed" the problem.
    Because:
    - sooner or later I have to deal with multiple parameters and result kinds, but Lambdas do not allow OUT or REF.
    (I've simplified my code to an "1 in, 2 out" parameter list ... but you dismissed the bool wwwGET already. In final code I have multiple result kinds, not just downloadedString! )
    - at the end I still have to wait somewhere to process the results and move on to the next stage.

    So the original question stays:
    - How do I WAIT in a non-blocking mode until a download is finished?
     
  13. BestHTTP

    BestHTTP

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

    It's not delayed the problem, instead removed one (you are not blocking the main thread), and your code/logic continues when there's data that can be processed.

    You might have to modify more code to work nice along this non-blocking logic, but in the end it will worth it.

    "- at the end I still have to wait somewhere to process the results and move on to the next stage."
    No, that code that processes the downloaded data should be called when there's downloaded data.

    If you block the main thread
    1.) it will produce warnings and/or application will be closed by the system on Android/iOS
    2.) Your user has no chance to change its mind and cancel the download as the UI is blocked from any interaction.
    3.) Users has different network conditions and throughput, so even if you try to set a reasonable timeout for the blocking requests that will work for some users, and it will just time out for others.

    But, because i'm not that man who doesn't let you shoot in your own leg, here's a class that can help send out requests in a blocking mode:
    Code (CSharp):
    1. /// <summary>
    2. /// Helper class to be able send a blocking HTTPRequest
    3. /// </summary>
    4. public sealed class BlockingRequestHelper
    5. {
    6.     #region Private Fields
    7.  
    8.     /// <summary>
    9.     /// AutResetEvent to wait for the request
    10.     /// </summary>
    11.     AutoResetEvent mre = new AutoResetEvent(false);
    12.  
    13.     /// <summary>
    14.     /// AutResetEvent to pause the thread
    15.     /// </summary>
    16.     AutoResetEvent threadPing = new AutoResetEvent(false);
    17.  
    18.     /// <summary>
    19.     /// Thread to call HTTPManager.OnUpdate
    20.     /// </summary>
    21.     Thread thread;
    22.  
    23.     /// <summary>
    24.     /// While true, HTTPManager.OnUpdate will be called
    25.     /// </summary>
    26.     bool doUpdate;
    27.  
    28.     /// <summary>
    29.     /// While true, the thread will not quit
    30.     /// </summary>
    31.     bool threadAlive;
    32.  
    33.     /// <summary>
    34.     /// To save the originally set callback
    35.     /// </summary>
    36.     OnRequestFinishedDelegate originalCallback;
    37.  
    38.     #endregion
    39.  
    40.     #region Public Functions
    41.  
    42.     /// <summary>
    43.     /// This will send the request and will block until it's finished
    44.     /// </summary>
    45.     public bool SendAndWait(HTTPRequest request, TimeSpan timeout)
    46.     {
    47.         HTTPManager.Logger.Information("BlockingRequest", "Sending Request...");
    48.  
    49.         // This must be called on Unity's main thread
    50.         HTTPManager.Setup();
    51.  
    52.         // Save the callback
    53.         originalCallback = request.Callback;
    54.  
    55.         // Set a new callback, where we will ping the ManualResetEvent
    56.         request.Callback = (req, resp) => mre.Set();
    57.  
    58.         // Send the request
    59.         request.Send();
    60.  
    61.         // Start or resume our thread to do OnUpdates
    62.         StartThread();
    63.  
    64.         // Block until the request's hijacked callback is called and will set the ManualResetEvent
    65.         bool result = mre.WaitOne(timeout);
    66.  
    67.         // Pause the thread
    68.         PauseThread();
    69.  
    70.         // Call the originally set callback on the main thread
    71.         if (originalCallback != null)
    72.         {
    73.             try
    74.             {
    75.                 originalCallback(request, request.Response);
    76.             }
    77.             catch(Exception ex)
    78.             {
    79.                 HTTPManager.Logger.Exception("BlockingRequest", "calling originalCallback", ex);
    80.             }
    81.         }
    82.  
    83.         HTTPManager.Logger.Information("BlockingRequest", "Request Done!");
    84.  
    85.         return result;
    86.     }
    87.  
    88.     /// <summary>
    89.     /// This will stop the started thread. Call it when no more SendAndWait will be called
    90.     /// </summary>
    91.     public void StopThread()
    92.     {
    93.         if (thread != null)
    94.         {
    95.             HTTPManager.Logger.Information("BlockingRequest", "Stopping Thread");
    96.  
    97.             thread = null;
    98.             doUpdate = false;
    99.             threadAlive = false;
    100.             threadPing.Set();
    101.         }
    102.     }
    103.  
    104.     #endregion
    105.  
    106.     #region Private Helper Functions
    107.  
    108.     void StartThread()
    109.     {
    110.         // Start a thread
    111.         if (thread == null)
    112.         {
    113.             HTTPManager.Logger.Information("BlockingRequest", "Starting new Thread");
    114.  
    115.             thread = new Thread(new ThreadStart(ThreadFunc));
    116.             doUpdate = true;
    117.             threadAlive = true;
    118.             thread.Start();
    119.         }
    120.         else
    121.         {
    122.             // Resume updating
    123.             HTTPManager.Logger.Information("BlockingRequest", "Resuming Thread");
    124.  
    125.             doUpdate = true;
    126.             threadPing.Set();
    127.         }
    128.     }
    129.  
    130.     void PauseThread()
    131.     {
    132.         doUpdate = false;
    133.     }
    134.  
    135.     void ThreadFunc()
    136.     {
    137.         HTTPManager.Logger.Information("BlockingRequest", "Thread Started");
    138.  
    139.         while (threadAlive)
    140.         {
    141.             while (doUpdate)
    142.             {
    143.                 // Don't eat all cpu cycles
    144.                 Thread.Sleep(100);
    145.  
    146.                 HTTPManager.OnUpdate();
    147.             }
    148.  
    149.             HTTPManager.Logger.Information("BlockingRequest", "Thread Paused");
    150.  
    151.             threadPing.WaitOne();
    152.  
    153.             HTTPManager.Logger.Information("BlockingRequest", "Thread Resumed");
    154.         }
    155.  
    156.         HTTPManager.Logger.Information("BlockingRequest", "Thread Quit");
    157.     }
    158.  
    159.     #endregion
    160. }
    You can use it like this:
    Code (CSharp):
    1. BlockingRequestHelper blkRquest = new BlockingRequestHelper();
    2.  
    3. BlockingRequestHelper blkRequest = new BlockingRequestHelper();
    4.  
    5. var request = new HTTPRequest(new Uri("https://httpbin.org"), (req, resp) =>
    6. {
    7.     switch (req.State)
    8.     {
    9.         // The request finished without any problem.
    10.         case HTTPRequestStates.Finished:
    11.             if (resp.IsSuccess)
    12.             {
    13.                 // TODO: Process response's data
    14.                 Debug.Log("Request Finished Successfully! Data length: " + resp.Data.Length + " " + resp.DataAsText);
    15.             }
    16.             else
    17.                 Debug.LogWarning(string.Format("Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
    18.                                                 resp.StatusCode,
    19.                                                 resp.Message,
    20.                                                 resp.DataAsText));
    21.             break;
    22.  
    23.         // The request finished with an unexpected error. The request's Exception property may contain more info about the error.
    24.         case HTTPRequestStates.Error:
    25.             Debug.LogWarning("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
    26.             break;
    27.  
    28.         // The request aborted, initiated by the user.
    29.         case HTTPRequestStates.Aborted:
    30.             Debug.LogWarning("Request Aborted!");
    31.             break;
    32.  
    33.         // Connecting to the server is timed out.
    34.         case HTTPRequestStates.ConnectionTimedOut:
    35.             Debug.LogError("Connection Timed Out!");
    36.             break;
    37.  
    38.         // The request didn't finished in the given time.
    39.         case HTTPRequestStates.TimedOut:
    40.             Debug.LogError("Processing the request Timed Out!");
    41.             break;
    42.     }
    43. });
    44.  
    45. // Instead of request.Send()
    46. blkRequest.SendAndWait(request, TimeSpan.FromSeconds(10));
    47.  
    48. // If no more request is made through blkRquest
    49. blkRequest.StopThread();
     
  14. PizzaProgram

    PizzaProgram

    Joined:
    Feb 12, 2014
    Posts:
    17
    Dear Support!

    I think you have misunderstood me a little. I do NOT want to block the main tread.
    (As I've already wrote many times earlier.)
    I was using most of the System.Net.WebClient(); downloads until now in a secondary thread!
    (Yes, with thread-safe variables.)
    But sometimes it had been easier to do some "direct-downloads" = visible and cancellable for users. That's why I've started to experience with this.

    The more I tried to solve this "my way" the more I had to realize: it is IMPOSSIBLE to "wait" in a "non-blocking" mode started from main thread.
    So the only way for me is to rewrite the WHOLE program logic.
    (Probably will take me several month, and will double my code. It's an extremely complex app developing since 3 years.) :-(

    I really appreciate ALL the help you gave me, thank You VERY very much!

    PS: one last question... is it possible to use BestHTTPd in a secondary Thread()?
    I mean: will it stay and run in that other new thread, or the callbacks are bound to the main (GUI) thread?
    (I like the timeout functions very much and would like to replace the dumb WebClient with your component.)
     
    Last edited: Aug 18, 2017
  15. BestHTTP

    BestHTTP

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

    Sorry for the misunderstanding.

    By default callbacks are dispatched from the Unity main thread, but disabling the HTTPUpdateDelegator and calling the HTTPManager.OnUpdate(); manually you can switch to any thread.
    But this will do just the callbacks stuff, processing the actual request is done on distinct threads.
     
  16. Loofah_Bu

    Loofah_Bu

    Joined:
    Sep 5, 2014
    Posts:
    2
    Hi,

    I used www and bestHTTP to download multiple texture.

    But I found bestHTTP is slower than www 10 times on mobile.

    So I traced code and saw many function be locked for the thread-safe.

    Even through those download tasks are parallel, we can't do it completely parallel.

    Is there any way to solve this problem? Thanks!
     
    Last edited: Aug 22, 2017
  17. haswoodmagicbean

    haswoodmagicbean

    Joined:
    Mar 13, 2015
    Posts:
    5
    Hi, I'm having some problems when using socket.io

    the timeout event is not firing.
    from docs :
    Timeout: Connection timeout before a "connect_error" and "connect_timeout" events are emitted.
    but when I tried, nothing got called.

    and then I tried to register all predefined events at docs, only "connect", "disconnect", "error", "event" events got fired.

    I'm using the latest BestHTTP package at asset store right now and using unity 5.6.2p4.

    here is my code. thank you
    Code (CSharp):
    1. SocketOptions options = new SocketOptions();
    2.         options.AutoConnect = true;
    3.         options.ReconnectionAttempts = 2;
    4.         options.Reconnection = true;
    5.         options.Timeout = TimeSpan.FromMilliseconds(5000);
    6.  
    7.         manager = new SocketManager(new Uri(connectionString), options);
    8.  
    9.         mSocket = manager.GetSocket(socketNamespace);
    10.  
    11.         mSocket.On(SocketIOEventTypes.Connect,onSocketConnected);
    12.         mSocket.On(SocketIOEventTypes.Disconnect,onSocketDisconnected);
    13.         mSocket.On(SocketIOEventTypes.Error, onSocketError);
    14.         mSocket.On(SocketIOEventTypes.Event,onSocketEvent);
    15.         mSocket.On("connecting",onSocketConnecting);
    16.         mSocket.On("reconnecting",onSocketReconnecting);
    17.         mSocket.On("reconnect_attempt", onSocketReconnectingAttempt);
    18.         mSocket.On("reconnect", onSocketReconnect);
    19.         mSocket.On("connect_timeout",onSocketConnectionTimeout);
    20.         mSocket.On("connect_error", onSocketConnectionTimeout);
    21.         mSocket.On("reconnect_failed",onSocketReconnectFailed);
     
  18. melefans

    melefans

    Joined:
    Aug 1, 2016
    Posts:
    36
    @BestHTTP
    Hi,
    Broken network conditions, how to force the closure of websocket。
     
  19. BestHTTP

    BestHTTP

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

    I'm curious locking would slow down so much. There's some locking for cache lookups when the download starts and when it ends, but in-between the actual download/upload is done without any thread synchronization mechanism.

    There's another lock in the HTTPManager's OnUpdate function to manage the connections and dispatch callbacks, but even a slow callback couldn't delay actual downloads on the other threads.

    In general however, the plugin will be slower than any native implementation. You can read some thoughts about it here. It's written against the WebGL/browser platform, but it's the same for any platform.
     
  20. BestHTTP

    BestHTTP

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

    Hmm, that looks like made some spelling mistakes. Timeout isn't an event, that's just an option in the SocketOptions class, as you already using it.

    When the plugin fails to establish the socket.io protocol with the server in the given time, it will emit an "error" with an Error object as its parameter, a "connect_error" and a "connect_timeout".
     
  21. BestHTTP

    BestHTTP

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

    You can access the InternalRequest object of the websocket instance, an call Abort on it. InternalRequest isn't available under WebGL though.
     
  22. haswoodmagicbean

    haswoodmagicbean

    Joined:
    Mar 13, 2015
    Posts:
    5
    Hmm, I don't get any error event emitted when connect error or connect timeout. as you can see, I already put event listener on "error" event from socket.io. I try it with disable and enable my wifi.
    can you give me example so that I can get event listener like disconnect, connecting, reconnecting, and reconnected to work? or I have to set auto reconnect to false, and do manager.open() manually because I only get event when disconnect and connected.
     
  23. BestHTTP

    BestHTTP

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

    That sequence will be fired when the manager is in Opening state. When already opened or the transport receives an error, it should only emit the error and will try to reconnect.
     
  24. haswoodmagicbean

    haswoodmagicbean

    Joined:
    Mar 13, 2015
    Posts:
    5
    I don't get any "error" emitted from "error" event on any of those state you said. can you please check the plugin?

    Edit : I just found out that event will only emitted to default namespace which is "/". I changed the SocketManager emitEvent to emitAll so all namespace sockets will get the event
     
    Last edited: Aug 29, 2017
  25. BestHTTP

    BestHTTP

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

    Used this code to subscribe:
    Code (CSharp):
    1. Manager.Socket.On(SocketIOEventTypes.Error, (socket, packet, args) => Debug.LogError(string.Format("Error: {0}", args[0].ToString())));
    It got called every time. There's one case however where these will not be delivered: when there's no default root namespace ('/') used.
     
  26. AG_Dan

    AG_Dan

    Joined:
    Jul 31, 2017
    Posts:
    6
    I'm writing some Unity editor utilities to help our designers edit configuration data for our game and then deploy it to our servers, and I ran into an issue when trying to use BestHTTP from editor scripts. HTTPUpdateDelegator.CheckInstance() fails due to an exception thrown when it calls GameObject.DontDestroyOnLoad() that is complaining about calling DontDestroyOnLoad outside of playback mode. Is this supposed to work in editor?

    I'm able to get things more-or-less working by pumping HTTPManager.OnUpdate() from my own code, and by changing the code in HTTPManager.Setup() to only call HTTPUpdateDelegator.CheckInstance() if Application.isPlaying is true, but this feels like a bit of a hack...
     
  27. BestHTTP

    BestHTTP

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

    What version of the plugin are you using? With the latest version of BestHTTP Pro you shouldn't receive that error because of DontDestroyOfLoad.

    On pumping OnUpdate() and CheckInstance(): while they are part of the plugin's public API for a reason, they should be called from the plugin even in the editor. HTTPUpdateDelegator should subscribe to the UnityEditor.EditorApplication.update event in its CheckInstance() function and call HTTPManager.OnUpdate() from there. CheckInstance() is called automatically when you send out a request.

    This code must work OOB with the latest version of the plugin:
    Code (CSharp):
    1. using System;
    2.  
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. using BestHTTP;
    7.  
    8. public class EditorTest : EditorWindow {
    9.     [MenuItem("Window/Editor Test")]
    10.     public static void Init() {
    11.         EditorWindow.GetWindow(typeof(EditorTest));
    12.     }
    13.  
    14.     void OnGUI() {
    15.         if (GUILayout.Button("Send Request"))
    16.             new HTTPRequest(new Uri("http://httpbin.org/get"), (req, resp) => Debug.Log(resp.DataAsText)).Send();
    17.     }
    18. }
     
    Last edited: Aug 30, 2017
  28. nrorn

    nrorn

    Joined:
    Sep 23, 2016
    Posts:
    7
    I am porting a project which utilizes BestHTTP Pro to Unity 2017.2.0(b8) .
    I am getting the following error:

    'Assets/Best HTTP (Pro)/BestHTTP/HTTPResponse.cs(149,25): error CS1061: Type `UnityEngine.Texture2D' does not contain a definition for `LoadImage' and no extension method `LoadImage' of type `UnityEngine.Texture2D' could be found. Are you missing `UnityEngine' using directive?'

    The problem seems to be with this code in the method DataAsTexture2D:
    ' texture = new UnityEngine.Texture2D(0, 0, UnityEngine.TextureFormat.ARGB32, false);
    texture.LoadImage(Data);'

    Any suggestion on how to fix?
     
  29. BestHTTP

    BestHTTP

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

    You should update the plugin from the Asset Store, this issue should be fixed already.
     
  30. roger-yoo

    roger-yoo

    Joined:
    Oct 26, 2014
    Posts:
    8
    When I try to connect to the node.js server using websocket,
    I get "Internal TLS error, this could not be an attack" error message.

    Error occurred in RecordStream :: ReadRecord.
    Here's the code at issue:
    byte [] recordHeader = TlsUtilities.ReadAllOrNothing (5, mInput);
    if (recordHeader == null)
    return false;


    recordHeader is null.

    node.js uses 6.11.2, and the websocket library is uWebSockets.
     
  31. melefans

    melefans

    Joined:
    Aug 1, 2016
    Posts:
    36
    thanks for your reply.
    Websocket how to get ping server latency ?
    StartPingThread = true , Will it reduce performance?
     
  32. cong91

    cong91

    Joined:
    May 31, 2013
    Posts:
    5
    Thanks for support.
    I have a question. My game is realtime use Socket.IO communicated between client and server.
    When game go background.
    After time for example 30s 40s. My game auto disconnect to server. But i want keep alive connection util game logout or time disconnect up to 3min or 5 min . I dont know why by Unity or Best HTTP Pro Plugin.
     
    Last edited: Sep 1, 2017
    Fatih86 likes this.
  33. BestHTTP

    BestHTTP

    Joined:
    Sep 11, 2013
    Posts:
    1,664
    @roger-yoo

    Unfortunately can't reproduce it. You cant try to switch the TLS handler by calling
    Code (CSharp):
    1. BestHTTP.HTTPManager.UseAlternateSSLDefaultValue = false;
     
  34. BestHTTP

    BestHTTP

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

    WebSocket pings will only keep the connection alive and to detect connection loss, latency isn't measured. It would be relatively easy to add though to the WebSocketResponse.cs
     
  35. BestHTTP

    BestHTTP

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

    The plugin doesn't use any native platform specific functionality, and mobile platforms tend to close raw TCP connections after some time when the application goes to the background.
    While it would be possible to add platform specific connection classes like I already did for WebGL, I don't have any code implemented to support this scenarios.
     
  36. Jribs

    Jribs

    Joined:
    Jun 10, 2014
    Posts:
    154
    Hi @BestHTTP

    I have a problem that I am hoping you have a solution for.

    In my project I download assetbundles various times from an AWS S3 bucket. I keep the bucket private so I first get a pre-signed URL from my server that is built with a token that the user has. The point is, pre-signed URL's will always be unique. This makes caching not possible which is an AWS problem, not yours.

    A pre-signed URL looks something like this
    Code (CSharp):
    1. var downloadUrl = "https://bucket.s3.amazonaws.com/filename?AWSAccessKeyId=MYACCESSKEY&Expires=111111&Signature=ALWAYS_UNIQUE"
    So in order to get around this I have been working on a little bit of custom caching. When I receive the pre-signed URL that tells me where to download the assetbundle from, I take the first part of the url which will always be the same for each.

    For example
    Code (CSharp):
    1. var cacheUrl = "https://bucket.s3.amazonaws.com/filename"
    Then once I have downloaded the data using the full url, I manually store the response in the cache using my shortened cacheUrl.

    Then on the next request I first check to see if the cache has an item with the cacheUrl, and then set that cacheUrl as my downloadUrl to download from the cache.

    It seems like something like this would be possible.

    I think my problem is that I am also streaming my downloads, so I'm not quite sure how to save that kind of response manually in the cache.

    Here is how I am attempting it
    Code (CSharp):
    1. private void StartDownloadQ(string fullUrl)
    2.     {
    3.         var baseUrl = fullUrl.Split('?');
    4.  
    5.         var cacheUrl = baseUrl[0];
    6.  
    7.         var hasCache = HTTPCacheService.GetEntity(new Uri(cacheUrl));
    8.  
    9.         string downloadUrl;
    10.  
    11.         if (hasCache == null)
    12.             downloadUrl = fullUrl;
    13.         else
    14.             downloadUrl = cacheUrl ;
    15.    
    16.         var fullData = new byte[0];
    17.  
    18.         var request = new HTTPRequest(new Uri(downloadUrl), (req, res) =>
    19.             {
    20.                 var fragments = res.GetStreamedFragments();
    21.                 if (fragments != null)
    22.                 {
    23.                     foreach (var data in fragments)
    24.                     {
    25.                         var newArray = new byte[fullData.Length + data.Length];
    26.                         Buffer.BlockCopy(fullData, 0, newArray, 0, fullData.Length);
    27.                         Buffer.BlockCopy(data, 0, newArray, fullData.Length, data.Length);
    28.                         fullData = newArray;
    29.                     }
    30.                 }
    31.  
    32.                 if (!res.IsStreamingFinished) return;
    33.  
    34.                 Debug.Log("Download Finished");
    35.  
    36.                 HTTPCacheService.Store(new Uri(activeDownload.cacheUrl), HTTPMethods.Get, res);
    37.  
    38.                 var assetBundle = Assetbundle.LoadFromMemory(fullData);
    39.  
    40.                 // ... handle assetbundle
    41.             }
    42.         );
    43.  
    44.         request.DisableCache = hasCache == null;
    45.         request.StreamFragmentSize = 1 * 1024 * 1024;
    46.         request.UseStreaming = true;
    47.  
    48.         request.Send();
    49.     }
    I know the stream response only has the most recent fragment data, so I'm not sure what to do in this case.

    Or if you have a better solution please let me know. Thanks! Great product!
     
    Last edited: Sep 7, 2017
  37. Frp529

    Frp529

    Joined:
    Apr 26, 2017
    Posts:
    3
    Code (CSharp):
    1.        
    2.             for (int i = 0; i < updateList.Count; ++i)
    3.             {
    4.                 m_LastDownloadedCount = 0;
    5.                 bundeleName = updateList[i].BundleName;
    6.                 hash = updateList[i].Hash;
    7.                 path = StringBuilder.StringFormat("{0}/{1}", dirPath, bundeleName);
    8.                 if (File.Exists(path))
    9.                     File.Delete(path);
    10.                 request = new HTTPRequest(new Uri(amStringBuilder.StringFormat("{0}{1}",address, bundeleName)),
    11.                 delegate (HTTPRequest originalRequest, HTTPResponse response)
    12.                 {
    13.                     List<byte[]> fragments = response.GetStreamedFragments();
    14.                     if (fragments == null)
    15.                         return;
    16.                     using (FileStream fs = new FileStream(path, FileMode.Append))
    17.                         foreach (byte[] data in fragments)
    18.                             fs.Write(data, 0, data.Length);
    19.  
    20.                     if(response.IsStreamingFinished)
    21.                     {
    22.                         PlayerPrefs.SetInt(bundeleName, hash);
    23.                         PlayerPrefs.Save();
    24.                         request.Clear();
    25.                     }
    26.                 });
    27.  
    28.                 request.OnProgress += DownloadProgress
    29.                 request.DisableCache = true; // already saving to a file, so turn off caching
    30.                 request.UseStreaming = true;
    31.                 request.StreamFragmentSize = 1 * 1024 * 1024; // 1 megabyte
    32.                 request.Send();
    33.          
    34.                 yield return request;
    35.             }
    @BestHTTP
    I'm having problem downloading assets bundles using streaming model, after downloading completes, the "IsStreamingFinished" flag is still false.sometimes the "fragments" is null.
     
    Last edited: Sep 7, 2017
  38. Jribs

    Jribs

    Joined:
    Jun 10, 2014
    Posts:
    154
    @Frp529 look at my post directly above yours. I do what you are trying to do.

    Sometimes the last fragment is null. You are checking if it is null but then you are returning out of the function, preventing it from completing. Flip that if statement and it will work.

    Code (CSharp):
    1. if (fragments != null) {
    2.      // Put fragments in the file
    3. }
     
  39. unity_ab

    unity_ab

    Joined:
    Sep 8, 2017
    Posts:
    4
    Hello BestHttp. We have the following error while try to send post request with multipart/form-data over ssl(https). When disable UseAlternateSSL all work good. But we can not validate certificate because CustomCertificateVerifyer is not call.

    Code (CSharp):
    1. Request Finished with Error! Write failure
    2.   at System.Net.Sockets.NetworkStream.Write (System.Byte[] buffer, Int32 offset, Int32 size) [0x00000] in <filename unknown>:0
    3.   at Org.BouncyCastle.Crypto.Tls.RecordStream.WriteRecord (Byte type, System.Byte[] plaintext, Int32 plaintextOffset, Int32 plaintextLength) [0x00143] in /Users/user/Documents/Projects/HappyWheels5.6trunk/Assets/Best HTTP (Pro)/BestHTTP/SecureProtocol/crypto/tls/RecordStream.cs:264
    4.   at Org.BouncyCastle.Crypto.Tls.TlsProtocol.SafeWriteRecord (Byte type, System.Byte[] buf, Int32 offset, Int32 len) [0x0000d] in /Users/user/Documents/Projects/HappyWheels5.6trunk/Assets/Best HTTP (Pro)/BestHTTP/SecureProtocol/crypto/tls/TlsProtocol.cs:520
    5. UnityEngine.Debug:LogError(Object)
    6. <SendPOSTRequest>c__AnonStorey1:<>m__0(HTTPRequest, HTTPResponse) (at Assets/Best HTTP (Pro)/LLNetworkManager.cs:126)
    7. BestHTTP.HTTPRequest:CallCallback() (at Assets/Best HTTP (Pro)/BestHTTP/HTTPRequest.cs:1255)
    8. BestHTTP.ConnectionBase:HandleCallback() (at Assets/Best HTTP (Pro)/BestHTTP/Connections/ConnectionBase.cs:171)
    9. BestHTTP.HTTPManager:OnUpdate() (at Assets/Best HTTP (Pro)/BestHTTP/HTTPManager.cs:643)
    10. BestHTTP.HTTPUpdateDelegator:Update() (at Assets/Best HTTP (Pro)/BestHTTP/HTTPUpdateDelegator.cs:171)
     
  40. Polysquat_Studios

    Polysquat_Studios

    Joined:
    Nov 6, 2014
    Posts:
    37
    Hey, is there a way to use cookies in the WebGl version of BestHTTP. I tried to build a project returning the response cookies and it failed. Im trying to get to get cookies from an AWS Lambda function and use the Set-Cookie to set them into the browser. I could just write my own JSlib, send the key pair to it and that write the cookie that way.
     
  41. BestHTTP

    BestHTTP

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

    If you are collecting the downloaded data into a large byte[], I wouldn't use streaming. I would do something like this:
    Code (CSharp):
    1. void StartDownloadQ(Uri uri, Action<byte[]> callback)
    2. {
    3.     // try to get the data from the cache
    4.     byte[] data = LoadFromCache(uri);
    5.  
    6.     if (data != null)
    7.     {
    8.         callback(data);
    9.         return;
    10.     }
    11.  
    12.     HTTPRequest request = new HTTPRequest(uri, (req, resp) =>
    13.     {
    14.         switch (req.State)
    15.         {
    16.             // The request finished without any problem.
    17.             case HTTPRequestStates.Finished:
    18.                 if (resp.IsSuccess)
    19.                 {
    20.                     // Save to the cache
    21.                     SaveToCache(req.Uri, resp.Data);
    22.  
    23.                     // call the callback
    24.                     callback(resp.Data);
    25.                 }
    26.                 else // Internal server error?
    27.                     Debug.LogWarning(string.Format("Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
    28.                                                     resp.StatusCode,
    29.                                                     resp.Message,
    30.                                                     resp.DataAsText));
    31.                 break;
    32.  
    33.             // The request finished with an unexpected error. The request's Exception property may contain more info about the error.
    34.             case HTTPRequestStates.Error:
    35.                 Debug.LogWarning("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
    36.                 break;
    37.  
    38.             // The request aborted, initiated by the user.
    39.             case HTTPRequestStates.Aborted:
    40.                 Debug.LogWarning("Request Aborted!");
    41.                 break;
    42.  
    43.             // Connecting to the server is timed out.
    44.             case HTTPRequestStates.ConnectionTimedOut:
    45.                 Debug.LogError("Connection Timed Out!");
    46.                 break;
    47.  
    48.             // The request didn't finished in the given time.
    49.             case HTTPRequestStates.TimedOut:
    50.                 Debug.LogError("Processing the request Timed Out!");
    51.                 break;
    52.         }
    53.     });
    54.  
    55.     request.DisableCache = true;
    56.     request.Send();
    57. }
    58.  
    59. void SaveToCache(Uri baseRequest, byte[] data)
    60. {
    61.     if (data == null)
    62.         return;
    63.  
    64.     string path = Application.persistentDataPath + baseRequest.LocalPath;
    65.  
    66.     File.WriteAllBytes(path, data);
    67.  
    68.     Debug.Log(string.Format("SaveToCache - Data for request '{0}' saved to {1}", baseRequest, path));
    69. }
    70.  
    71. byte[] LoadFromCache(Uri baseRequest)
    72. {
    73.     string path = Application.persistentDataPath + baseRequest.LocalPath;
    74.  
    75.     if (!File.Exists(path))
    76.         return null;
    77.      
    78.     byte[] data = File.ReadAllBytes(path);
    79.  
    80.     Debug.Log(string.Format("LoadFromCache - Data for request '{0}' loaded from {1}", baseRequest, path));
    81.  
    82.     return data;
    83. }
    And you could do call the StartDownloadQ function like this:
    Code (CSharp):
    1.  
    2. StartDownloadQ(new Uri("https://bucket.s3.amazonaws.com/filename?AWSAccessKeyId=MYACCESSKEY&Expires=111111&Signature=ALWAYS_UNIQUE"),
    3.                 (fullData) => {
    4.                     var assetBundle = AssetBundle.LoadFromMemory(fullData);
    5.  
    6.                     // ... handle assetbundle
    7.                 });
    8.  
     
  42. BestHTTP

    BestHTTP

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

    You are receiving this error every time you try to send a request to the same server?
    Because it looks like the server closed the tcp connection. First I would try to disable connection pooling, in some cases it can help:
    Code (CSharp):
    1. HTTPManager.KeepAliveDefaultValue = false;
    Also, you can validate certifications when UseAlternateSSL is set to false, but you have to use the HTTPManager.DefaultCertificationValidator callback instead.
     
  43. BestHTTP

    BestHTTP

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

    There's no out of the box way, but you can edit the \Assets\Best HTTP (Pro)\Plugins\WebGL\BestHTTP_WebRequest.jslib and alter it as you want.
    You can find the c# side code in the \Assets\Best HTTP (Pro)\BestHTTP\Connections\WebGLConnection.cs
     
  44. Jribs

    Jribs

    Joined:
    Jun 10, 2014
    Posts:
    154
    @BestHTTP Fantastic! Just implemented your solution and it all works great! I just needed to add my own expiration solution and now i'm good to go. Thanks!
     
  45. rattlesnake

    rattlesnake

    Joined:
    Jul 18, 2013
    Posts:
    138
    Hey you made this script to stream distant URL and it works fine :)
    I would like to also stream a big JPG but from my local drive...
    I tried a URL like : string url = "file:///"+cheminImage;
    But i shows a Download failed: Error</color>

    Could you modify a bit the code in order to make it works ?
    Thank you so much, have a nice day :)
     
  46. BestHTTP

    BestHTTP

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

    It should work just fine, however not all platforms are supported by the file:// protocol implementation (android for example that I know that will not work).

    upload_2017-9-12_8-12-20.png
     
  47. rattlesnake

    rattlesnake

    Joined:
    Jul 18, 2013
    Posts:
    138
    Ah yes I understand ! thank you once again for your answer :)
     
  48. Uli_Okm

    Uli_Okm

    Joined:
    Jul 10, 2012
    Posts:
    95
    Hi!
    I`m using this plugin from some time now, and have only one doubt:
    Is there any advantage in using the precompiled .dll files over the open source scripts (in performance or memory usage)?
    I`m asking this because right now I have a project that is running in Windows standalone and WebGL that has EXTREME limitations in the amount of RAM I can use, and also the computers running it are veeery old, so every small performance increase is necessary.

    Thanks
     
  49. BestHTTP

    BestHTTP

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

    The precompiled dlls are there to reduce compile time. Performance and/or memory wise it shouldn't be different.

    However, using the Pro (and the source code), you can disable various features of the plugin. For example, if you don't use the SignalR protocol, you can disable it by adding the BESTHTTP_DISABLE_SIGNALR to the Scripting Define Symbols.
    I think it will not help too much, but it might free up a little memory.
     
  50. unity_ab

    unity_ab

    Joined:
    Sep 8, 2017
    Posts:
    4
    We have this error with some requests, not all. And periodically we have another kind of errors with post requests:

    Request Finished with Error! Remote server closed the connection before sending response header!

    Our server has another clients and all work good with them. We can reproduce this error only on device (iphone, ipad).
    And this errors appeared when we begin use https protocol. With http all worked good.