I am using Djoser to create an authentication backend. I want the following to happen:
- User registers at
http://localhost:8000/auth/users/
with email, username, password, and re_password - User receives an activation email at the email they entered with a URL to activate their account. The URL is of the form:
<web url>/activate/{uid}/token
(eg:http://localhost:8000/activate/Mg/apur8c-6100390a061c5ff081235193867f943a
) - Clicking on that URL sends a GET request to the server where a view (
UserActivationView
) receives the extracted uid and token and sends a request of its own to<web url>/users/activation/{uid}/token
(http://localhost:8000/users/activation/Mg/apur8c-6100390a061c5ff081235193867f943a
). And returns the response that it gets from that request as its response. - User account gets activated.
This is the code that does all of the above:
# Djoser configuration in settings.py
DJOSER = {
'LOGIN_FIELD': 'email',
'PASSWORD_RESET_CONFIRM_URL': 'password-reset/{uid}/{token}',
'USERNAME_RESET_CONFIRM_URL': 'username-reset/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
'ACTIVATION_URL': 'activate/{uid}/{token}',
'SEND_CONFIRMATION_EMAIL': True,
'PASSWORD_CHANGE_EMAIL_CONFIRMATION': True,
'USERNAME_CHANGED_EMAIL_CONFIRMATION': True,
'USER_CREATE_PASSWORD_RETYPE': True,
'SET_PASSWORD_RETYPE': True,
}
# views.py in user app (users/views.py)
from rest_framework.views import APIView
from rest_framework.response import Response
import requests
class UserActivationView(APIView):
def get(self, request, uid, token):
protocol = "https://" if request.is_secure() else "http://"
web_url = protocol + request.get_host()
post_url = web_url + '/auth/users/activation/'
post_data = {'uid': uid, "token": token}
print(f"Sending request to {post_url} with {post_data}")
result = requests.post(post_url, data=post_data) # <<-- causes the server to hang
return Response(result.text)
# users/urls.py
from django.urls import path, include
from .views import UserActivationView
app_name = "users"
urlpatterns = [
path('auth/', include('djoser.urls')),
path('auth/', include('djoser.urls.jwt')),
path('activate/<str:uid>/<str:token>/',
UserActivationView.as_view())
]
# MAIN project urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('chat/', include('frontend.urls')),
path("chat/", include('backend.urls')),
path('api/accounts/', include('backend.urls')),
path('', include('users.urls'))
]
Now, the problem arises in step 3 (at the result = requests.post(post_url, data=post_data)
line). When the view attempts to send a post request the server hangs for quite some time. During this, it doesn't even halt when I do CTRL + C. This is the output that I get (I have added comments):
HTTP POST /auth/users/ 201 [5.87, 127.0.0.1:56239] ## User registration was successful and email was sent
HTTP GET /activate/Mg/apuvq9-3461158f807a1e054738458bc2372486 301 [0.00, 127.0.0.1:56245] # 301 response while trying to GET the URL sent in the email
Sending request to http://localhost:8000/auth/users/activation/ with {'uid': 'Mg', 'token': 'apuvq9-3461158f807a1e054738458bc2372486'} # My print statement
# A long pause here. Server hangs and then this error appears:
Application instance <Task pending coro=<StaticFilesWrapper.__call__() running at D:\pigeon\venv\lib\site-packages\channels\staticfiles.py:44> wait_for=<Future pending cb=[_chain_future.<locals>._call_check_cancel() at C:\ProgramData\Anaconda3\lib\asyncio\futures.py:348, <TaskWakeupMethWrapper object at 0x000001E3EDE96858>()]>> for connection <WebRequest at 0x1e3edd8a788 method=GET uri=/activate/Mg/apuvq9-3461158f807a1e054738458bc2372486/ clientproto=HTTP/1.1> took too long to shut down and was killed.
Application instance <Task pending coro=<StaticFilesWrapper.__call__() running at D:\pigeon\venv\lib\site-packages\channels\staticfiles.py:44> wait_for=<Future pending cb=[_chain_future.<locals>._call_check_cancel() at C:\ProgramData\Anaconda3\lib\asyncio\futures.py:348, <TaskWakeupMethWrapper object at 0x000001E3EE1067C8>()]>> for connection <WebRequest at 0x1e3edd8a788 method=GET uri=/activate/Mg/apuvq9-3461158f807a1e054738458bc2372486/ clientproto=HTTP/1.1> took too long to shut down and was killed.
Internal Server Error: /activate/Mg/apuvq9-3461158f807a1e054738458bc2372486/
Traceback (most recent call last):
File "D:\pigeon\venv\lib\site-packages\asgiref\sync.py", line 482, in thread_handler
raise exc_info[1]
File "D:\pigeon\venv\lib\site-packages\django\core\handlers\exception.py", line 38, in inner
response = await get_response(request)
File "D:\pigeon\venv\lib\site-packages\django\core\handlers\base.py", line 238, in _get_response_async
)(e, request)
File "D:\pigeon\venv\lib\site-packages\asgiref\sync.py", line 444, in __call__
ret = await asyncio.wait_for(future, timeout=None)
File "C:\ProgramData\Anaconda3\lib\asyncio\tasks.py", line 414, in wait_for
return await fut
concurrent.futures._base.CancelledError
Alternatively, if I manually send an activation request via Postman or a separate Python script then the request works just fine and the user is activated:
The problem only seems to arise when the request is made from within the view to the server. I also tried result = requests.get('http://localhost:8000/chat')
as an experiment and it too caused the server to hang whereas result = requests.get('http://google.com')
causes no such issues.
The view's code was taken from here and someone under the answer also commented:
This hangs my debug django server – Hack_Hut Apr 25 at 11:55
How can I resolve this?
I am using:
- Python: 3.7.5
- Django: 3.2.5
- Djoser: 2.1.0
- Django rest framework: 3.12.4
- Requests: 2.26.0