4

I am downloading Assetbundles using the UnitywebRequest method from HTTPS, however the UnityWebRequest.SendWebRequest seems to take its sweet time to actually start receiving any data.

public static IEnumerator DownloadMenuAssetBundle(string fileName)
{
    string path = Path.Combine(Globals.Platform, fileName);
    UnityWebRequest www = new UnityWebRequest(FileManager.RequestFile(path));//this returns the complete url to the bundle e.g https://mywebsite.com/unity/myBundle
    www.downloadHandler = new DownloadHandlerBuffer();
    www.SendWebRequest();
    while (!www.isDone)
    {
        Debug.Log("response: " + www.responseCode);
        if (www.isNetworkError)
        {
            Debug.Log(www.error);
        }
        Debug.Log("downloaded:" + www.downloadedBytes);
        yield return null;
    }
    Debug.Log("downloaded bytes: " + www.downloadedBytes);
    Debug.Log("final response:" + www.responseCode);

    if (www.error != null)
    {
        Debug.LogError("Encountered error while downloading: <color=blue>" + fileName + "</color>: " + www.error);
    }
    else
    {
       //rest of the logic, this works
    } 

Debug.Log("response: " + www.downloadedBytes); will return 0 for a random amount of time (ranging from a few to up to a couple minutes at times). But www. isNetworkError is never hit, and once bytes do start to be received it'll download the entire thing in a couple of miliseconds.

Previously i was using the exact same script on a http server and it worked flawlessly without any delays, but once i switched over to https it started taking a while. The delay also does not happen in the editor (Unity version 2017.2.1f1 runtime version .net 3.5 with 2.0 subset api compability) but happens on all my mobile devices (Oneplus 3, samsung galaxy s8, samsung galaxy s6).

At first www.responseCode returned a 301 Moved Permanently, i resolved this by using the new https url instead of the http url hoping this would fix it. However it didn't and now i only get 200 OK.

It is also an inconsistent issue because the timing it takes isn't ever the same, neither does it even happen all the time (but does most of the time)

Can this be an issue by the security layer taking additional time or the server taking its time to respond? if this is the issue how would i be able to track this down (though i doubt this as it works well in the editor)?

EDIT: workaround I got around the issue by using a WWW class instead of UnityWebRequest. the delay is completely gone now but no SSL certificate validation is being done as Unity seems to deny them by default. I wouldn't really call it a fix but works for now.

Remy
  • 4,843
  • 5
  • 30
  • 60
  • 1
    Remove the `if (www.isNetworkError)` from the `while` loop. You should only do that after the loop is done and outside of it. If there is an an error the loop should exit right away. – Programmer Oct 01 '18 at 14:55
  • I just saw your edit. Do you have SSL certificate you want to validate during the request? – Programmer Oct 05 '18 at 19:13
  • @Programmer yes, if possible. I have to say that so far I've not yet had the time to investigate/look into it thoroughly yet. But assume i have to make a certificate store using x509? – Remy Oct 05 '18 at 19:43
  • 1
    I suspect that. Note entirely sure but you can use the [`UnityWebRequest.certificateHandler`](https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest-certificateHandler.html) and see what happens. [This](https://docs.unity3d.com/ScriptReference/Networking.CertificateHandler.ValidateCertificate.html) shows how to validate a certificate. You can also check out my [other](https://stackoverflow.com/a/43678419/3785314) which is also a solution to slow `UnityWebRequest` on Android by using `WebRequest` – Programmer Oct 05 '18 at 23:17
  • Hi, could you show me how to use the www class to send post request? can't find any documentation as to its obsolete now – Jerin Cherian Feb 06 '20 at 09:58

1 Answers1

-3

Always trouble with UnityWebRequest - sometimes nothing makes sense with UnityWebRequests...

You can try to replace

 UnityWebRequest www = new UnityWebRequest(FileManager.RequestFile(path));

with

 UnityWebRequest www = new UnityWebRequestAssetBundle(FileManager.RequestFile(path), 0);

Optional for testing:

Also I think your while loop is not "save" to catch the network error. I would prefer the following like mentioned in the documentation

UnityWebRequest www = new UnityWebRequestAssetBundle(FileManager.RequestFile(path);
yield return www.SendWebRequest();

if (request.isNetworkError || request.isHttpError)
{
    Debug.Log(www.error.ToString());
}
else
{
    // for testing only // if yield return www.SendWebRequest(); is working as expected www.isDone = true here!
    while (!www.isDone)
    {
        Debug.Log("Something is wrong! "  + www.responseCode);
        yield return new WaitForSeconds(0.1f);
    }
    // do whatever you want
}

Hope this helps.

dome12b
  • 583
  • 11
  • 32
  • 2
    Your `while` loop statement is false. What's the point of you using `yield return www.SendWebRequest()` while also using `while (!www.isDone)`? By yielding `SendWebRequest` the use of the `while` loop is now pintless since it will wait until the download is done before the `while` loop get's to execute. The idea of using `while (!www.isDone)` is to get the download status. Also, it's better to use `yield return null` here than `WaitForSeconds(0.1f)`. Why check or generate generate garbage every `0.1` second? – Programmer Oct 01 '18 at 14:20
  • Yes you are right with the pintless while loop. But if `yield return www.SendWebRequest();` is waiting until the download is done the "garbage" of 0.1 seconds is never processed. I have the experience that the yield return statements behave strange on mobile and web. I am working with this code on my web project. And it is working. It is not "right" as how coroutines should work. I have another "waiting coroutine" by using lambda: `yield return new WaitUntil(() => www.isDone` – dome12b Oct 01 '18 at 14:47
  • 2
    Then why have the `while` loop at-all? Notice how OP is using `www.downloadedBytes` so this means that OP wants to and must use `while (!www.isDone)` in this case. Again, `yield return new WaitUntil(() => www.isDone` also generates garbage too due to `new WaitUntil` and lambda and they have nothing to do with the issue. I think OP should stick with the `yield return null`. You really have to edit your answer. – Programmer Oct 01 '18 at 14:52
  • I think we are looking for a solution and not optimization here. "Garbage" in terms of "useless code" is not the point here. And again, if the request is working there is no need for a extra `WaitUntil` or `while` loop - I know. My approach is to use `UnityWebRequestAssetBundle` as it is. I will make an edit ;) – dome12b Oct 01 '18 at 15:18
  • 2
    The main reason of my first comment was **not** about optimization but to point to you that you misused the `www.isDone` property which you still have not fixed. The `yield return www.SendWebRequest();` will **block** the request and `www.isDone` and `www.downloadedBytes` will not work properly. I had to point out the performance code because you took a code that doesn't generate garbage and made it to do so which in fact made it worse and doesn't solve the issue. I am **not** calling your code "Garbage" or "useless code". I meant that the code generates GC. Please, Google that. – Programmer Oct 01 '18 at 15:32
  • Wow... `www.isDone` is not working properly after `yield return www.SendWebRequest();` ?! I think you should google `UnityWebRequest.isDone` ... I did not misread your "garbage" - but I still think that's not the point here. But please show us your solution and vote down my answer. Just wanted to show the OP a ("false") solution working in my case. don't call `www.downloadedBytes` on web or mobile. Coroutines do not work really async. The are working in between. You can google that, too... – dome12b Oct 01 '18 at 15:50
  • 2
    Pay little attention to what programmer is saying and you'll understand it. Calling `www.SendWebRequest()` then waiting with `UnityWebRequest.isDone` in a `while` loop is Ok. However, calling `yield return www.SendWebRequest()` then waiting with `UnityWebRequest.isDone` in a `while` loop is NOT Ok. This is because the `yield return` will make it to wait in the `SendWebRequest` until the request is done. This makes the use of `isDone` to be redundant and `www.downloadedBytes` cannot be used because the request would have been finished by the time it reaches where `www.downloadedBytes` is. – PassetCronUs Oct 01 '18 at 16:05
  • The idea of using `www.downloadedBytes` is to get the status/progress of the download while the data is still downloading and this must be used with `isDone` properly. If you don't need that information then just use `yield return www.SendWebRequest()`. Notice how OP used `www.downloadedBytes`. This is a sign that he/she wants that. – PassetCronUs Oct 01 '18 at 16:12
  • unfortunately `UnityWebRequestAssetBundle` isn't implemented untill unity 2018 (i'm on 2017.1f1) and the deprecated version of it, `UnityWebRequest.GetAssetbundle` doesn't solve it. You and Programmer are right about my networkError check being in the wrong place so fixed at. I now tried downloading the bundles using `WWW` class instead and this seems to remove the delay. – Remy Oct 03 '18 at 06:59
  • to clarify, in the code i posted above i am indeed only using `while(!www.isDone)` to read the progress of the download, since i wanted to know if it was just downloading really slowly (maybe bad internet or something) or if it didn't start at all. for the finished code i'll swap to `yield return www.SendWebRequest()` – Remy Oct 03 '18 at 07:04