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 typing import TYPE_CHECKING, Union 

5 

6from multidict import MultiMapping 

7 

8from .typedefs import StrOrURL 

9 

10try: 

11 import ssl 

12 

13 SSLContext = ssl.SSLContext 

14except ImportError: # pragma: no cover 

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

16 

17if TYPE_CHECKING: 

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

19 from .http_parser import RawResponseMessage 

20else: 

21 RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None 

22 

23__all__ = ( 

24 "ClientError", 

25 "ClientConnectionError", 

26 "ClientConnectionResetError", 

27 "ClientOSError", 

28 "ClientConnectorError", 

29 "ClientProxyConnectionError", 

30 "ClientSSLError", 

31 "ClientConnectorDNSError", 

32 "ClientConnectorSSLError", 

33 "ClientConnectorCertificateError", 

34 "ConnectionTimeoutError", 

35 "SocketTimeoutError", 

36 "ServerConnectionError", 

37 "ServerTimeoutError", 

38 "ServerDisconnectedError", 

39 "ServerFingerprintMismatch", 

40 "ClientResponseError", 

41 "ClientHttpProxyError", 

42 "WSServerHandshakeError", 

43 "ContentTypeError", 

44 "ClientPayloadError", 

45 "InvalidURL", 

46 "InvalidUrlClientError", 

47 "RedirectClientError", 

48 "NonHttpUrlClientError", 

49 "InvalidUrlRedirectClientError", 

50 "NonHttpUrlRedirectClientError", 

51 "WSMessageTypeError", 

52) 

53 

54 

55class ClientError(Exception): 

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

57 

58 

59class ClientResponseError(ClientError): 

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

61 

62 request_info: An instance of RequestInfo. 

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

64 status: HTTP status code. 

65 message: Error message. 

66 headers: Response headers. 

67 """ 

68 

69 def __init__( 

70 self, 

71 request_info: RequestInfo, 

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

73 *, 

74 status: int | None = None, 

75 message: str = "", 

76 headers: MultiMapping[str] | None = None, 

77 ) -> None: 

78 self.request_info = request_info 

79 if status is not None: 

80 self.status = status 

81 else: 

82 self.status = 0 

83 self.message = message 

84 self.headers = headers 

85 self.history = history 

86 self.args = (request_info, history) 

87 

88 def __str__(self) -> str: 

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

90 

91 def __repr__(self) -> str: 

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

93 if self.status != 0: 

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

95 if self.message != "": 

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

97 if self.headers is not None: 

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

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

100 

101 

102class ContentTypeError(ClientResponseError): 

103 """ContentType found is not valid.""" 

104 

105 

106class WSServerHandshakeError(ClientResponseError): 

107 """websocket server handshake error.""" 

108 

109 

110class ClientHttpProxyError(ClientResponseError): 

111 """HTTP proxy error. 

112 

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

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

115 on ``CONNECT`` request. 

116 """ 

117 

118 

119class TooManyRedirects(ClientResponseError): 

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

121 

122 

123class ClientConnectionError(ClientError): 

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

125 

126 

127class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): 

128 """ConnectionResetError""" 

129 

130 

131class ClientOSError(ClientConnectionError, OSError): 

132 """OSError error.""" 

133 

134 

135class ClientConnectorError(ClientOSError): 

136 """Client connector error. 

137 

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

139 a connection can not be established. 

140 """ 

141 

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

143 self._conn_key = connection_key 

144 self._os_error = os_error 

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

146 self.args = (connection_key, os_error) 

147 

148 @property 

149 def os_error(self) -> OSError: 

150 return self._os_error 

151 

152 @property 

153 def host(self) -> str: 

154 return self._conn_key.host 

155 

156 @property 

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

158 return self._conn_key.port 

159 

160 @property 

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

162 return self._conn_key.ssl 

163 

164 def __str__(self) -> str: 

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

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

167 ) 

168 

169 # OSError.__reduce__ does too much black magick 

170 __reduce__ = BaseException.__reduce__ 

171 

172 

173class ClientConnectorDNSError(ClientConnectorError): 

174 """DNS resolution failed during client connection. 

175 

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

177 DNS resolution fails. 

178 """ 

179 

180 

181class ClientProxyConnectionError(ClientConnectorError): 

182 """Proxy connection error. 

183 

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

185 connection to proxy can not be established. 

186 """ 

187 

188 

189class UnixClientConnectorError(ClientConnectorError): 

190 """Unix connector error. 

191 

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

193 if connection to unix socket can not be established. 

194 """ 

195 

196 def __init__( 

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

198 ) -> None: 

199 self._path = path 

200 super().__init__(connection_key, os_error) 

201 

202 @property 

203 def path(self) -> str: 

204 return self._path 

205 

206 def __str__(self) -> str: 

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

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

209 ) 

210 

211 

212class ServerConnectionError(ClientConnectionError): 

213 """Server connection errors.""" 

214 

215 

216class ServerDisconnectedError(ServerConnectionError): 

217 """Server disconnected.""" 

218 

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

220 if message is None: 

221 message = "Server disconnected" 

222 

223 self.args = (message,) 

224 self.message = message 

225 

226 

227class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 

228 """Server timeout error.""" 

229 

230 

231class ConnectionTimeoutError(ServerTimeoutError): 

232 """Connection timeout error.""" 

233 

234 

235class SocketTimeoutError(ServerTimeoutError): 

236 """Socket timeout error.""" 

237 

238 

239class ServerFingerprintMismatch(ServerConnectionError): 

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

241 

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

243 self.expected = expected 

244 self.got = got 

245 self.host = host 

246 self.port = port 

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

248 

249 def __repr__(self) -> str: 

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

251 

252 

253class ClientPayloadError(ClientError): 

254 """Response payload error.""" 

255 

256 

257class InvalidURL(ClientError, ValueError): 

258 """Invalid URL. 

259 

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

261 part. 

262 """ 

263 

264 # Derive from ValueError for backward compatibility 

265 

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

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

268 # on URL(url) call 

269 self._url = url 

270 self._description = description 

271 

272 if description: 

273 super().__init__(url, description) 

274 else: 

275 super().__init__(url) 

276 

277 @property 

278 def url(self) -> StrOrURL: 

279 return self._url 

280 

281 @property 

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

283 return self._description 

284 

285 def __repr__(self) -> str: 

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

287 

288 def __str__(self) -> str: 

289 if self._description: 

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

291 return str(self._url) 

292 

293 

294class InvalidUrlClientError(InvalidURL): 

295 """Invalid URL client error.""" 

296 

297 

298class RedirectClientError(ClientError): 

299 """Client redirect error.""" 

300 

301 

302class NonHttpUrlClientError(ClientError): 

303 """Non http URL client error.""" 

304 

305 

306class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): 

307 """Invalid URL redirect client error.""" 

308 

309 

310class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): 

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

312 

313 

314class ClientSSLError(ClientConnectorError): 

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

316 

317 

318if ssl is not None: 

319 cert_errors = (ssl.CertificateError,) 

320 cert_errors_bases = ( 

321 ClientSSLError, 

322 ssl.CertificateError, 

323 ) 

324 

325 ssl_errors = (ssl.SSLError,) 

326 ssl_error_bases = (ClientSSLError, ssl.SSLError) 

327else: # pragma: no cover 

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

329 cert_errors_bases = ( 

330 ClientSSLError, 

331 ValueError, 

332 ) 

333 

334 ssl_errors = tuple() 

335 ssl_error_bases = (ClientSSLError,) 

336 

337 

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

339 """Response ssl error.""" 

340 

341 

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

343 """Response certificate error.""" 

344 

345 _conn_key: ConnectionKey 

346 

347 def __init__( 

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

349 self, 

350 connection_key: ConnectionKey, 

351 certificate_error: Exception, 

352 ) -> None: 

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

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

355 os_error = certificate_error 

356 else: 

357 os_error = OSError() 

358 

359 super().__init__(connection_key, os_error) 

360 self._certificate_error = certificate_error 

361 self.args = (connection_key, certificate_error) 

362 

363 @property 

364 def certificate_error(self) -> Exception: 

365 return self._certificate_error 

366 

367 @property 

368 def host(self) -> str: 

369 return self._conn_key.host 

370 

371 @property 

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

373 return self._conn_key.port 

374 

375 @property 

376 def ssl(self) -> bool: 

377 return self._conn_key.is_ssl 

378 

379 def __str__(self) -> str: 

380 return ( 

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

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

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

384 ) 

385 

386 

387class WSMessageTypeError(TypeError): 

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