Repository: ynput/OpenPype
Issue: Ftrack SSL connection error

Almost every launch we get these errors in tray debug. It doesn't seem to affect primary functionality, but should be resolved nevertheless.

```
>>> [  Application "Application action" - Registered successfully (0.0029sec)  ]
>>> [  Action "Batch Tasks" - Registered successfully (0.0029sec)  ]
>>> [  Action "Pype Admin - Clean hierarchical custom attributes" - Registered successfully (0.0041sec)  ]
>>> [  Action "Sort Review" - Registered successfully (0.0392sec)  ]
>>> [  Action "Open File" - Registered successfully (0.0028sec)  ]
>>> [  Action "Pype Admin - Create/Update Avalon Attributes" - Registered successfully (0.0028sec)  ]
>>> [  Action "Create Folders" - Registered successfully (0.0030sec)  ]
>>> [  Action "Create Project Structure" - Registered successfully (0.0040sec)  ]
>>> [  Action "Delete Asset/Subsets" - Registered successfully (0.0036sec)  ]
>>> [  Action "Pype Admin - Delete old versions" - Registered successfully (0.0061sec)  ]
ERROR:ftrack_api.event.hub.SocketBaseEventHub:Error sending event <Event {'id': '294f0cdd465b46dc8a7828c6ad9d458a', 'data': {'subscriber': {'id': 'b96634222cff4b2d87d50a69ee2d7b49'}, 'subscription': 'topic=ftrack.connect.verify-startup and data.storage_scenario.scenario="ftrack.centralized-storage"'}, 'topic': 'ftrack.meta.subscribe', 'sent': None, 'source': {'id': 'ad1ab7e241b14ad1b851fc9e14f84e65', 'user': {'username': 'milan.kolar'}}, 'target': '', 'in_reply_to_event': None}>.
Traceback (most recent call last):
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\ftrack_api\\event\\hub.py", line 890, in _send_packet
    self._connection.send(packet)
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\websocket\\_core.py", line 282, in send
    return self.send_frame(frame)
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\websocket\\_core.py", line 310, in send_frame
    l = self._send(data)
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\websocket\\_core.py", line 510, in _send
    return send(self.sock, data)
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\websocket\\_socket.py", line 168, in send
    return _send()
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\websocket\\_socket.py", line 150, in _send
    return sock.send(data)
  File "C:\\Program Files\\Python37\\lib\\ssl.py", line 1003, in send
    return self._sslobj.write(data)
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:2423)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\ftrack_api\\event\\hub.py", line 675, in _publish
    self._event_namespace, event, callback=callback
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\ftrack_api\\event\\hub.py", line 868, in _emit_event_packet
    self._code_name_mapping['event'], data=data, callback=callback
  File "C:\\Users\\milan\\code\\pype3.0\\.venv\\lib\\site-packages\\ftrack_api\\event\\hub.py", line 894, in _send_packet
    'Failed to send packet: {0}'.format(error)
ftrack_api.exception.EventHubConnectionError: Failed to send packet: EOF occurred in violation of protocol (_ssl.c:2423)
```

Changed files:
openpype/modules/ftrack/ftrack_server/ftrack_server.py, openpype/modules/ftrack/ftrack_server/lib.py

diff --git a/openpype/modules/ftrack/ftrack_server/ftrack_server.py b/openpype/modules/ftrack/ftrack_server/ftrack_server.py
index 285ca29dc5..bd67fba3d6 100644
--- a/openpype/modules/ftrack/ftrack_server/ftrack_server.py
+++ b/openpype/modules/ftrack/ftrack_server/ftrack_server.py
@@ -1,4 +1,5 @@
 import os
+import time
 import types
 import logging
 import traceback
@@ -10,7 +11,6 @@ from openpype.lib import (
     modules_from_path
 )
 
-
 log = PypeLogger.get_logger(__name__)
 
 """
@@ -120,6 +120,18 @@ class FtrackServer:
         if not session:
             session = ftrack_api.Session(auto_connect_event_hub=True)
 
+        # Wait until session has connected event hub
+        if session._auto_connect_event_hub_thread:
+            # Use timeout from session (since ftrack-api 2.1.0)
+            timeout = getattr(session, "request_timeout", 60)
+            started = time.time()
+            while not session.event_hub.connected:
+                if (time.time() - started) > timeout:
+                    raise RuntimeError((
+                        "Connection to Ftrack was not created in {} seconds"
+                    ).format(timeout))
+                time.sleep(0.1)
+
         self.session = session
         if load_files:
             if not self.handler_paths:
diff --git a/openpype/modules/ftrack/ftrack_server/lib.py b/openpype/modules/ftrack/ftrack_server/lib.py
index 91f3712136..88f849e765 100644
--- a/openpype/modules/ftrack/ftrack_server/lib.py
+++ b/openpype/modules/ftrack/ftrack_server/lib.py
@@ -3,11 +3,11 @@ import sys
 import logging
 import getpass
 import atexit
-import tempfile
 import threading
 import datetime
 import time
 import queue
+import appdirs
 import pymongo
 
 import requests
@@ -165,7 +165,6 @@ class ProcessEventHub(SocketBaseEventHub):
 
     def wait(self, duration=None):
         """Overriden wait
-
         Event are loaded from Mongo DB when queue is empty. Handled event is
         set as processed in Mongo DB.
         """
@@ -252,7 +251,7 @@ class CustomEventHubSession(ftrack_api.session.Session):
         self, server_url=None, api_key=None, api_user=None, auto_populate=True,
         plugin_paths=None, cache=None, cache_key_maker=None,
         auto_connect_event_hub=False, schema_cache_path=None,
-        plugin_arguments=None, **kwargs
+        plugin_arguments=None, timeout=60, **kwargs
     ):
         self.kwargs = kwargs
 
@@ -331,6 +330,7 @@ class CustomEventHubSession(ftrack_api.session.Session):
         self._request.auth = ftrack_api.session.SessionAuthentication(
             self._api_key, self._api_user
         )
+        self.request_timeout = timeout
 
         self.auto_populate = auto_populate
 
@@ -368,8 +368,9 @@ class CustomEventHubSession(ftrack_api.session.Session):
         # rebuilding types)?
         if schema_cache_path is not False:
             if schema_cache_path is None:
+                schema_cache_path = appdirs.user_cache_dir()
                 schema_cache_path = os.environ.get(
-                    'FTRACK_API_SCHEMA_CACHE_PATH', tempfile.gettempdir()
+                    'FTRACK_API_SCHEMA_CACHE_PATH', schema_cache_path
                 )
 
             schema_cache_path = os.path.join(