Soumik Bose commited on
Commit
bd41de4
Β·
1 Parent(s): bd8337b

ssl logic

Browse files
Files changed (1) hide show
  1. controller.py +94 -46
controller.py CHANGED
@@ -60,7 +60,7 @@ logging.basicConfig(
60
  logger = logging.getLogger("API_Controller")
61
 
62
  # ==============================================================================
63
- # ASYNC CONNECTION POOL MANAGER
64
  # ==============================================================================
65
  class AsyncPoolManager:
66
  def __init__(self):
@@ -70,21 +70,52 @@ class AsyncPoolManager:
70
 
71
  async def get_pg_pool(self, db_url: str) -> asyncpg.Pool:
72
  async with self._lock:
73
- if db_url not in self._pg_pools:
74
- logger.info(f"Creating new AsyncPG pool for: {db_url[:25]}...")
75
- try:
76
- # asyncpg handles Keepalives and SSL automatically better than psycopg2
77
- pool = await asyncpg.create_pool(
78
- dsn=db_url,
79
- min_size=1,
80
- max_size=20,
81
- max_inactive_connection_lifetime=300,
82
- command_timeout=60
83
- )
84
- self._pg_pools[db_url] = pool
85
- except Exception as e:
86
- logger.error(f"Failed to create PG pool: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  raise e
 
88
  return self._pg_pools[db_url]
89
 
90
  async def get_mysql_pool(self, db_url: str) -> asyncmy.Pool:
@@ -94,37 +125,54 @@ class AsyncPoolManager:
94
 
95
  async with self._lock:
96
  # Check again inside lock
97
- if db_url not in self._mysql_pools:
98
- logger.info(f"Creating new AsyncMy pool for: {db_url[:25]}...")
99
- parsed = urlparse(db_url)
100
- qs = parse_qs(parsed.query)
101
-
102
- # Determine SSL settings
103
- # asyncmy needs an SSL context or ssl=True/False
104
- ssl_ctx = None
105
- if 'ssl-mode' in qs:
106
- mode = qs['ssl-mode'][0].upper()
107
- if mode != 'DISABLED':
108
- # Create a default SSL context that allows self-signed certs (common in cloud DBs)
109
- ssl_ctx = ssl.create_default_context()
110
- ssl_ctx.check_hostname = False
111
- ssl_ctx.verify_mode = ssl.CERT_NONE
112
-
113
- try:
114
- pool = await asyncmy.create_pool(
115
- user=parsed.username,
116
- password=parsed.password,
117
- host=parsed.hostname,
118
- port=parsed.port or 3306,
119
- db=parsed.path.lstrip("/"),
120
- minsize=1,
121
- maxsize=20,
122
- autocommit=True,
123
- pool_recycle=280,
124
- ssl=ssl_ctx # Pass the SSL context explicitly
125
- )
126
- self._mysql_pools[db_url] = pool
127
- except Exception as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  logger.error(f"Failed to create MySQL pool: {e}")
129
  raise e
130
 
 
60
  logger = logging.getLogger("API_Controller")
61
 
62
  # ==============================================================================
63
+ # ASYNC CONNECTION POOL MANAGER (UPDATED WITH SSL FALLBACK)
64
  # ==============================================================================
65
  class AsyncPoolManager:
66
  def __init__(self):
 
70
 
71
  async def get_pg_pool(self, db_url: str) -> asyncpg.Pool:
72
  async with self._lock:
73
+ if db_url in self._pg_pools:
74
+ return self._pg_pools[db_url]
75
+
76
+ logger.info(f"Creating new AsyncPG pool for: {db_url[:25]}...")
77
+
78
+ # Define connection logic
79
+ async def attempt_connect(force_no_ssl: bool = False):
80
+ # Common config
81
+ config = {
82
+ "dsn": db_url,
83
+ "min_size": 1,
84
+ "max_size": 20,
85
+ "max_inactive_connection_lifetime": 300,
86
+ "command_timeout": 60
87
+ }
88
+
89
+ # If falling back, explicitly disable SSL (overrides DSN settings in most cases)
90
+ if force_no_ssl:
91
+ config["ssl"] = False
92
+
93
+ return await asyncpg.create_pool(**config)
94
+
95
+ try:
96
+ # 1. Try Default Connection (uses DSN settings or defaults)
97
+ pool = await attempt_connect(force_no_ssl=False)
98
+ logger.info("βœ… Connected to Postgres (Standard/SSL)")
99
+ self._pg_pools[db_url] = pool
100
+
101
+ except Exception as e:
102
+ err_msg = str(e).lower()
103
+ # 2. Check for SSL specific errors
104
+ # "server does not support ssl" is the specific error from Postgres
105
+ # "sslerror" covers generic python ssl issues
106
+ if "ssl" in err_msg or "certificate" in err_msg or "connection reset" in err_msg:
107
+ logger.warning(f"⚠️ SSL Connection failed ({e}). Retrying with SSL DISABLED...")
108
+ try:
109
+ pool = await attempt_connect(force_no_ssl=True)
110
+ logger.info("βœ… Connected to Postgres (No SSL Fallback)")
111
+ self._pg_pools[db_url] = pool
112
+ except Exception as retry_e:
113
+ logger.error(f"❌ Postgres Retry Failed: {retry_e}")
114
+ raise retry_e
115
+ else:
116
+ logger.error(f"❌ Failed to create PG pool: {e}")
117
  raise e
118
+
119
  return self._pg_pools[db_url]
120
 
121
  async def get_mysql_pool(self, db_url: str) -> asyncmy.Pool:
 
125
 
126
  async with self._lock:
127
  # Check again inside lock
128
+ if db_url in self._mysql_pools:
129
+ return self._mysql_pools[db_url]
130
+
131
+ logger.info(f"Creating new AsyncMy pool for: {db_url[:25]}...")
132
+ parsed = urlparse(db_url)
133
+ qs = parse_qs(parsed.query)
134
+
135
+ # Determine initial SSL settings
136
+ initial_ssl_ctx = None
137
+ if 'ssl-mode' in qs:
138
+ mode = qs['ssl-mode'][0].upper()
139
+ if mode != 'DISABLED':
140
+ initial_ssl_ctx = ssl.create_default_context()
141
+ initial_ssl_ctx.check_hostname = False
142
+ initial_ssl_ctx.verify_mode = ssl.CERT_NONE
143
+
144
+ # Helper to create pool
145
+ async def attempt_connect(ssl_context):
146
+ return await asyncmy.create_pool(
147
+ user=parsed.username,
148
+ password=parsed.password,
149
+ host=parsed.hostname,
150
+ port=parsed.port or 3306,
151
+ db=parsed.path.lstrip("/"),
152
+ minsize=1,
153
+ maxsize=20,
154
+ autocommit=True,
155
+ pool_recycle=280,
156
+ ssl=ssl_context
157
+ )
158
+
159
+ try:
160
+ # 1. Try with calculated SSL context
161
+ pool = await attempt_connect(initial_ssl_ctx)
162
+ logger.info("βœ… Connected to MySQL")
163
+ self._mysql_pools[db_url] = pool
164
+ except Exception as e:
165
+ # 2. If it fails and we were trying to use SSL, try disabling it
166
+ if initial_ssl_ctx is not None:
167
+ logger.warning(f"⚠️ MySQL SSL handshake failed ({e}). Retrying without SSL...")
168
+ try:
169
+ pool = await attempt_connect(None) # Pass None to disable SSL
170
+ logger.info("βœ… Connected to MySQL (No SSL Fallback)")
171
+ self._mysql_pools[db_url] = pool
172
+ except Exception as retry_e:
173
+ logger.error(f"❌ MySQL Retry Failed: {retry_e}")
174
+ raise retry_e
175
+ else:
176
  logger.error(f"Failed to create MySQL pool: {e}")
177
  raise e
178