Repository: learningequality/kolibri

Issue:
Catch ValueError during network discovery for when JSON parsing fails
<!--
Instructions:
 * Fill out the sections below, replace …'s with information about your issue
 * Use the 'preview' function above this text box to verify formatting before submitting
-->

### Observed behavior
<!--
Description of the behavior that was observed, including screenshots or other references when applicable
-->

When trying to import content channels (from local network or internet), no error message is produced even though the connection isn't established. (The "trying to connect" message stays on forever) This happens when there is a typo in the address. (I used kolibri-demo.learningequality.omg)  When there's no internet, an error message DOES get produced. 

### Expected behavior
<!--
Description of what behavior was expected but did not occur
-->

An error message should've been produced.

### User-facing consequences
<!--
Implications and real-world consequences for learners, coaches, admins, and other users of the application
-->

The user will not know how long they have to wait to see if the connection is being established.

### Errors and logs
<!--
Relevant logs from:
 * the command line
 * ~/.kolibri/logs/kolibri.txt
 * the browser console

Please wrap errors in triple backticks for clean formatting like this:
```
01:10 info: something happened
01:12 error: something bad happened
```
-->

…

### Steps to reproduce
<!--
Precise steps that someone else can follow in order to see this behavior
-->

Import content channels from local network or internet >input "kolibri-demo.learningequality.omg" or something else with a typo in it.

### Context
<!--
Tell us about your environment, including:
 * Kolibri version
 * Operating system
 * Browser
-->

Kolibri version: 0.14.3
Operating system: Android 5.1
Browser: Android App
Device: BLU Advance 5.0



Discussion:
On the kolibri-beta demo server, I do see an error when trying to _add_ the example address.

![image](https://user-images.githubusercontent.com/10248067/92151109-6e25a800-edd5-11ea-86d8-5b2beb4f1eb0.png)

@MasakoOllivier Is this the same same step you failed to see an error (under the URL address text box)?
Yes. I used Android 5 and 10, and both didn't produce any error message. (no internet did produce an error.) I'm testing something else right now, but I'll try it again to see if it was some sort of a glitch.
The message "Trying to connect to server..." stays on forever.

(This one is Android 10, Nokia 1.3)

![Screenshot_20200903-111449](https://user-images.githubusercontent.com/70489926/92152267-40416300-edd7-11ea-975e-094fa4374d38.png)


Ah, that's good info! This _might_ be specific to the Android version then. 

What should happen is that the app would timeout after failing to connect to that `.omg` address and multiple variations of it.

@kollivier Is there anyway to get the logs of this phone to see why the URL validation isn't working?
Actually, it's happening on my Mac, too. (Kolibri 0.14.3)

![image](https://user-images.githubusercontent.com/70489926/92153678-84356780-edd9-11ea-9dfe-f061e939b1af.png)

It hasn't been "forever" yet, but it's been at least 2 min.
@jonboiser I did find an error in the logs, and can confirm this happens in the Mac app as well. Here's the error:

```
INFO 2020-09-03 18:14:04,256 kolibri.core.discovery.utils.network.client Attempting connections to variations of the URL: Kolibri-demo.learningequality.omg
INFO 2020-09-03 18:14:04,326 kolibri.core.discovery.utils.network.client Attempting connection to: http://kolibri-demo.learningequality.omg:8080/
INFO 2020-09-03 18:14:09,638 kolibri.core.discovery.utils.network.client Unable to connect: HTTPConnectionPool(host='kolibri-demo.learningequality.omg', port=8080): Max retries exceeded with url: /api/public/info/ (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x79b62778>, 'Connection to kolibri-demo.learningequality.omg timed out. (connect timeout=5)'))
INFO 2020-09-03 18:14:09,640 kolibri.core.discovery.utils.network.client Attempting connection to: http://kolibri-demo.learningequality.omg/
ERROR 2020-09-03 18:14:10,058 django.request Internal Server Error: /api/discovery/staticnetworklocation/
Traceback (most recent call last):
  File "/home/kivy/build/android/kolibri/dist/django/core/handlers/exception.py", line 41, in inner
  File "/home/kivy/build/android/kolibri/dist/django/core/handlers/base.py", line 187, in _get_response
  File "/home/kivy/build/android/kolibri/dist/django/core/handlers/base.py", line 185, in _get_response
  File "/home/kivy/build/android/kolibri/dist/django/views/decorators/csrf.py", line 58, in wrapped_view
  File "/home/kivy/build/android/kolibri/dist/rest_framework/viewsets.py", line 116, in view
  File "/home/kivy/build/android/kolibri/dist/rest_framework/views.py", line 495, in dispatch
  File "/home/kivy/build/android/kolibri/dist/rest_framework/views.py", line 455, in handle_exception
  File "/home/kivy/build/android/kolibri/dist/rest_framework/views.py", line 492, in dispatch
  File "/home/kivy/build/android/kolibri/dist/rest_framework/mixins.py", line 20, in create
  File "/home/kivy/build/android/kolibri/dist/rest_framework/serializers.py", line 236, in is_valid
  File "/home/kivy/build/android/kolibri/dist/rest_framework/serializers.py", line 434, in run_validation
  File "/home/kivy/build/android/kolibri/dist/rest_framework/serializers.py", line 493, in to_internal_value
  File "/home/kivy/build/android/kolibri/core/discovery/serializers.py", line 44, in validate_base_url
  File "/home/kivy/build/android/kolibri/core/discovery/utils/network/client.py", line 30, in __init__
  File "/home/kivy/build/android/kolibri/core/discovery/utils/network/client.py", line 49, in _attempt_connections
  File "/home/kivy/build/android/kolibri/dist/requests/models.py", line 897, in json
  File "/home/kivy/.local/share/python-for-android/build/other_builds/python3/armeabi-v7a__ndk_target_21/python3/Lib/json/__init__.py", line 357, in loads
  File "/home/kivy/.local/share/python-for-android/build/other_builds/python3/armeabi-v7a__ndk_target_21/python3/Lib/json/decoder.py", line 337, in decode
  File "/home/kivy/.local/share/python-for-android/build/other_builds/python3/armeabi-v7a__ndk_target_21/python3/Lib/json/decoder.py", line 355, in raw_decode
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
```
Full log is attached. I'll help Masako get setup with the Android tools needed to grab these logs later today!

[kolibri.txt](https://github.com/learningequality/kolibri/files/5170651/kolibri.txt)

So it seems to raise an Internal Server Error that doesn't get sent to the client
I think one way to fix it is to handle more general `Exceptions` at this line.

https://github.com/learningequality/kolibri/blob/65c0d60a7b2507337f95b131492e47d0e62e2b47/kolibri/core/discovery/utils/network/client.py#L56-L57

But maybe the problem here is that `.omg` isn't a real TLD? It might be something internal to the `requests` module.
The issue is that we do catch requests `RequestException` here: https://github.com/learningequality/kolibri/blob/develop/kolibri/core/discovery/utils/network/client.py#L56 but are not catching the JSON Decode Error.

We should add `ValueError` to the tuple of caught exceptions, as a Python 2 compatible way of catching the error.

Resolving patch:
diff --git a/kolibri/core/discovery/utils/network/client.py b/kolibri/core/discovery/utils/network/client.py
--- a/kolibri/core/discovery/utils/network/client.py
+++ b/kolibri/core/discovery/utils/network/client.py
@@ -55,6 +55,10 @@ def _attempt_connections(self, urls):
                     return response.url.rstrip("/").replace("api/public/info", "")
             except (requests.RequestException) as e:
                 logger.info("Unable to connect: {}".format(e))
+            except ValueError:
+                logger.info(
+                    "Invalid JSON returned when attempting to connect to a remote server"
+                )
 
         # we weren't able to connect to any of the URL variations, so all we can do is throw
         raise errors.NetworkLocationNotFound()


Test patch: