Commit e60557c2 authored by Tim Graham's avatar Tim Graham
Browse files

[1.4.x] Fixed #24238 -- Removed unused WSGIRequestHandler.get_environ()

Also moved the test as it wasn't running.
parent 4376d6ef
Loading
Loading
Loading
Loading
+0 −33
Original line number Diff line number Diff line
@@ -138,39 +138,6 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
        self.style = color_style()
        super(WSGIRequestHandler, self).__init__(*args, **kwargs)

    def get_environ(self):
        env = self.server.base_environ.copy()
        env['SERVER_PROTOCOL'] = self.request_version
        env['REQUEST_METHOD'] = self.command
        if '?' in self.path:
            path,query = self.path.split('?',1)
        else:
            path,query = self.path,''

        env['PATH_INFO'] = urllib.unquote(path)
        env['QUERY_STRING'] = query
        env['REMOTE_ADDR'] = self.client_address[0]

        if self.headers.typeheader is None:
            env['CONTENT_TYPE'] = self.headers.type
        else:
            env['CONTENT_TYPE'] = self.headers.typeheader

        length = self.headers.getheader('content-length')
        if length:
            env['CONTENT_LENGTH'] = length

        for h in self.headers.headers:
            k,v = h.split(':',1)
            k=k.replace('-','_').upper(); v=v.strip()
            if k in env:
                continue                    # skip content length, type,etc.
            if 'HTTP_'+k in env:
                env['HTTP_'+k] += ','+v     # comma-separate multiple headers
            else:
                env['HTTP_'+k] = v
        return env

    def log_message(self, format, *args):
        # Don't bother logging requests for admin images or the favicon.
        if (self.path.startswith(self.admin_media_prefix)
+0 −67
Original line number Diff line number Diff line
import sys

from django.core.servers.basehttp import WSGIRequestHandler
from django.test import TestCase
from django.utils.six import BytesIO, StringIO


class Stub(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)


class WSGIRequestHandlerTestCase(TestCase):

    def test_strips_underscore_headers(self):
        """WSGIRequestHandler ignores headers containing underscores.

        This follows the lead of nginx and Apache 2.4, and is to avoid
        ambiguity between dashes and underscores in mapping to WSGI environ,
        which can have security implications.
        """
        def test_app(environ, start_response):
            """A WSGI app that just reflects its HTTP environ."""
            start_response('200 OK', [])
            http_environ_items = sorted(
                '%s:%s' % (k, v) for k, v in environ.items()
                if k.startswith('HTTP_')
            )
            yield (','.join(http_environ_items)).encode('utf-8')

        rfile = BytesIO()
        rfile.write(b"GET / HTTP/1.0\r\n")
        rfile.write(b"Some-Header: good\r\n")
        rfile.write(b"Some_Header: bad\r\n")
        rfile.write(b"Other_Header: bad\r\n")
        rfile.seek(0)

        # WSGIRequestHandler closes the output file; we need to make this a
        # no-op so we can still read its contents.
        class UnclosableBytesIO(BytesIO):
            def close(self):
                pass

        wfile = UnclosableBytesIO()

        def makefile(mode, *a, **kw):
            if mode == 'rb':
                return rfile
            elif mode == 'wb':
                return wfile

        request = Stub(makefile=makefile)
        server = Stub(base_environ={}, get_app=lambda: test_app)

        # We don't need to check stderr, but we don't want it in test output
        old_stderr = sys.stderr
        sys.stderr = StringIO()
        try:
            # instantiating a handler runs the request as side effect
            WSGIRequestHandler(request, '192.168.0.2', server)
        finally:
            sys.stderr = old_stderr

        wfile.seek(0)
        body = list(wfile.readlines())[-1]

        self.assertEqual(body, b'HTTP_SOME_HEADER:good')
+66 −1
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
Tests for django.core.servers.
"""
import os
import sys
from urlparse import urljoin
import urllib2

@@ -10,8 +11,10 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase, LiveServerTestCase
from django.core.handlers.wsgi import WSGIHandler
from django.core.servers.basehttp import AdminMediaHandler, WSGIServerException
from django.core.servers.basehttp import (
    AdminMediaHandler, WSGIRequestHandler, WSGIServerException)
from django.test.utils import override_settings
from django.utils.six import BytesIO, StringIO

from .models import Person

@@ -213,3 +216,65 @@ class LiveServerDatabase(LiveServerBase):
        self.urlopen('/create_model_instance/')
        names = [person.name for person in Person.objects.all()]
        self.assertEquals(names, ['jane', 'robert', 'emily'])


class Stub(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)


class WSGIRequestHandlerTestCase(TestCase):

    def test_strips_underscore_headers(self):
        """WSGIRequestHandler ignores headers containing underscores.

        This follows the lead of nginx and Apache 2.4, and is to avoid
        ambiguity between dashes and underscores in mapping to WSGI environ,
        which can have security implications.
        """
        def test_app(environ, start_response):
            """A WSGI app that just reflects its HTTP environ."""
            start_response('200 OK', [])
            http_environ_items = sorted(
                '%s:%s' % (k, v) for k, v in environ.items()
                if k.startswith('HTTP_')
            )
            yield (','.join(http_environ_items)).encode('utf-8')

        rfile = BytesIO()
        rfile.write("GET / HTTP/1.0\r\n")
        rfile.write("Some-Header: good\r\n")
        rfile.write("Some_Header: bad\r\n")
        rfile.write("Other_Header: bad\r\n")
        rfile.seek(0)

        # WSGIRequestHandler closes the output file; we need to make this a
        # no-op so we can still read its contents.
        class UnclosableBytesIO(BytesIO):
            def close(self):
                pass

        wfile = UnclosableBytesIO()

        def makefile(mode, *a, **kw):
            if mode == 'rb':
                return rfile
            elif mode == 'wb':
                return wfile

        request = Stub(makefile=makefile)
        server = Stub(base_environ={}, get_app=lambda: test_app)

        # We don't need to check stderr, but we don't want it in test output
        old_stderr = sys.stderr
        sys.stderr = StringIO()
        try:
            # instantiating a handler runs the request as side effect
            WSGIRequestHandler(request, '192.168.0.2', server)
        finally:
            sys.stderr = old_stderr

        wfile.seek(0)
        body = list(wfile.readlines())[-1]

        self.assertEqual(body, 'HTTP_SOME_HEADER:good')