Repository: halcy/mastodon.py
Issue: stream exit() does not work

My code is:
```

        m = Mastodon(
            access_token=u.get('token'),
            api_base_url='https://' + host
        )
        listener = MyListener(mid)
        stream=m.stream_user(listener, run_async=True, reconnect_async=True)
```

Later I try to close the connection by calling
stream.close()
And I got:
```
 File "masto.py", line 154, in <module>
    stream.close()
  File "/home/shura/projects/megagate/venv/lib/python3.7/site-packages/mastodon/Mastodon.py", line 3570, in close
    self.connection.close()
AttributeError: 'NoneType' object has no attribute 'close'

```

Changed files:
mastodon/Mastodon.py

diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py
index 0922a6f..64450b6 100644
--- a/mastodon/Mastodon.py
+++ b/mastodon/Mastodon.py
@@ -3610,7 +3610,8 @@ class Mastodon:
                 
             def close(self):
                 self.closed = True
-                self.connection.close()
+                if not self.connection is None:
+                    self.connection.close()
 
             def is_alive(self):
                 return self._thread.is_alive()
@@ -3621,6 +3622,14 @@ class Mastodon:
                 else:
                     return True
 
+            def _sleep_attentive(self):
+                if self._thread != threading.current_thread():
+                    raise RuntimeError ("Illegal call from outside the stream_handle thread")
+                time_remaining = self.reconnect_async_wait_sec
+                while time_remaining>0 and not self.closed:
+                    time.sleep(0.5)
+                    time_remaining -= 0.5
+
             def _threadproc(self):
                 self._thread = threading.current_thread()
                 
@@ -3642,16 +3651,26 @@ class Mastodon:
                         self.reconnecting = True
                         connect_success = False
                         while not connect_success:
-                            connect_success = True
+                            if self.closed:
+                                # Someone from outside stopped the streaming
+                                self.running = False
+                                break
                             try:
-                                self.connection = self.connect_func()
-                                if self.connection.status_code != 200:
-                                    time.sleep(self.reconnect_async_wait_sec)
-                                    connect_success = False
-                                    exception = MastodonNetworkError("Could not connect to server.")
+                                the_connection = self.connect_func()
+                                if the_connection.status_code != 200:
+                                    exception = MastodonNetworkError(f"Could not connect to server. "
+                                                                     f"HTTP status: {the_connection.status_code}")
                                     listener.on_abort(exception)
+                                    self._sleep_attentive()
+                                if self.closed:
+                                    # Here we have maybe a rare race condition. Exactly on connect, someone
+                                    # stopped the streaming before. We close the previous established connection:
+                                    the_connection.close()
+                                else:
+                                    self.connection = the_connection
+                                    connect_success = True
                             except:
-                                time.sleep(self.reconnect_async_wait_sec)
+                                self._sleep_attentive()
                                 connect_success = False
                         self.reconnecting = False
                     else: