codekingpro/portable-devtools
115k
1import warnings
2from typing import Any, Dict, Iterable, List, Optional, Set # noqa
3
4from yarl import URL
5
6from .typedefs import LooseHeaders, StrOrURL
7from .web_response import Response
8
9__all__ = (
10 "HTTPException",
11 "HTTPError",
12 "HTTPRedirection",
13 "HTTPSuccessful",
14 "HTTPOk",
15 "HTTPCreated",
16 "HTTPAccepted",
17 "HTTPNonAuthoritativeInformation",
18 "HTTPNoContent",
19 "HTTPResetContent",
20 "HTTPPartialContent",
21 "HTTPMove",
22 "HTTPMultipleChoices",
23 "HTTPMovedPermanently",
24 "HTTPFound",
25 "HTTPSeeOther",
26 "HTTPNotModified",
27 "HTTPUseProxy",
28 "HTTPTemporaryRedirect",
29 "HTTPPermanentRedirect",
30 "HTTPClientError",
31 "HTTPBadRequest",
32 "HTTPUnauthorized",
33 "HTTPPaymentRequired",
34 "HTTPForbidden",
35 "HTTPNotFound",
36 "HTTPMethodNotAllowed",
37 "HTTPNotAcceptable",
38 "HTTPProxyAuthenticationRequired",
39 "HTTPRequestTimeout",
40 "HTTPConflict",
41 "HTTPGone",
42 "HTTPLengthRequired",
43 "HTTPPreconditionFailed",
44 "HTTPRequestEntityTooLarge",
45 "HTTPRequestURITooLong",
46 "HTTPUnsupportedMediaType",
47 "HTTPRequestRangeNotSatisfiable",
48 "HTTPExpectationFailed",
49 "HTTPMisdirectedRequest",
50 "HTTPUnprocessableEntity",
51 "HTTPFailedDependency",
52 "HTTPUpgradeRequired",
53 "HTTPPreconditionRequired",
54 "HTTPTooManyRequests",
55 "HTTPRequestHeaderFieldsTooLarge",
56 "HTTPUnavailableForLegalReasons",
57 "HTTPServerError",
58 "HTTPInternalServerError",
59 "HTTPNotImplemented",
60 "HTTPBadGateway",
61 "HTTPServiceUnavailable",
62 "HTTPGatewayTimeout",
63 "HTTPVersionNotSupported",
64 "HTTPVariantAlsoNegotiates",
65 "HTTPInsufficientStorage",
66 "HTTPNotExtended",
67 "HTTPNetworkAuthenticationRequired",
68)
69
70
71class NotAppKeyWarning(UserWarning):
72 """Warning when not using AppKey in Application."""
73
74
75############################################################
76# HTTP Exceptions
77############################################################
78
79
80class HTTPException(Response, Exception):
81
82 # You should set in subclasses:
83 # status = 200
84
85 status_code = -1
86 empty_body = False
87
88 __http_exception__ = True
89
90 def __init__(
91 self,
92 *,
93 headers: Optional[LooseHeaders] = None,
94 reason: Optional[str] = None,
95 body: Any = None,
96 text: Optional[str] = None,
97 content_type: Optional[str] = None,
98 ) -> None:
99 if body is not None:
100 warnings.warn(
101 "body argument is deprecated for http web exceptions",
102 DeprecationWarning,
103 )
104 if reason is not None and ("\r" in reason or "\n" in reason):
105 raise ValueError("Reason cannot contain \\r or \\n")
106 Response.__init__(
107 self,
108 status=self.status_code,
109 headers=headers,
110 reason=reason,
111 body=body,
112 text=text,
113 content_type=content_type,
114 )
115 Exception.__init__(self, self.reason)
116 if self.body is None and not self.empty_body:
117 self.text = f"{self.status}: {self.reason}"
118
119 def __bool__(self) -> bool:
120 return True
121
122
123class HTTPError(HTTPException):
124 """Base class for exceptions with status codes in the 400s and 500s."""
125
126
127class HTTPRedirection(HTTPException):
128 """Base class for exceptions with status codes in the 300s."""
129
130
131class HTTPSuccessful(HTTPException):
132 """Base class for exceptions with status codes in the 200s."""
133
134
135class HTTPOk(HTTPSuccessful):
136 status_code = 200
137
138
139class HTTPCreated(HTTPSuccessful):
140 status_code = 201
141
142
143class HTTPAccepted(HTTPSuccessful):
144 status_code = 202
145
146
147class HTTPNonAuthoritativeInformation(HTTPSuccessful):
148 status_code = 203
149
150
151class HTTPNoContent(HTTPSuccessful):
152 status_code = 204
153 empty_body = True
154
155
156class HTTPResetContent(HTTPSuccessful):
157 status_code = 205
158 empty_body = True
159
160
161class HTTPPartialContent(HTTPSuccessful):
162 status_code = 206
163
164
165############################################################
166# 3xx redirection
167############################################################
168
169
170class HTTPMove(HTTPRedirection):
171 def __init__(
172 self,
173 location: StrOrURL,
174 *,
175 headers: Optional[LooseHeaders] = None,
176 reason: Optional[str] = None,
177 body: Any = None,
178 text: Optional[str] = None,
179 content_type: Optional[str] = None,
180 ) -> None:
181 if not location:
182 raise ValueError("HTTP redirects need a location to redirect to.")
183 super().__init__(
184 headers=headers,
185 reason=reason,
186 body=body,
187 text=text,
188 content_type=content_type,
189 )
190 self.headers["Location"] = str(URL(location))
191 self.location = location
192
193
194class HTTPMultipleChoices(HTTPMove):
195 status_code = 300
196
197
198class HTTPMovedPermanently(HTTPMove):
199 status_code = 301
200
201
202class HTTPFound(HTTPMove):
203 status_code = 302
204
205
206# This one is safe after a POST (the redirected location will be
207# retrieved with GET):
208class HTTPSeeOther(HTTPMove):
209 status_code = 303
210
211
212class HTTPNotModified(HTTPRedirection):
213 # FIXME: this should include a date or etag header
214 status_code = 304
215 empty_body = True
216
217
218class HTTPUseProxy(HTTPMove):
219 # Not a move, but looks a little like one
220 status_code = 305
221
222
223class HTTPTemporaryRedirect(HTTPMove):
224 status_code = 307
225
226
227class HTTPPermanentRedirect(HTTPMove):
228 status_code = 308
229
230
231############################################################
232# 4xx client error
233############################################################
234
235
236class HTTPClientError(HTTPError):
237 pass
238
239
240class HTTPBadRequest(HTTPClientError):
241 status_code = 400
242
243
244class HTTPUnauthorized(HTTPClientError):
245 status_code = 401
246
247
248class HTTPPaymentRequired(HTTPClientError):
249 status_code = 402
250
251
252class HTTPForbidden(HTTPClientError):
253 status_code = 403
254
255
256class HTTPNotFound(HTTPClientError):
257 status_code = 404
258
259
260class HTTPMethodNotAllowed(HTTPClientError):
261 status_code = 405
262
263 def __init__(
264 self,
265 method: str,
266 allowed_methods: Iterable[str],
267 *,
268 headers: Optional[LooseHeaders] = None,
269 reason: Optional[str] = None,
270 body: Any = None,
271 text: Optional[str] = None,
272 content_type: Optional[str] = None,
273 ) -> None:
274 allow = ",".join(sorted(allowed_methods))
275 super().__init__(
276 headers=headers,
277 reason=reason,
278 body=body,
279 text=text,
280 content_type=content_type,
281 )
282 self.headers["Allow"] = allow
283 self.allowed_methods: Set[str] = set(allowed_methods)
284 self.method = method.upper()
285
286
287class HTTPNotAcceptable(HTTPClientError):
288 status_code = 406
289
290
291class HTTPProxyAuthenticationRequired(HTTPClientError):
292 status_code = 407
293
294
295class HTTPRequestTimeout(HTTPClientError):
296 status_code = 408
297
298
299class HTTPConflict(HTTPClientError):
300 status_code = 409
301
302
303class HTTPGone(HTTPClientError):
304 status_code = 410
305
306
307class HTTPLengthRequired(HTTPClientError):
308 status_code = 411
309
310
311class HTTPPreconditionFailed(HTTPClientError):
312 status_code = 412
313
314
315class HTTPRequestEntityTooLarge(HTTPClientError):
316 status_code = 413
317
318 def __init__(self, max_size: float, actual_size: float, **kwargs: Any) -> None:
319 kwargs.setdefault(
320 "text",
321 "Maximum request body size {} exceeded, "
322 "actual body size {}".format(max_size, actual_size),
323 )
324 super().__init__(**kwargs)
325
326
327class HTTPRequestURITooLong(HTTPClientError):
328 status_code = 414
329
330
331class HTTPUnsupportedMediaType(HTTPClientError):
332 status_code = 415
333
334
335class HTTPRequestRangeNotSatisfiable(HTTPClientError):
336 status_code = 416
337
338
339class HTTPExpectationFailed(HTTPClientError):
340 status_code = 417
341
342
343class HTTPMisdirectedRequest(HTTPClientError):
344 status_code = 421
345
346
347class HTTPUnprocessableEntity(HTTPClientError):
348 status_code = 422
349
350
351class HTTPFailedDependency(HTTPClientError):
352 status_code = 424
353
354
355class HTTPUpgradeRequired(HTTPClientError):
356 status_code = 426
357
358
359class HTTPPreconditionRequired(HTTPClientError):
360 status_code = 428
361
362
363class HTTPTooManyRequests(HTTPClientError):
364 status_code = 429
365
366
367class HTTPRequestHeaderFieldsTooLarge(HTTPClientError):
368 status_code = 431
369
370
371class HTTPUnavailableForLegalReasons(HTTPClientError):
372 status_code = 451
373
374 def __init__(
375 self,
376 link: Optional[StrOrURL],
377 *,
378 headers: Optional[LooseHeaders] = None,
379 reason: Optional[str] = None,
380 body: Any = None,
381 text: Optional[str] = None,
382 content_type: Optional[str] = None,
383 ) -> None:
384 super().__init__(
385 headers=headers,
386 reason=reason,
387 body=body,
388 text=text,
389 content_type=content_type,
390 )
391 self._link = None
392 if link:
393 self._link = URL(link)
394 self.headers["Link"] = f'<{str(self._link)}>; rel="blocked-by"'
395
396 @property
397 def link(self) -> Optional[URL]:
398 return self._link
399
400
401############################################################
402# 5xx Server Error
403############################################################
404# Response status codes beginning with the digit "5" indicate cases in
405# which the server is aware that it has erred or is incapable of
406# performing the request. Except when responding to a HEAD request, the
407# server SHOULD include an entity containing an explanation of the error
408# situation, and whether it is a temporary or permanent condition. User
409# agents SHOULD display any included entity to the user. These response
410# codes are applicable to any request method.
411
412
413class HTTPServerError(HTTPError):
414 pass
415
416
417class HTTPInternalServerError(HTTPServerError):
418 status_code = 500
419
420
421class HTTPNotImplemented(HTTPServerError):
422 status_code = 501
423
424
425class HTTPBadGateway(HTTPServerError):
426 status_code = 502
427
428
429class HTTPServiceUnavailable(HTTPServerError):
430 status_code = 503
431
432
433class HTTPGatewayTimeout(HTTPServerError):
434 status_code = 504
435
436
437class HTTPVersionNotSupported(HTTPServerError):
438 status_code = 505
439
440
441class HTTPVariantAlsoNegotiates(HTTPServerError):
442 status_code = 506
443
444
445class HTTPInsufficientStorage(HTTPServerError):
446 status_code = 507
447
448
449class HTTPNotExtended(HTTPServerError):
450 status_code = 510
451
452
453class HTTPNetworkAuthenticationRequired(HTTPServerError):
454 status_code = 511
455 