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

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

155 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 args: tuple[RequestInfo, tuple[ClientResponse, ...]] 

69 

70 def __init__( 

71 self, 

72 request_info: RequestInfo, 

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

74 *, 

75 status: int | None = None, 

76 message: str = "", 

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

78 ) -> None: 

79 self.request_info = request_info 

80 if status is not None: 

81 self.status = status 

82 else: 

83 self.status = 0 

84 self.message = message 

85 self.headers = headers 

86 self.history = history 

87 self.args = (request_info, history) 

88 

89 def __str__(self) -> str: 

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

91 

92 def __repr__(self) -> str: 

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

94 if self.status != 0: 

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

96 if self.message != "": 

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

98 if self.headers is not None: 

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

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

101 

102 

103class ContentTypeError(ClientResponseError): 

104 """ContentType found is not valid.""" 

105 

106 

107class WSServerHandshakeError(ClientResponseError): 

108 """websocket server handshake error.""" 

109 

110 

111class ClientHttpProxyError(ClientResponseError): 

112 """HTTP proxy error. 

113 

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

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

116 on ``CONNECT`` request. 

117 """ 

118 

119 

120class TooManyRedirects(ClientResponseError): 

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

122 

123 

124class ClientConnectionError(ClientError): 

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

126 

127 

128class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): 

129 """ConnectionResetError""" 

130 

131 

132class ClientOSError(ClientConnectionError, OSError): 

133 """OSError error.""" 

134 

135 

136class ClientConnectorError(ClientOSError): 

137 """Client connector error. 

138 

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

140 a connection can not be established. 

141 """ 

142 

143 args: tuple[ConnectionKey, OSError] 

144 

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

146 self._conn_key = connection_key 

147 self._os_error = os_error 

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

149 self.args = (connection_key, os_error) 

150 

151 @property 

152 def os_error(self) -> OSError: 

153 return self._os_error 

154 

155 @property 

156 def host(self) -> str: 

157 return self._conn_key.host 

158 

159 @property 

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

161 return self._conn_key.port 

162 

163 @property 

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

165 return self._conn_key.ssl 

166 

167 def __str__(self) -> str: 

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

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

170 ) 

171 

172 # OSError.__reduce__ does too much black magick 

173 __reduce__ = BaseException.__reduce__ 

174 

175 

176class ClientConnectorDNSError(ClientConnectorError): 

177 """DNS resolution failed during client connection. 

178 

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

180 DNS resolution fails. 

181 """ 

182 

183 

184class ClientProxyConnectionError(ClientConnectorError): 

185 """Proxy connection error. 

186 

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

188 connection to proxy can not be established. 

189 """ 

190 

191 

192class UnixClientConnectorError(ClientConnectorError): 

193 """Unix connector error. 

194 

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

196 if connection to unix socket can not be established. 

197 """ 

198 

199 def __init__( 

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

201 ) -> None: 

202 self._path = path 

203 super().__init__(connection_key, os_error) 

204 

205 @property 

206 def path(self) -> str: 

207 return self._path 

208 

209 def __str__(self) -> str: 

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

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

212 ) 

213 

214 

215class ServerConnectionError(ClientConnectionError): 

216 """Server connection errors.""" 

217 

218 

219class ServerDisconnectedError(ServerConnectionError): 

220 """Server disconnected.""" 

221 

222 args: tuple[RawResponseMessage | str] 

223 

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

225 if message is None: 

226 message = "Server disconnected" 

227 

228 self.args = (message,) 

229 self.message = message 

230 

231 

232class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 

233 """Server timeout error.""" 

234 

235 

236class ConnectionTimeoutError(ServerTimeoutError): 

237 """Connection timeout error.""" 

238 

239 

240class SocketTimeoutError(ServerTimeoutError): 

241 """Socket timeout error.""" 

242 

243 

244class ServerFingerprintMismatch(ServerConnectionError): 

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

246 

247 args: tuple[bytes, bytes, str, int] 

248 

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

250 self.expected = expected 

251 self.got = got 

252 self.host = host 

253 self.port = port 

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

255 

256 def __repr__(self) -> str: 

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

258 

259 

260class ClientPayloadError(ClientError): 

261 """Response payload error.""" 

262 

263 

264class InvalidURL(ClientError, ValueError): 

265 """Invalid URL. 

266 

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

268 part. 

269 """ 

270 

271 # Derive from ValueError for backward compatibility 

272 

273 args: tuple[StrOrURL] | tuple[StrOrURL, str] 

274 

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

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

277 # on URL(url) call 

278 self._url = url 

279 self._description = description 

280 

281 if description: 

282 super().__init__(url, description) 

283 else: 

284 super().__init__(url) 

285 

286 @property 

287 def url(self) -> StrOrURL: 

288 return self._url 

289 

290 @property 

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

292 return self._description 

293 

294 def __repr__(self) -> str: 

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

296 

297 def __str__(self) -> str: 

298 if self._description: 

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

300 return str(self._url) 

301 

302 

303class InvalidUrlClientError(InvalidURL): 

304 """Invalid URL client error.""" 

305 

306 

307class RedirectClientError(ClientError): 

308 """Client redirect error.""" 

309 

310 

311class NonHttpUrlClientError(ClientError): 

312 """Non http URL client error.""" 

313 

314 

315class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): 

316 """Invalid URL redirect client error.""" 

317 

318 

319class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): 

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

321 

322 

323class ClientSSLError(ClientConnectorError): 

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

325 

326 

327if ssl is not None: 

328 cert_errors = (ssl.CertificateError,) 

329 cert_errors_bases = ( 

330 ClientSSLError, 

331 ssl.CertificateError, 

332 ) 

333 

334 ssl_errors = (ssl.SSLError,) 

335 ssl_error_bases = (ClientSSLError, ssl.SSLError) 

336else: # pragma: no cover 

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

338 cert_errors_bases = ( 

339 ClientSSLError, 

340 ValueError, 

341 ) 

342 

343 ssl_errors = tuple() 

344 ssl_error_bases = (ClientSSLError,) 

345 

346 

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

348 """Response ssl error.""" 

349 

350 

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

352 """Response certificate error.""" 

353 

354 _conn_key: ConnectionKey 

355 args: tuple[ConnectionKey, Exception] 

356 

357 def __init__( 

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

359 self, 

360 connection_key: ConnectionKey, 

361 certificate_error: Exception, 

362 ) -> None: 

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

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

365 os_error = certificate_error 

366 else: 

367 os_error = OSError() 

368 

369 super().__init__(connection_key, os_error) 

370 self._certificate_error = certificate_error 

371 self.args = (connection_key, certificate_error) 

372 

373 @property 

374 def certificate_error(self) -> Exception: 

375 return self._certificate_error 

376 

377 @property 

378 def host(self) -> str: 

379 return self._conn_key.host 

380 

381 @property 

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

383 return self._conn_key.port 

384 

385 @property 

386 def ssl(self) -> bool: 

387 return self._conn_key.is_ssl 

388 

389 def __str__(self) -> str: 

390 return ( 

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

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

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

394 ) 

395 

396 

397class WSMessageTypeError(TypeError): 

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