-
-
Notifications
You must be signed in to change notification settings - Fork 53
/
http.py
264 lines (203 loc) · 7.41 KB
/
http.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
import re
import sys
from django.conf import settings
from django.contrib.auth import logout
from django.core.exceptions import ImproperlyConfigured
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.urls import NoReverseMatch, Resolver404, resolve, reverse
from django.utils.cache import add_never_cache_headers
from django.utils.module_loading import import_string
from maintenance_mode.core import get_maintenance_mode
from maintenance_mode.utils import get_client_ip_address
def get_maintenance_response_context(request):
context = {}
if settings.MAINTENANCE_MODE_GET_CONTEXT:
try:
get_request_context_func = import_string(
settings.MAINTENANCE_MODE_GET_CONTEXT
)
except ImportError as error:
raise ImproperlyConfigured(
"settings.MAINTENANCE_MODE_GET_CONTEXT is not a valid function path."
) from error
context = get_request_context_func(request=request)
return context
def get_maintenance_response(request):
"""
Return a '503 Service Unavailable' HTML or JSON response based on user preference.
"""
if settings.MAINTENANCE_MODE_REDIRECT_URL:
return redirect(settings.MAINTENANCE_MODE_REDIRECT_URL)
response = None
response_type = settings.MAINTENANCE_MODE_RESPONSE_TYPE
if response_type == "html":
response = get_maintenance_html_response(request)
elif response_type == "json":
response = get_maintenance_json_response(request)
else:
raise ImproperlyConfigured(
"settings.MAINTENANCE_MODE_RESPONSE_TYPE value must be 'html' or 'json'."
)
if settings.MAINTENANCE_MODE_RETRY_AFTER:
response["Retry-After"] = settings.MAINTENANCE_MODE_RETRY_AFTER
add_never_cache_headers(response)
return response
def get_maintenance_html_response(request):
"""
Return an HTML response for maintenance.
"""
context = get_maintenance_response_context(request)
response = render(
request,
settings.MAINTENANCE_MODE_TEMPLATE,
status=settings.MAINTENANCE_MODE_STATUS_CODE,
context=context,
)
return response
def get_maintenance_json_response(request):
"""
Return a JSON response for maintenance.
"""
context = get_maintenance_response_context(request)
response = JsonResponse(
context,
status=settings.MAINTENANCE_MODE_STATUS_CODE,
)
return response
def _need_maintenance_from_view(request):
try:
view_match = resolve(request.path)
view_func = view_match[0]
view_dict = view_func.__dict__
view_force_maintenance_mode_off = view_dict.get(
"force_maintenance_mode_off", False
)
if view_force_maintenance_mode_off:
# view has 'force_maintenance_mode_off' decorator
return False
view_force_maintenance_mode_on = view_dict.get(
"force_maintenance_mode_on", False
)
if view_force_maintenance_mode_on:
# view has 'force_maintenance_mode_on' decorator
return True
except Resolver404:
pass
def _need_maintenance_from_url(request):
try:
url_off = reverse("maintenance_mode_off")
resolve(url_off)
if url_off == request.path_info:
return False
except NoReverseMatch:
# maintenance_mode.urls not added
pass
def _need_maintenance_ignore_users(request):
if not hasattr(request, "user"):
return
user = request.user
if settings.MAINTENANCE_MODE_LOGOUT_AUTHENTICATED_USER and user.is_authenticated:
logout(request)
user = request.user
if settings.MAINTENANCE_MODE_IGNORE_ANONYMOUS_USER and user.is_anonymous:
return False
if settings.MAINTENANCE_MODE_IGNORE_AUTHENTICATED_USER and user.is_authenticated:
return False
if settings.MAINTENANCE_MODE_IGNORE_STAFF and user.is_staff:
return False
if settings.MAINTENANCE_MODE_IGNORE_SUPERUSER and user.is_superuser:
return False
def _need_maintenance_ignore_admin_site(request):
if not settings.MAINTENANCE_MODE_IGNORE_ADMIN_SITE:
return
try:
request_path = request.path if request.path else ""
if not request_path.endswith("/"):
request_path += "/"
admin_url = reverse("admin:index")
if request_path.startswith(admin_url):
return False
except NoReverseMatch:
# admin.urls not added
pass
def _need_maintenance_ignore_tests(request):
if not settings.MAINTENANCE_MODE_IGNORE_TESTS:
return
is_testing = False
if (len(sys.argv) > 0 and "runtests" in sys.argv[0]) or (
len(sys.argv) > 1 and sys.argv[1] == "test"
):
# python runtests.py | python manage.py test | python
# setup.py test | django-admin.py test
is_testing = True
if is_testing:
return False
def _need_maintenance_ignore_ip_addresses(request):
if not settings.MAINTENANCE_MODE_IGNORE_IP_ADDRESSES:
return
if settings.MAINTENANCE_MODE_GET_CLIENT_IP_ADDRESS:
try:
get_client_ip_address_func = import_string(
settings.MAINTENANCE_MODE_GET_CLIENT_IP_ADDRESS
)
except ImportError as error:
raise ImproperlyConfigured(
"settings.MAINTENANCE_MODE_GET_CLIENT_IP_ADDRESS "
"is not a valid function path."
) from error
else:
client_ip_address = get_client_ip_address_func(request)
else:
client_ip_address = get_client_ip_address(request)
for ip_address in settings.MAINTENANCE_MODE_IGNORE_IP_ADDRESSES:
ip_address_re = re.compile(ip_address)
if ip_address_re.match(client_ip_address):
return False
def _need_maintenance_ignore_urls(request):
if not settings.MAINTENANCE_MODE_IGNORE_URLS:
return
for url in settings.MAINTENANCE_MODE_IGNORE_URLS:
if not isinstance(url, re.Pattern):
url = str(url)
url_re = re.compile(url)
if url_re.match(request.path_info):
return False
def _need_maintenance_redirects(request):
if not settings.MAINTENANCE_MODE_REDIRECT_URL:
return
redirect_url_re = re.compile(settings.MAINTENANCE_MODE_REDIRECT_URL)
if redirect_url_re.match(request.path_info):
return False
def need_maintenance_response(request):
"""
Tells if the given request needs a maintenance response or not.
"""
value = _need_maintenance_from_view(request)
if isinstance(value, bool):
return value
value = get_maintenance_mode()
if not value:
return value
value = _need_maintenance_from_url(request)
if isinstance(value, bool):
return value
value = _need_maintenance_ignore_users(request)
if isinstance(value, bool):
return value
value = _need_maintenance_ignore_admin_site(request)
if isinstance(value, bool):
return value
value = _need_maintenance_ignore_tests(request)
if isinstance(value, bool):
return value
value = _need_maintenance_ignore_ip_addresses(request)
if isinstance(value, bool):
return value
value = _need_maintenance_ignore_urls(request)
if isinstance(value, bool):
return value
value = _need_maintenance_redirects(request)
if isinstance(value, bool):
return value
return True