From 9a6e2df3a8f01ea761529bec48e5a8dc0ea9575b Mon Sep 17 00:00:00 2001 From: Ad Timmering Date: Mon, 22 Nov 2021 22:08:24 +0900 Subject: [PATCH] Fixed #32397 -- Made startapp/startproject management commands set User-Agent. This sets User-Agent to 'Django/'. --- django/core/management/templates.py | 14 +++++++++---- tests/admin_scripts/tests.py | 31 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/django/core/management/templates.py b/django/core/management/templates.py index 6449342ae1..cfcaff7c0f 100644 --- a/django/core/management/templates.py +++ b/django/core/management/templates.py @@ -7,7 +7,7 @@ import shutil import stat import tempfile from importlib import import_module -from urllib.request import urlretrieve +from urllib.request import build_opener import django from django.conf import settings @@ -277,8 +277,14 @@ class TemplateCommand(BaseCommand): if self.verbosity >= 2: self.stdout.write('Downloading %s' % display_url) + + the_path = os.path.join(tempdir, filename) + opener = build_opener() + opener.addheaders = [('User-Agent', f'Django/{django.__version__}')] try: - the_path, info = urlretrieve(url, os.path.join(tempdir, filename)) + with opener.open(url) as source, open(the_path, 'wb') as target: + headers = source.info() + target.write(source.read()) except OSError as e: raise CommandError("couldn't download URL %s to %s: %s" % (url, filename, e)) @@ -286,7 +292,7 @@ class TemplateCommand(BaseCommand): used_name = the_path.split('/')[-1] # Trying to get better name from response headers - content_disposition = info.get('content-disposition') + content_disposition = headers['content-disposition'] if content_disposition: _, params = cgi.parse_header(content_disposition) guessed_filename = params.get('filename') or used_name @@ -295,7 +301,7 @@ class TemplateCommand(BaseCommand): # Falling back to content type guessing ext = self.splitext(guessed_filename)[1] - content_type = info.get('content-type') + content_type = headers['content-type'] if not ext and content_type: ext = mimetypes.guess_extension(content_type) if ext: diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 0f4bbc57d7..c8d81e1fc0 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -33,7 +33,11 @@ from django.test import ( LiveServerTestCase, SimpleTestCase, TestCase, override_settings, ) from django.test.utils import captured_stderr, captured_stdout +from django.urls import path from django.utils.version import PY39 +from django.views.static import serve + +from . import urls custom_templates_dir = os.path.join(os.path.dirname(__file__), 'custom_templates') @@ -2127,6 +2131,33 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase): self.assertTrue(os.path.isdir(testproject_dir)) self.assertTrue(os.path.exists(os.path.join(testproject_dir, 'run.py'))) + def test_custom_project_template_from_tarball_by_url_django_user_agent(self): + user_agent = None + + def serve_template(request, *args, **kwargs): + nonlocal user_agent + user_agent = request.headers['User-Agent'] + return serve(request, *args, **kwargs) + + old_urlpatterns = urls.urlpatterns[:] + try: + urls.urlpatterns += [ + path( + 'user_agent_check/', + serve_template, + {'document_root': os.path.join(urls.here, 'custom_templates')}, + ), + ] + + template_url = f'{self.live_server_url}/user_agent_check/project_template.tgz' + args = ['startproject', '--template', template_url, 'urltestproject'] + _, err = self.run_django_admin(args) + + self.assertNoOutput(err) + self.assertIn('Django/%s' % get_version(), user_agent) + finally: + urls.urlpatterns = old_urlpatterns + def test_project_template_tarball_url(self): "Startproject management command handles project template tar/zip balls from non-canonical urls" template_url = '%s/custom_templates/project_template.tgz/' % self.live_server_url