Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/internal/middleware/slash.py: 50%

1 from plain.http import ResponsePermanentRedirect

2 from plain.runtime import settings

3 from plain.urls import is_valid_path

4 from plain.utils.http import escape_leading_slashes

5

6

7 class RedirectSlashMiddleware:

8 def __init__(self, get_response):

9 self.get_response = get_response

10

11 def __call__(self, request):

12 """

13 Rewrite the URL based on settings.APPEND_SLASH

14 """

15

16 response = self.get_response(request)

17

18 """

19 When the status code of the response is 404, it may redirect to a path

20 with an appended slash if should_redirect_with_slash() returns True.

21 """

22 # If the given URL is "Not Found", then check if we should redirect to

23 # a path with a slash appended.

24 if response.status_code == 404 and self.should_redirect_with_slash(request):

25 return ResponsePermanentRedirect(self.get_full_path_with_slash(request))

26

27 return response

28

29 def should_redirect_with_slash(self, request):

30 """

31 Return True if settings.APPEND_SLASH is True and appending a slash to

32 the request path turns an invalid path into a valid one.

33 """

34 if settings.APPEND_SLASH and not request.path_info.endswith("/"):

35 urlconf = getattr(request, "urlconf", None)

36 if not is_valid_path(request.path_info, urlconf):

37 match = is_valid_path(f"{request.path_info}/", urlconf)

38 if match:

39 view = match.func

40 return getattr(view, "should_append_slash", True)

41 return False

42

43 def get_full_path_with_slash(self, request):

44 """

45 Return the full path of the request with a trailing slash appended.

46

47 Raise a RuntimeError if settings.DEBUG is True and request.method is

48 POST, PUT, or PATCH.

49 """

50 new_path = request.get_full_path(force_append_slash=True)

51 # Prevent construction of scheme relative urls.

52 new_path = escape_leading_slashes(new_path)

53 if settings.DEBUG and request.method in ("POST", "PUT", "PATCH"):

54 raise RuntimeError(

55 f"You called this URL via {request.method}, but the URL doesn't end "

56 "in a slash and you have APPEND_SLASH set. Plain can't "

57 f"redirect to the slash URL while maintaining {request.method} data. "

58 f"Change your form to point to {request.get_host() + new_path} (note the trailing "

59 "slash), or set APPEND_SLASH=False in your Plain settings."

60 )

61 return new_path