1- import asyncio
21import json
32from typing import Any , Dict , Optional
43
54import aiohttp
65
76from src .badfish .helpers .async_lru import alru_cache
87from src .badfish .helpers .exceptions import BadfishException
8+ from src .badfish .helpers .session_manager import SessionManager
99
1010
1111class HTTPClient :
@@ -19,10 +19,15 @@ def __init__(self, host: str, username: str, password: str, logger, retries: int
1919 self .host_uri = f"https://{ host } "
2020 self .redfish_uri = "/redfish/v1"
2121 self .root_uri = f"{ self .host_uri } { self .redfish_uri } "
22- self .semaphore = asyncio . Semaphore ( 50 )
22+ self .session_manager : Optional [ SessionManager ] = None
2323 self .token = None
2424 self .session_id = None
2525
26+ async def _ensure_session_manager (self ):
27+ """Lazy initialization of session manager."""
28+ if self .session_manager is None :
29+ self .session_manager = await SessionManager .get_instance ()
30+
2631 async def error_handler (self , response : aiohttp .ClientResponse , message : Optional [str ] = None ) -> None :
2732 try :
2833 raw = await response .text ("utf-8" , "ignore" )
@@ -47,13 +52,13 @@ async def error_handler(self, response: aiohttp.ClientResponse, message: Optiona
4752 @alru_cache (maxsize = 64 )
4853 async def get_request (self , uri : str , _continue : bool = False , _get_token : bool = False ):
4954 return await self .get_raw (uri , _continue , _get_token )
50-
55+
5156 @alru_cache (maxsize = 64 )
5257 async def get_json (self , uri : str , _continue : bool = False , _get_token : bool = False ):
5358 response = await self .get_raw (uri , _continue , _get_token )
5459 if not response :
5560 return None
56-
61+
5762 # Parse JSON from response
5863 try :
5964 raw = await response .text ("utf-8" , "ignore" )
@@ -64,28 +69,30 @@ async def get_json(self, uri: str, _continue: bool = False, _get_token: bool = F
6469 return None
6570
6671 async def get_raw (self , uri : str , _continue : bool = False , _get_token : bool = False ):
72+ await self ._ensure_session_manager ()
73+
74+ # Check token cache first (unless we're getting a new token)
75+ if not _get_token and not self .token :
76+ cached_token = self .session_manager .get_token (self .host )
77+ if cached_token :
78+ self .token = cached_token
79+ cached_session_id = self .session_manager .get_session_id (self .host )
80+ if cached_session_id :
81+ self .session_id = cached_session_id
82+
83+ headers = {}
84+ auth = None
85+
86+ if _get_token :
87+ auth = aiohttp .BasicAuth (self .username , self .password )
88+ elif self .token :
89+ headers ["X-Auth-Token" ] = self .token
90+
6791 try :
68- async with self .semaphore :
69- async with aiohttp .ClientSession () as session :
70- if not _get_token :
71- async with session .get (
72- uri ,
73- headers = {"X-Auth-Token" : self .token } if self .token else {},
74- ssl = False ,
75- timeout = 60 ,
76- ) as _response :
77- await _response .read ()
78- else :
79- async with session .get (
80- uri ,
81- auth = aiohttp .BasicAuth (self .username , self .password ),
82- ssl = False ,
83- timeout = 60 ,
84- ) as _response :
85- await _response .read ()
92+ _response = await self .session_manager .get_request (uri , headers , auth )
8693 except (Exception , TimeoutError ) as ex :
8794 if _continue :
88- return
95+ return None
8996 else :
9097 self .logger .debug (f"HTTPClient get_raw exception: { ex } " )
9198 self .logger .debug (f"Exception type: { type (ex )} " )
@@ -100,39 +107,28 @@ async def post_request(
100107 headers : Dict [str , str ],
101108 _get_token : bool = False ,
102109 ):
110+ await self ._ensure_session_manager ()
111+
112+ if not _get_token and self .token :
113+ headers = headers .copy () # Avoid mutating caller's dict
114+ headers .update ({"X-Auth-Token" : self .token })
115+
103116 try :
104- async with self .semaphore :
105- async with aiohttp .ClientSession () as session :
106- if not _get_token and self .token :
107- headers .update ({"X-Auth-Token" : self .token })
108- async with session .post (
109- uri ,
110- data = json .dumps (payload ),
111- headers = headers ,
112- ssl = False ,
113- ) as _response :
114- if _response .status != 204 :
115- await _response .read ()
116- else :
117- return _response
117+ _response = await self .session_manager .post_request (uri , payload , headers )
118118 except (Exception , TimeoutError ):
119119 raise BadfishException ("Failed to communicate with server." )
120120 return _response
121121
122122 async def patch_request (self , uri : str , payload : Dict [str , Any ], headers : Dict [str , str ], _continue : bool = False ):
123+ await self ._ensure_session_manager ()
124+
125+ if self .token :
126+ headers = headers .copy () # Avoid mutating caller's dict
127+ headers .update ({"X-Auth-Token" : self .token })
128+
123129 try :
124- async with self .semaphore :
125- async with aiohttp .ClientSession () as session :
126- if self .token :
127- headers .update ({"X-Auth-Token" : self .token })
128- async with session .patch (
129- uri ,
130- data = json .dumps (payload ),
131- headers = headers ,
132- ssl = False ,
133- ) as _response :
134- raw_data = await _response .read ()
135- return _response
130+ _response = await self .session_manager .patch_request (uri , payload , headers )
131+ return _response
136132 except Exception as ex :
137133 if _continue :
138134 return None
@@ -141,18 +137,15 @@ async def patch_request(self, uri: str, payload: Dict[str, Any], headers: Dict[s
141137 raise BadfishException ("Failed to communicate with server." )
142138
143139 async def delete_request (self , uri : str , headers : Dict [str , str ]):
140+ await self ._ensure_session_manager ()
141+
142+ if self .token :
143+ headers = headers .copy () # Avoid mutating caller's dict
144+ headers .update ({"X-Auth-Token" : self .token })
145+
144146 try :
145- async with self .semaphore :
146- async with aiohttp .ClientSession () as session :
147- if self .token :
148- headers .update ({"X-Auth-Token" : self .token })
149- async with session .delete (
150- uri ,
151- headers = headers ,
152- ssl = False ,
153- ) as _response :
154- raw_data = await _response .read ()
155- return _response
147+ _response = await self .session_manager .delete_request (uri , headers )
148+ return _response
156149 except (Exception , TimeoutError ):
157150 raise BadfishException ("Failed to communicate with server." )
158151
@@ -183,6 +176,8 @@ async def find_session_uri(self):
183176 return session_uri
184177
185178 async def validate_credentials (self ):
179+ await self ._ensure_session_manager ()
180+
186181 payload = {"UserName" : self .username , "Password" : self .password }
187182 headers = {"content-type" : "application/json" }
188183 session_uri = await self .find_session_uri ()
@@ -200,6 +195,10 @@ async def validate_credentials(self):
200195
201196 self .session_id = _response .headers .get ("Location" )
202197 token = _response .headers .get ("X-Auth-Token" )
198+
199+ # Cache token in session manager
200+ self .session_manager .cache_token (self .host , token , self .session_id , ttl_seconds = 3600 )
201+
203202 return token
204203
205204 async def delete_session (self ):
@@ -223,8 +222,13 @@ async def delete_session(self):
223222 except Exception as ex :
224223 self .logger .warning (f"Failed to delete session for { self .host } : { ex } " )
225224 finally :
225+ # Invalidate cached token in session manager
226+ if self .session_manager :
227+ self .session_manager .invalidate_token (self .host )
226228 self .session_id = None
227229 self .token = None
228230 except Exception :
231+ if self .session_manager :
232+ self .session_manager .invalidate_token (self .host )
229233 self .session_id = None
230234 self .token = None
0 commit comments