Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/aiohttp/client_exceptions.py: 56%

135 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:40 +0000

1"""HTTP related errors.""" 

2 

3import asyncio 

4import warnings 

5from typing import TYPE_CHECKING, Any, Optional, Tuple, Union 

6 

7from .http_parser import RawResponseMessage 

8from .typedefs import LooseHeaders 

9 

10try: 

11 import ssl 

12 

13 SSLContext = ssl.SSLContext 

14except ImportError: # pragma: no cover 

15 ssl = SSLContext = None # type: ignore[assignment] 

16 

17 

18if TYPE_CHECKING: # pragma: no cover 

19 from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo 

20else: 

21 RequestInfo = ClientResponse = ConnectionKey = None 

22 

23__all__ = ( 

24 "ClientError", 

25 "ClientConnectionError", 

26 "ClientOSError", 

27 "ClientConnectorError", 

28 "ClientProxyConnectionError", 

29 "ClientSSLError", 

30 "ClientConnectorSSLError", 

31 "ClientConnectorCertificateError", 

32 "ServerConnectionError", 

33 "ServerTimeoutError", 

34 "ServerDisconnectedError", 

35 "ServerFingerprintMismatch", 

36 "ClientResponseError", 

37 "ClientHttpProxyError", 

38 "WSServerHandshakeError", 

39 "ContentTypeError", 

40 "ClientPayloadError", 

41 "InvalidURL", 

42) 

43 

44 

45class ClientError(Exception): 

46 """Base class for client connection errors.""" 

47 

48 

49class ClientResponseError(ClientError): 

50 """Base class for exceptions that occur after getting a response. 

51 

52 request_info: An instance of RequestInfo. 

53 history: A sequence of responses, if redirects occurred. 

54 status: HTTP status code. 

55 message: Error message. 

56 headers: Response headers. 

57 """ 

58 

59 def __init__( 

60 self, 

61 request_info: RequestInfo, 

62 history: Tuple[ClientResponse, ...], 

63 *, 

64 code: Optional[int] = None, 

65 status: Optional[int] = None, 

66 message: str = "", 

67 headers: Optional[LooseHeaders] = None, 

68 ) -> None: 

69 self.request_info = request_info 

70 if code is not None: 

71 if status is not None: 

72 raise ValueError( 

73 "Both code and status arguments are provided; " 

74 "code is deprecated, use status instead" 

75 ) 

76 warnings.warn( 

77 "code argument is deprecated, use status instead", 

78 DeprecationWarning, 

79 stacklevel=2, 

80 ) 

81 if status is not None: 

82 self.status = status 

83 elif code is not None: 

84 self.status = code 

85 else: 

86 self.status = 0 

87 self.message = message 

88 self.headers = headers 

89 self.history = history 

90 self.args = (request_info, history) 

91 

92 def __str__(self) -> str: 

93 return "{}, message={!r}, url={!r}".format( 

94 self.status, 

95 self.message, 

96 self.request_info.real_url, 

97 ) 

98 

99 def __repr__(self) -> str: 

100 args = f"{self.request_info!r}, {self.history!r}" 

101 if self.status != 0: 

102 args += f", status={self.status!r}" 

103 if self.message != "": 

104 args += f", message={self.message!r}" 

105 if self.headers is not None: 

106 args += f", headers={self.headers!r}" 

107 return f"{type(self).__name__}({args})" 

108 

109 @property 

110 def code(self) -> int: 

111 warnings.warn( 

112 "code property is deprecated, use status instead", 

113 DeprecationWarning, 

114 stacklevel=2, 

115 ) 

116 return self.status 

117 

118 @code.setter 

119 def code(self, value: int) -> None: 

120 warnings.warn( 

121 "code property is deprecated, use status instead", 

122 DeprecationWarning, 

123 stacklevel=2, 

124 ) 

125 self.status = value 

126 

127 

128class ContentTypeError(ClientResponseError): 

129 """ContentType found is not valid.""" 

130 

131 

132class WSServerHandshakeError(ClientResponseError): 

133 """websocket server handshake error.""" 

134 

135 

136class ClientHttpProxyError(ClientResponseError): 

137 """HTTP proxy error. 

138 

139 Raised in :class:`aiohttp.connector.TCPConnector` if 

140 proxy responds with status other than ``200 OK`` 

141 on ``CONNECT`` request. 

142 """ 

143 

144 

145class TooManyRedirects(ClientResponseError): 

146 """Client was redirected too many times.""" 

147 

148 

149class ClientConnectionError(ClientError): 

150 """Base class for client socket errors.""" 

151 

152 

153class ClientOSError(ClientConnectionError, OSError): 

154 """OSError error.""" 

155 

156 

157class ClientConnectorError(ClientOSError): 

158 """Client connector error. 

159 

160 Raised in :class:`aiohttp.connector.TCPConnector` if 

161 a connection can not be established. 

162 """ 

163 

164 def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None: 

165 self._conn_key = connection_key 

166 self._os_error = os_error 

167 super().__init__(os_error.errno, os_error.strerror) 

168 self.args = (connection_key, os_error) 

169 

170 @property 

171 def os_error(self) -> OSError: 

172 return self._os_error 

173 

174 @property 

175 def host(self) -> str: 

176 return self._conn_key.host 

177 

178 @property 

179 def port(self) -> Optional[int]: 

180 return self._conn_key.port 

181 

182 @property 

183 def ssl(self) -> Union[SSLContext, None, bool, "Fingerprint"]: 

184 return self._conn_key.ssl 

185 

186 def __str__(self) -> str: 

187 return "Cannot connect to host {0.host}:{0.port} ssl:{1} [{2}]".format( 

188 self, self.ssl if self.ssl is not None else "default", self.strerror 

189 ) 

190 

191 # OSError.__reduce__ does too much black magick 

192 __reduce__ = BaseException.__reduce__ 

193 

194 

195class ClientProxyConnectionError(ClientConnectorError): 

196 """Proxy connection error. 

197 

198 Raised in :class:`aiohttp.connector.TCPConnector` if 

199 connection to proxy can not be established. 

200 """ 

201 

202 

203class UnixClientConnectorError(ClientConnectorError): 

204 """Unix connector error. 

205 

206 Raised in :py:class:`aiohttp.connector.UnixConnector` 

207 if connection to unix socket can not be established. 

208 """ 

209 

210 def __init__( 

211 self, path: str, connection_key: ConnectionKey, os_error: OSError 

212 ) -> None: 

213 self._path = path 

214 super().__init__(connection_key, os_error) 

215 

216 @property 

217 def path(self) -> str: 

218 return self._path 

219 

220 def __str__(self) -> str: 

221 return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format( 

222 self, self.ssl if self.ssl is not None else "default", self.strerror 

223 ) 

224 

225 

226class ServerConnectionError(ClientConnectionError): 

227 """Server connection errors.""" 

228 

229 

230class ServerDisconnectedError(ServerConnectionError): 

231 """Server disconnected.""" 

232 

233 def __init__(self, message: Union[RawResponseMessage, str, None] = None) -> None: 

234 if message is None: 

235 message = "Server disconnected" 

236 

237 self.args = (message,) 

238 self.message = message 

239 

240 

241class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 

242 """Server timeout error.""" 

243 

244 

245class ServerFingerprintMismatch(ServerConnectionError): 

246 """SSL certificate does not match expected fingerprint.""" 

247 

248 def __init__(self, expected: bytes, got: bytes, host: str, port: int) -> None: 

249 self.expected = expected 

250 self.got = got 

251 self.host = host 

252 self.port = port 

253 self.args = (expected, got, host, port) 

254 

255 def __repr__(self) -> str: 

256 return "<{} expected={!r} got={!r} host={!r} port={!r}>".format( 

257 self.__class__.__name__, self.expected, self.got, self.host, self.port 

258 ) 

259 

260 

261class ClientPayloadError(ClientError): 

262 """Response payload error.""" 

263 

264 

265class InvalidURL(ClientError, ValueError): 

266 """Invalid URL. 

267 

268 URL used for fetching is malformed, e.g. it doesn't contains host 

269 part. 

270 """ 

271 

272 # Derive from ValueError for backward compatibility 

273 

274 def __init__(self, url: Any) -> None: 

275 # The type of url is not yarl.URL because the exception can be raised 

276 # on URL(url) call 

277 super().__init__(url) 

278 

279 @property 

280 def url(self) -> Any: 

281 return self.args[0] 

282 

283 def __repr__(self) -> str: 

284 return f"<{self.__class__.__name__} {self.url}>" 

285 

286 

287class ClientSSLError(ClientConnectorError): 

288 """Base error for ssl.*Errors.""" 

289 

290 

291if ssl is not None: 

292 cert_errors = (ssl.CertificateError,) 

293 cert_errors_bases = ( 

294 ClientSSLError, 

295 ssl.CertificateError, 

296 ) 

297 

298 ssl_errors = (ssl.SSLError,) 

299 ssl_error_bases = (ClientSSLError, ssl.SSLError) 

300else: # pragma: no cover 

301 cert_errors = tuple() 

302 cert_errors_bases = ( 

303 ClientSSLError, 

304 ValueError, 

305 ) 

306 

307 ssl_errors = tuple() 

308 ssl_error_bases = (ClientSSLError,) 

309 

310 

311class ClientConnectorSSLError(*ssl_error_bases): # type: ignore[misc] 

312 """Response ssl error.""" 

313 

314 

315class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc] 

316 """Response certificate error.""" 

317 

318 def __init__( 

319 self, connection_key: ConnectionKey, certificate_error: Exception 

320 ) -> None: 

321 self._conn_key = connection_key 

322 self._certificate_error = certificate_error 

323 self.args = (connection_key, certificate_error) 

324 

325 @property 

326 def certificate_error(self) -> Exception: 

327 return self._certificate_error 

328 

329 @property 

330 def host(self) -> str: 

331 return self._conn_key.host 

332 

333 @property 

334 def port(self) -> Optional[int]: 

335 return self._conn_key.port 

336 

337 @property 

338 def ssl(self) -> bool: 

339 return self._conn_key.is_ssl 

340 

341 def __str__(self) -> str: 

342 return ( 

343 "Cannot connect to host {0.host}:{0.port} ssl:{0.ssl} " 

344 "[{0.certificate_error.__class__.__name__}: " 

345 "{0.certificate_error.args}]".format(self) 

346 )