Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/client_exceptions.py: 57%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

149 statements  

1"""HTTP related errors.""" 

2 

3import asyncio 

4from collections.abc import Mapping 

5from typing import TYPE_CHECKING, Union 

6 

7from .typedefs import StrOrURL 

8 

9try: 

10 import ssl 

11 

12 SSLContext = ssl.SSLContext 

13except ImportError: # pragma: no cover 

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

15 

16if TYPE_CHECKING: 

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

18 from .http_parser import RawResponseMessage 

19else: 

20 RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None 

21 

22__all__ = ( 

23 "ClientError", 

24 "ClientConnectionError", 

25 "ClientConnectionResetError", 

26 "ClientOSError", 

27 "ClientConnectorError", 

28 "ClientProxyConnectionError", 

29 "ClientSSLError", 

30 "ClientConnectorDNSError", 

31 "ClientConnectorSSLError", 

32 "ClientConnectorCertificateError", 

33 "ConnectionTimeoutError", 

34 "SocketTimeoutError", 

35 "ServerConnectionError", 

36 "ServerTimeoutError", 

37 "ServerDisconnectedError", 

38 "ServerFingerprintMismatch", 

39 "ClientResponseError", 

40 "ClientHttpProxyError", 

41 "WSServerHandshakeError", 

42 "ContentTypeError", 

43 "ClientPayloadError", 

44 "InvalidURL", 

45 "InvalidUrlClientError", 

46 "RedirectClientError", 

47 "NonHttpUrlClientError", 

48 "InvalidUrlRedirectClientError", 

49 "NonHttpUrlRedirectClientError", 

50 "WSMessageTypeError", 

51) 

52 

53 

54class ClientError(Exception): 

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

56 

57 

58class ClientResponseError(ClientError): 

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

60 

61 request_info: An instance of RequestInfo. 

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

63 status: HTTP status code. 

64 message: Error message. 

65 headers: Response headers. 

66 """ 

67 

68 def __init__( 

69 self, 

70 request_info: RequestInfo, 

71 history: tuple[ClientResponse, ...], 

72 *, 

73 status: int | None = None, 

74 message: str = "", 

75 headers: Mapping[str, str] | None = None, 

76 ) -> None: 

77 self.request_info = request_info 

78 if status is not None: 

79 self.status = status 

80 else: 

81 self.status = 0 

82 self.message = message 

83 self.headers = headers 

84 self.history = history 

85 self.args = (request_info, history) 

86 

87 def __str__(self) -> str: 

88 return f"{self.status}, message={self.message!r}, url={str(self.request_info.real_url)!r}" 

89 

90 def __repr__(self) -> str: 

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

92 if self.status != 0: 

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

94 if self.message != "": 

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

96 if self.headers is not None: 

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

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

99 

100 

101class ContentTypeError(ClientResponseError): 

102 """ContentType found is not valid.""" 

103 

104 

105class WSServerHandshakeError(ClientResponseError): 

106 """websocket server handshake error.""" 

107 

108 

109class ClientHttpProxyError(ClientResponseError): 

110 """HTTP proxy error. 

111 

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

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

114 on ``CONNECT`` request. 

115 """ 

116 

117 

118class TooManyRedirects(ClientResponseError): 

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

120 

121 

122class ClientConnectionError(ClientError): 

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

124 

125 

126class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): 

127 """ConnectionResetError""" 

128 

129 

130class ClientOSError(ClientConnectionError, OSError): 

131 """OSError error.""" 

132 

133 

134class ClientConnectorError(ClientOSError): 

135 """Client connector error. 

136 

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

138 a connection can not be established. 

139 """ 

140 

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

142 self._conn_key = connection_key 

143 self._os_error = os_error 

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

145 self.args = (connection_key, os_error) 

146 

147 @property 

148 def os_error(self) -> OSError: 

149 return self._os_error 

150 

151 @property 

152 def host(self) -> str: 

153 return self._conn_key.host 

154 

155 @property 

156 def port(self) -> int | None: 

157 return self._conn_key.port 

158 

159 @property 

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

161 return self._conn_key.ssl 

162 

163 def __str__(self) -> str: 

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

165 self, "default" if self.ssl is True else self.ssl, self.strerror 

166 ) 

167 

168 # OSError.__reduce__ does too much black magick 

169 __reduce__ = BaseException.__reduce__ 

170 

171 

172class ClientConnectorDNSError(ClientConnectorError): 

173 """DNS resolution failed during client connection. 

174 

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

176 DNS resolution fails. 

177 """ 

178 

179 

180class ClientProxyConnectionError(ClientConnectorError): 

181 """Proxy connection error. 

182 

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

184 connection to proxy can not be established. 

185 """ 

186 

187 

188class UnixClientConnectorError(ClientConnectorError): 

189 """Unix connector error. 

190 

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

192 if connection to unix socket can not be established. 

193 """ 

194 

195 def __init__( 

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

197 ) -> None: 

198 self._path = path 

199 super().__init__(connection_key, os_error) 

200 

201 @property 

202 def path(self) -> str: 

203 return self._path 

204 

205 def __str__(self) -> str: 

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

207 self, "default" if self.ssl is True else self.ssl, self.strerror 

208 ) 

209 

210 

211class ServerConnectionError(ClientConnectionError): 

212 """Server connection errors.""" 

213 

214 

215class ServerDisconnectedError(ServerConnectionError): 

216 """Server disconnected.""" 

217 

218 def __init__(self, message: RawResponseMessage | str | None = None) -> None: 

219 if message is None: 

220 message = "Server disconnected" 

221 

222 self.args = (message,) 

223 self.message = message 

224 

225 

226class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 

227 """Server timeout error.""" 

228 

229 

230class ConnectionTimeoutError(ServerTimeoutError): 

231 """Connection timeout error.""" 

232 

233 

234class SocketTimeoutError(ServerTimeoutError): 

235 """Socket timeout error.""" 

236 

237 

238class ServerFingerprintMismatch(ServerConnectionError): 

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

240 

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

242 self.expected = expected 

243 self.got = got 

244 self.host = host 

245 self.port = port 

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

247 

248 def __repr__(self) -> str: 

249 return f"<{self.__class__.__name__} expected={self.expected!r} got={self.got!r} host={self.host!r} port={self.port!r}>" 

250 

251 

252class ClientPayloadError(ClientError): 

253 """Response payload error.""" 

254 

255 

256class InvalidURL(ClientError, ValueError): 

257 """Invalid URL. 

258 

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

260 part. 

261 """ 

262 

263 # Derive from ValueError for backward compatibility 

264 

265 def __init__(self, url: StrOrURL, description: str | None = None) -> None: 

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

267 # on URL(url) call 

268 self._url = url 

269 self._description = description 

270 

271 if description: 

272 super().__init__(url, description) 

273 else: 

274 super().__init__(url) 

275 

276 @property 

277 def url(self) -> StrOrURL: 

278 return self._url 

279 

280 @property 

281 def description(self) -> "str | None": 

282 return self._description 

283 

284 def __repr__(self) -> str: 

285 return f"<{self.__class__.__name__} {self}>" 

286 

287 def __str__(self) -> str: 

288 if self._description: 

289 return f"{self._url} - {self._description}" 

290 return str(self._url) 

291 

292 

293class InvalidUrlClientError(InvalidURL): 

294 """Invalid URL client error.""" 

295 

296 

297class RedirectClientError(ClientError): 

298 """Client redirect error.""" 

299 

300 

301class NonHttpUrlClientError(ClientError): 

302 """Non http URL client error.""" 

303 

304 

305class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): 

306 """Invalid URL redirect client error.""" 

307 

308 

309class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): 

310 """Non http URL redirect client error.""" 

311 

312 

313class ClientSSLError(ClientConnectorError): 

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

315 

316 

317if ssl is not None: 

318 cert_errors = (ssl.CertificateError,) 

319 cert_errors_bases = ( 

320 ClientSSLError, 

321 ssl.CertificateError, 

322 ) 

323 

324 ssl_errors = (ssl.SSLError,) 

325 ssl_error_bases = (ClientSSLError, ssl.SSLError) 

326else: # pragma: no cover 

327 cert_errors = tuple() # type: ignore[unreachable] 

328 cert_errors_bases = ( 

329 ClientSSLError, 

330 ValueError, 

331 ) 

332 

333 ssl_errors = tuple() 

334 ssl_error_bases = (ClientSSLError,) 

335 

336 

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

338 """Response ssl error.""" 

339 

340 

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

342 """Response certificate error.""" 

343 

344 _conn_key: ConnectionKey 

345 

346 def __init__( 

347 # TODO: If we require ssl in future, this can become ssl.CertificateError 

348 self, 

349 connection_key: ConnectionKey, 

350 certificate_error: Exception, 

351 ) -> None: 

352 if isinstance(certificate_error, cert_errors + (OSError,)): 

353 # ssl.CertificateError has errno and strerror, so we should be fine 

354 os_error = certificate_error 

355 else: 

356 os_error = OSError() 

357 

358 super().__init__(connection_key, os_error) 

359 self._certificate_error = certificate_error 

360 self.args = (connection_key, certificate_error) 

361 

362 @property 

363 def certificate_error(self) -> Exception: 

364 return self._certificate_error 

365 

366 @property 

367 def host(self) -> str: 

368 return self._conn_key.host 

369 

370 @property 

371 def port(self) -> int | None: 

372 return self._conn_key.port 

373 

374 @property 

375 def ssl(self) -> bool: 

376 return self._conn_key.is_ssl 

377 

378 def __str__(self) -> str: 

379 return ( 

380 f"Cannot connect to host {self.host}:{self.port} ssl:{self.ssl} " 

381 f"[{self.certificate_error.__class__.__name__}: " 

382 f"{self.certificate_error.args}]" 

383 ) 

384 

385 

386class WSMessageTypeError(TypeError): 

387 """WebSocket message type is not valid."""