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

153 statements  

1"""HTTP related errors.""" 

2 

3import asyncio 

4from collections.abc import Mapping 

5from typing import TYPE_CHECKING 

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 "UploadAbortedError", 

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

70 

71 def __init__( 

72 self, 

73 request_info: RequestInfo, 

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

75 *, 

76 status: int | None = None, 

77 message: str = "", 

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

79 ) -> None: 

80 self.request_info = request_info 

81 if status is not None: 

82 self.status = status 

83 else: 

84 self.status = 0 

85 self.message = message 

86 self.headers = headers 

87 self.history = history 

88 self.args = (request_info, history) 

89 

90 def __str__(self) -> str: 

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

92 

93 def __repr__(self) -> str: 

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

95 if self.status != 0: 

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

97 if self.message != "": 

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

99 if self.headers is not None: 

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

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

102 

103 

104class ContentTypeError(ClientResponseError): 

105 """ContentType found is not valid.""" 

106 

107 

108class WSServerHandshakeError(ClientResponseError): 

109 """websocket server handshake error.""" 

110 

111 

112class ClientHttpProxyError(ClientResponseError): 

113 """HTTP proxy error. 

114 

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

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

117 on ``CONNECT`` request. 

118 """ 

119 

120 

121class TooManyRedirects(ClientResponseError): 

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

123 

124 

125class ClientConnectionError(ClientError): 

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

127 

128 

129class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): 

130 """ConnectionResetError""" 

131 

132 

133class ClientOSError(ClientConnectionError, OSError): 

134 """OSError error.""" 

135 

136 

137class ClientConnectorError(ClientOSError): 

138 """Client connector error. 

139 

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

141 a connection can not be established. 

142 """ 

143 

144 args: tuple[ConnectionKey, OSError] 

145 

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

147 self._conn_key = connection_key 

148 self._os_error = os_error 

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

150 self.args = (connection_key, os_error) 

151 

152 @property 

153 def os_error(self) -> OSError: 

154 return self._os_error 

155 

156 @property 

157 def host(self) -> str: 

158 return self._conn_key.host 

159 

160 @property 

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

162 return self._conn_key.port 

163 

164 @property 

165 def ssl(self) -> "SSLContext | bool | Fingerprint": 

166 return self._conn_key.ssl 

167 

168 def __str__(self) -> str: 

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

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

171 ) 

172 

173 # OSError.__reduce__ does too much black magick 

174 __reduce__ = BaseException.__reduce__ 

175 

176 

177class ClientConnectorDNSError(ClientConnectorError): 

178 """DNS resolution failed during client connection. 

179 

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

181 DNS resolution fails. 

182 """ 

183 

184 

185class ClientProxyConnectionError(ClientConnectorError): 

186 """Proxy connection error. 

187 

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

189 connection to proxy can not be established. 

190 """ 

191 

192 

193class UnixClientConnectorError(ClientConnectorError): 

194 """Unix connector error. 

195 

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

197 if connection to unix socket can not be established. 

198 """ 

199 

200 def __init__( 

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

202 ) -> None: 

203 self._path = path 

204 super().__init__(connection_key, os_error) 

205 

206 @property 

207 def path(self) -> str: 

208 return self._path 

209 

210 def __str__(self) -> str: 

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

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

213 ) 

214 

215 

216class ServerConnectionError(ClientConnectionError): 

217 """Server connection errors.""" 

218 

219 

220class ServerDisconnectedError(ServerConnectionError): 

221 """Server disconnected.""" 

222 

223 args: tuple[RawResponseMessage | str] 

224 

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

226 if message is None: 

227 message = "Server disconnected" 

228 

229 self.args = (message,) 

230 self.message = message 

231 

232 

233class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 

234 """Server timeout error.""" 

235 

236 

237class ConnectionTimeoutError(ServerTimeoutError): 

238 """Connection timeout error.""" 

239 

240 

241class SocketTimeoutError(ServerTimeoutError): 

242 """Socket timeout error.""" 

243 

244 

245class ServerFingerprintMismatch(ServerConnectionError): 

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

247 

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

249 

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

251 self.expected = expected 

252 self.got = got 

253 self.host = host 

254 self.port = port 

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

256 

257 def __repr__(self) -> str: 

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

259 

260 

261class ClientPayloadError(ClientError): 

262 """Response payload error.""" 

263 

264 

265class UploadAbortedError(ClientError): 

266 """The request body was never fully sent.""" 

267 

268 

269class InvalidURL(ClientError, ValueError): 

270 """Invalid URL. 

271 

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

273 part. 

274 """ 

275 

276 # Derive from ValueError for backward compatibility 

277 

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

279 

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

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

282 # on URL(url) call 

283 self._url = url 

284 self._description = description 

285 

286 if description: 

287 super().__init__(url, description) 

288 else: 

289 super().__init__(url) 

290 

291 @property 

292 def url(self) -> StrOrURL: 

293 return self._url 

294 

295 @property 

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

297 return self._description 

298 

299 def __repr__(self) -> str: 

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

301 

302 def __str__(self) -> str: 

303 if self._description: 

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

305 return str(self._url) 

306 

307 

308class InvalidUrlClientError(InvalidURL): 

309 """Invalid URL client error.""" 

310 

311 

312class RedirectClientError(ClientError): 

313 """Client redirect error.""" 

314 

315 

316class NonHttpUrlClientError(ClientError): 

317 """Non http URL client error.""" 

318 

319 

320class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): 

321 """Invalid URL redirect client error.""" 

322 

323 

324class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): 

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

326 

327 

328class ClientSSLError(ClientConnectorError): 

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

330 

331 

332if ssl is not None: 

333 cert_errors = (ssl.CertificateError,) 

334 cert_errors_bases = ( 

335 ClientSSLError, 

336 ssl.CertificateError, 

337 ) 

338 

339 ssl_errors = (ssl.SSLError,) 

340 ssl_error_bases = (ClientSSLError, ssl.SSLError) 

341else: # pragma: no cover 

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

343 cert_errors_bases = ( 

344 ClientSSLError, 

345 ValueError, 

346 ) 

347 

348 ssl_errors = tuple() 

349 ssl_error_bases = (ClientSSLError,) 

350 

351 

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

353 """Response ssl error.""" 

354 

355 

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

357 """Response certificate error.""" 

358 

359 _conn_key: ConnectionKey 

360 args: tuple[ConnectionKey, Exception] 

361 

362 def __init__( 

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

364 self, 

365 connection_key: ConnectionKey, 

366 certificate_error: Exception, 

367 ) -> None: 

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

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

370 os_error = certificate_error 

371 else: 

372 os_error = OSError() 

373 

374 super().__init__(connection_key, os_error) 

375 self._certificate_error = certificate_error 

376 self.args = (connection_key, certificate_error) 

377 

378 @property 

379 def certificate_error(self) -> Exception: 

380 return self._certificate_error 

381 

382 @property 

383 def host(self) -> str: 

384 return self._conn_key.host 

385 

386 @property 

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

388 return self._conn_key.port 

389 

390 def __str__(self) -> str: 

391 return ( 

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

393 f"ssl:{'default' if self.ssl is True else self.ssl} " 

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

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

396 ) 

397 

398 

399class WSMessageTypeError(TypeError): 

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