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

124 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-26 06:16 +0000

1"""HTTP related errors.""" 

2 

3import asyncio 

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

5 

6from .http_parser import RawResponseMessage 

7from .typedefs import LooseHeaders 

8 

9try: 

10 import ssl 

11 

12 SSLContext = ssl.SSLContext 

13except ImportError: # pragma: no cover 

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

15 

16 

17if TYPE_CHECKING: 

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

19else: 

20 RequestInfo = ClientResponse = ConnectionKey = None 

21 

22__all__ = ( 

23 "ClientError", 

24 "ClientConnectionError", 

25 "ClientOSError", 

26 "ClientConnectorError", 

27 "ClientProxyConnectionError", 

28 "ClientSSLError", 

29 "ClientConnectorSSLError", 

30 "ClientConnectorCertificateError", 

31 "ConnectionTimeoutError", 

32 "SocketTimeoutError", 

33 "ServerConnectionError", 

34 "ServerTimeoutError", 

35 "ServerDisconnectedError", 

36 "ServerFingerprintMismatch", 

37 "ClientResponseError", 

38 "ClientHttpProxyError", 

39 "WSServerHandshakeError", 

40 "ContentTypeError", 

41 "ClientPayloadError", 

42 "InvalidURL", 

43) 

44 

45 

46class ClientError(Exception): 

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

48 

49 

50class ClientResponseError(ClientError): 

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

52 

53 request_info: An instance of RequestInfo. 

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

55 status: HTTP status code. 

56 message: Error message. 

57 headers: Response headers. 

58 """ 

59 

60 def __init__( 

61 self, 

62 request_info: RequestInfo, 

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

64 *, 

65 status: Optional[int] = None, 

66 message: str = "", 

67 headers: Optional[LooseHeaders] = None, 

68 ) -> None: 

69 self.request_info = request_info 

70 if status is not None: 

71 self.status = status 

72 else: 

73 self.status = 0 

74 self.message = message 

75 self.headers = headers 

76 self.history = history 

77 self.args = (request_info, history) 

78 

79 def __str__(self) -> str: 

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

81 self.status, 

82 self.message, 

83 self.request_info.real_url, 

84 ) 

85 

86 def __repr__(self) -> str: 

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

88 if self.status != 0: 

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

90 if self.message != "": 

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

92 if self.headers is not None: 

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

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

95 

96 

97class ContentTypeError(ClientResponseError): 

98 """ContentType found is not valid.""" 

99 

100 

101class WSServerHandshakeError(ClientResponseError): 

102 """websocket server handshake error.""" 

103 

104 

105class ClientHttpProxyError(ClientResponseError): 

106 """HTTP proxy error. 

107 

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

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

110 on ``CONNECT`` request. 

111 """ 

112 

113 

114class TooManyRedirects(ClientResponseError): 

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

116 

117 

118class ClientConnectionError(ClientError): 

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

120 

121 

122class ClientOSError(ClientConnectionError, OSError): 

123 """OSError error.""" 

124 

125 

126class ClientConnectorError(ClientOSError): 

127 """Client connector error. 

128 

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

130 a connection can not be established. 

131 """ 

132 

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

134 self._conn_key = connection_key 

135 self._os_error = os_error 

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

137 self.args = (connection_key, os_error) 

138 

139 @property 

140 def os_error(self) -> OSError: 

141 return self._os_error 

142 

143 @property 

144 def host(self) -> str: 

145 return self._conn_key.host 

146 

147 @property 

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

149 return self._conn_key.port 

150 

151 @property 

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

153 return self._conn_key.ssl 

154 

155 def __str__(self) -> str: 

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

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

158 ) 

159 

160 # OSError.__reduce__ does too much black magick 

161 __reduce__ = BaseException.__reduce__ 

162 

163 

164class ClientProxyConnectionError(ClientConnectorError): 

165 """Proxy connection error. 

166 

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

168 connection to proxy can not be established. 

169 """ 

170 

171 

172class UnixClientConnectorError(ClientConnectorError): 

173 """Unix connector error. 

174 

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

176 if connection to unix socket can not be established. 

177 """ 

178 

179 def __init__( 

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

181 ) -> None: 

182 self._path = path 

183 super().__init__(connection_key, os_error) 

184 

185 @property 

186 def path(self) -> str: 

187 return self._path 

188 

189 def __str__(self) -> str: 

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

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

192 ) 

193 

194 

195class ServerConnectionError(ClientConnectionError): 

196 """Server connection errors.""" 

197 

198 

199class ServerDisconnectedError(ServerConnectionError): 

200 """Server disconnected.""" 

201 

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

203 if message is None: 

204 message = "Server disconnected" 

205 

206 self.args = (message,) 

207 self.message = message 

208 

209 

210class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 

211 """Server timeout error.""" 

212 

213 

214class ConnectionTimeoutError(ServerTimeoutError): 

215 """Connection timeout error.""" 

216 

217 

218class SocketTimeoutError(ServerTimeoutError): 

219 """Socket timeout error.""" 

220 

221 

222class ServerFingerprintMismatch(ServerConnectionError): 

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

224 

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

226 self.expected = expected 

227 self.got = got 

228 self.host = host 

229 self.port = port 

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

231 

232 def __repr__(self) -> str: 

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

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

235 ) 

236 

237 

238class ClientPayloadError(ClientError): 

239 """Response payload error.""" 

240 

241 

242class InvalidURL(ClientError, ValueError): 

243 """Invalid URL. 

244 

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

246 part. 

247 """ 

248 

249 # Derive from ValueError for backward compatibility 

250 

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

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

253 # on URL(url) call 

254 super().__init__(url) 

255 

256 @property 

257 def url(self) -> Any: 

258 return self.args[0] 

259 

260 def __repr__(self) -> str: 

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

262 

263 

264class ClientSSLError(ClientConnectorError): 

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

266 

267 

268if ssl is not None: 

269 cert_errors = (ssl.CertificateError,) 

270 cert_errors_bases = ( 

271 ClientSSLError, 

272 ssl.CertificateError, 

273 ) 

274 

275 ssl_errors = (ssl.SSLError,) 

276 ssl_error_bases = (ClientSSLError, ssl.SSLError) 

277else: # pragma: no cover 

278 cert_errors = tuple() 

279 cert_errors_bases = ( 

280 ClientSSLError, 

281 ValueError, 

282 ) 

283 

284 ssl_errors = tuple() 

285 ssl_error_bases = (ClientSSLError,) 

286 

287 

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

289 """Response ssl error.""" 

290 

291 

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

293 """Response certificate error.""" 

294 

295 def __init__( 

296 self, connection_key: ConnectionKey, certificate_error: Exception 

297 ) -> None: 

298 self._conn_key = connection_key 

299 self._certificate_error = certificate_error 

300 self.args = (connection_key, certificate_error) 

301 

302 @property 

303 def certificate_error(self) -> Exception: 

304 return self._certificate_error 

305 

306 @property 

307 def host(self) -> str: 

308 return self._conn_key.host 

309 

310 @property 

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

312 return self._conn_key.port 

313 

314 @property 

315 def ssl(self) -> bool: 

316 return self._conn_key.is_ssl 

317 

318 def __str__(self) -> str: 

319 return ( 

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

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

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

323 )