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

120 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:52 +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: # pragma: no cover 

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

32 "ServerTimeoutError", 

33 "ServerDisconnectedError", 

34 "ServerFingerprintMismatch", 

35 "ClientResponseError", 

36 "ClientHttpProxyError", 

37 "WSServerHandshakeError", 

38 "ContentTypeError", 

39 "ClientPayloadError", 

40 "InvalidURL", 

41) 

42 

43 

44class ClientError(Exception): 

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

46 

47 

48class ClientResponseError(ClientError): 

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

50 

51 request_info: An instance of RequestInfo. 

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

53 status: HTTP status code. 

54 message: Error message. 

55 headers: Response headers. 

56 """ 

57 

58 def __init__( 

59 self, 

60 request_info: RequestInfo, 

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

62 *, 

63 status: Optional[int] = None, 

64 message: str = "", 

65 headers: Optional[LooseHeaders] = None, 

66 ) -> None: 

67 self.request_info = request_info 

68 if status is not None: 

69 self.status = status 

70 else: 

71 self.status = 0 

72 self.message = message 

73 self.headers = headers 

74 self.history = history 

75 self.args = (request_info, history) 

76 

77 def __str__(self) -> str: 

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

79 self.status, 

80 self.message, 

81 self.request_info.real_url, 

82 ) 

83 

84 def __repr__(self) -> str: 

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

86 if self.status != 0: 

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

88 if self.message != "": 

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

90 if self.headers is not None: 

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

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

93 

94 

95class ContentTypeError(ClientResponseError): 

96 """ContentType found is not valid.""" 

97 

98 

99class WSServerHandshakeError(ClientResponseError): 

100 """websocket server handshake error.""" 

101 

102 

103class ClientHttpProxyError(ClientResponseError): 

104 """HTTP proxy error. 

105 

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

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

108 on ``CONNECT`` request. 

109 """ 

110 

111 

112class TooManyRedirects(ClientResponseError): 

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

114 

115 

116class ClientConnectionError(ClientError): 

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

118 

119 

120class ClientOSError(ClientConnectionError, OSError): 

121 """OSError error.""" 

122 

123 

124class ClientConnectorError(ClientOSError): 

125 """Client connector error. 

126 

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

128 a connection can not be established. 

129 """ 

130 

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

132 self._conn_key = connection_key 

133 self._os_error = os_error 

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

135 self.args = (connection_key, os_error) 

136 

137 @property 

138 def os_error(self) -> OSError: 

139 return self._os_error 

140 

141 @property 

142 def host(self) -> str: 

143 return self._conn_key.host 

144 

145 @property 

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

147 return self._conn_key.port 

148 

149 @property 

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

151 return self._conn_key.ssl 

152 

153 def __str__(self) -> str: 

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

155 self, self.ssl if self.ssl is not None else "default", self.strerror 

156 ) 

157 

158 # OSError.__reduce__ does too much black magick 

159 __reduce__ = BaseException.__reduce__ 

160 

161 

162class ClientProxyConnectionError(ClientConnectorError): 

163 """Proxy connection error. 

164 

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

166 connection to proxy can not be established. 

167 """ 

168 

169 

170class UnixClientConnectorError(ClientConnectorError): 

171 """Unix connector error. 

172 

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

174 if connection to unix socket can not be established. 

175 """ 

176 

177 def __init__( 

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

179 ) -> None: 

180 self._path = path 

181 super().__init__(connection_key, os_error) 

182 

183 @property 

184 def path(self) -> str: 

185 return self._path 

186 

187 def __str__(self) -> str: 

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

189 self, self.ssl if self.ssl is not None else "default", self.strerror 

190 ) 

191 

192 

193class ServerConnectionError(ClientConnectionError): 

194 """Server connection errors.""" 

195 

196 

197class ServerDisconnectedError(ServerConnectionError): 

198 """Server disconnected.""" 

199 

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

201 if message is None: 

202 message = "Server disconnected" 

203 

204 self.args = (message,) 

205 self.message = message 

206 

207 

208class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 

209 """Server timeout error.""" 

210 

211 

212class ServerFingerprintMismatch(ServerConnectionError): 

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

214 

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

216 self.expected = expected 

217 self.got = got 

218 self.host = host 

219 self.port = port 

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

221 

222 def __repr__(self) -> str: 

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

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

225 ) 

226 

227 

228class ClientPayloadError(ClientError): 

229 """Response payload error.""" 

230 

231 

232class InvalidURL(ClientError, ValueError): 

233 """Invalid URL. 

234 

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

236 part. 

237 """ 

238 

239 # Derive from ValueError for backward compatibility 

240 

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

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

243 # on URL(url) call 

244 super().__init__(url) 

245 

246 @property 

247 def url(self) -> Any: 

248 return self.args[0] 

249 

250 def __repr__(self) -> str: 

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

252 

253 

254class ClientSSLError(ClientConnectorError): 

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

256 

257 

258if ssl is not None: 

259 cert_errors = (ssl.CertificateError,) 

260 cert_errors_bases = ( 

261 ClientSSLError, 

262 ssl.CertificateError, 

263 ) 

264 

265 ssl_errors = (ssl.SSLError,) 

266 ssl_error_bases = (ClientSSLError, ssl.SSLError) 

267else: # pragma: no cover 

268 cert_errors = tuple() 

269 cert_errors_bases = ( 

270 ClientSSLError, 

271 ValueError, 

272 ) 

273 

274 ssl_errors = tuple() 

275 ssl_error_bases = (ClientSSLError,) 

276 

277 

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

279 """Response ssl error.""" 

280 

281 

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

283 """Response certificate error.""" 

284 

285 def __init__( 

286 self, connection_key: ConnectionKey, certificate_error: Exception 

287 ) -> None: 

288 self._conn_key = connection_key 

289 self._certificate_error = certificate_error 

290 self.args = (connection_key, certificate_error) 

291 

292 @property 

293 def certificate_error(self) -> Exception: 

294 return self._certificate_error 

295 

296 @property 

297 def host(self) -> str: 

298 return self._conn_key.host 

299 

300 @property 

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

302 return self._conn_key.port 

303 

304 @property 

305 def ssl(self) -> bool: 

306 return self._conn_key.is_ssl 

307 

308 def __str__(self) -> str: 

309 return ( 

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

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

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

313 )