diff options
authorDan McGee <>2013-04-16 21:59:32 -0500
committerDan McGee <>2013-04-16 21:59:32 -0500
commit31d39e75eea7fb6cdf3bb8bfd8b490d45de04ee9 (patch)
parent0589853360d12a1746e2d8e92e798f2727a0b5df (diff)
Add shortcut for HEAD requests on slower views
We sometimes see some web bots and crawlers make HEAD requests to verify existence of certain pages in the application. However, they are less than kind as 20-50 requests might arrive at the same time, and package search and details pages are some of the slowest rendering pages we have due to the Django template engine. Rather than waste time generating the content only to throw it away, response as soon as we can with either a 404 or 200 response as appropriate, omitting the 'Content-Length' header completely, which seems to be acceptable by the HTTP spec. Signed-off-by: Dan McGee <>
3 files changed, 15 insertions, 1 deletions
diff --git a/main/ b/main/
index cdd4ff71..8394e5cd 100644
--- a/main/
+++ b/main/
@@ -8,6 +8,7 @@ import hashlib
from django.core.cache import cache
from django.db import connections, router
+from django.http import HttpResponse
from django.utils.timezone import now
from django.template.defaultfilters import slugify
@@ -55,6 +56,14 @@ def clear_cache_function(func, args, kwargs):
+def empty_response():
+ empty = HttpResponse('')
+ # designating response as 'streaming' forces ConditionalGetMiddleware to
+ # not add a 'Content-Length: 0' header
+ empty.streaming = True
+ return empty
def format_http_headers(request):
headers = sorted((k, v) for k, v in request.META.items()
if k.startswith('HTTP_'))
diff --git a/packages/views/ b/packages/views/
index 50783835..87424483 100644
--- a/packages/views/
+++ b/packages/views/
@@ -7,6 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.utils.timezone import now
from main.models import Package, PackageFile, Arch, Repo
+from main.utils import empty_response
from mirrors.utils import get_mirror_url_for_download
from ..models import Update
from ..utils import get_group_info, PackageJSONEncoder
@@ -126,6 +127,8 @@ def details(request, name='', repo='', arch=''):
pkg = Package.objects.select_related(
'arch', 'repo', 'packager').get(pkgname=name,
repo=repo_obj, arch=arch_obj)
+ if request.method == 'HEAD':
+ return empty_response()
return render(request, 'packages/details.html', {'pkg': pkg})
except Package.DoesNotExist:
# attempt a variety of fallback options before 404ing
diff --git a/packages/views/ b/packages/views/
index 0362602e..b3778172 100644
--- a/packages/views/
+++ b/packages/views/
@@ -7,7 +7,7 @@ from django.http import HttpResponse
from django.views.generic import ListView
from main.models import Package, Arch, Repo
-from main.utils import make_choice
+from main.utils import empty_response, make_choice
from ..models import PackageRelation
from ..utils import attach_maintainers, PackageJSONEncoder
@@ -99,6 +99,8 @@ class SearchListView(ListView):
allowed_sort = list(sort_fields) + ["-" + s for s in sort_fields]
def get(self, request, *args, **kwargs):
+ if request.method == 'HEAD':
+ return empty_response()
self.form = PackageSearchForm(data=request.GET,
return super(SearchListView, self).get(request, *args, **kwargs)