def test_custom_headers(self):
    http_headers = appinfo.HttpHeadersDict()
    http_headers['Content-type'] = 'text/xml'
    http_headers['ETag'] = 'abc123'
    http_headers['Expires'] = 'tomorrow'
    http_headers['Cache-Control'] = 'private'
    http_headers['Custom-Header'] = 'custom,value'

    url_map = appinfo.URLMap(url='/',
                             static_files='index.html',
                             http_headers=http_headers)

    h = static_files_handler.StaticContentHandler(
        root_path=None,
        url_map=url_map,
        url_pattern='/$')

    os.path.getmtime('/home/appdir/index.html').AndReturn(12345.6)
    static_files_handler.StaticContentHandler._read_file(
        '/home/appdir/index.html').AndReturn('Hello World!')

    self.mox.ReplayAll()
    self.assertResponse('200 OK',
                        {'Content-length': '12',
                         'Content-type': 'text/xml',
                         'ETag': 'abc123',
                         'Expires': 'tomorrow',
                         'Cache-Control': 'private',
                         'Custom-Header': 'custom,value'},
                        'Hello World!',
                        h._handle_path,
                        '/home/appdir/index.html',
                        {'REQUEST_METHOD': 'GET'})
    self.mox.VerifyAll()
    self.assertEqual(
        static_files_handler.StaticContentHandler._filename_to_mtime_and_etag,
        {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
Пример #2
0
    def _handle_path(self, full_path, environ, start_response):
        """Serves the response to a request for a particular file.

    Note that production App Engine treats all methods as "GET" except "HEAD".

    Unless set explicitly, the "Expires" and "Cache-Control" headers are
    deliberately different from their production values to make testing easier.
    If set explicitly then the values are preserved because the user may
    reasonably want to test for them.

    Args:
      full_path: A string containing the absolute path to the file to serve.
      environ: An environ dict for the current request as defined in PEP-333.
      start_response: A function with semantics defined in PEP-333.

    Returns:
      An iterable over strings containing the body of the HTTP response.
    """
        data = None
        if full_path in self._filename_to_mtime_and_etag:
            last_mtime, etag = self._filename_to_mtime_and_etag[full_path]
        else:
            last_mtime = etag = None

        user_headers = self._url_map.http_headers or appinfo.HttpHeadersDict()

        if_match = environ.get('HTTP_IF_MATCH')
        if_none_match = environ.get('HTTP_IF_NONE_MATCH')

        try:
            mtime = os.path.getmtime(full_path)
        except (OSError, IOError) as e:
            # RFC-2616 section 14.24 says:
            # If none of the entity tags match, or if "*" is given and no current
            # entity exists, the server MUST NOT perform the requested method, and
            # MUST return a 412 (Precondition Failed) response.
            if if_match:
                start_response('412 Precondition Failed', [])
                return []
            elif self._url_map.require_matching_file:
                return None
            else:
                return self._handle_io_exception(start_response, e)

        if mtime != last_mtime:
            try:
                data = self._read_file(full_path)
            except (OSError, IOError) as e:
                return self._handle_io_exception(start_response, e)
            etag = self._calculate_etag(data)
            self._filename_to_mtime_and_etag[full_path] = mtime, etag

        if if_match and not self._check_etag_match(
                if_match, etag, allow_weak_match=False):
            # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
            start_response('412 Precondition Failed',
                           [('ETag', '"%s"' % etag)])
            return []
        elif if_none_match and self._check_etag_match(
                if_none_match, etag, allow_weak_match=True):
            # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
            start_response('304 Not Modified', [('ETag', '"%s"' % etag)])
            return []
        else:
            if data is None:
                try:
                    data = self._read_file(full_path)
                except (OSError, IOError) as e:
                    return self._handle_io_exception(start_response, e)

                etag = self._calculate_etag(data)
                self._filename_to_mtime_and_etag[full_path] = mtime, etag

            headers = [('Content-length', str(len(data)))]

            if user_headers.Get('Content-type') is None:
                headers.append(
                    ('Content-type', self._get_mime_type(full_path)))

            if user_headers.Get('ETag') is None:
                headers.append(('ETag', '"%s"' % etag))

            if user_headers.Get('Expires') is None:
                headers.append(('Expires', 'Fri, 01 Jan 1990 00:00:00 GMT'))

            if user_headers.Get('Cache-Control') is None:
                headers.append(('Cache-Control', 'no-cache'))

            for name, value in user_headers.iteritems():
                # "name" will always be unicode due to the way that ValidatedDict works.
                headers.append((str(name), value))

            start_response('200 OK', headers)
            if environ['REQUEST_METHOD'] == 'HEAD':
                return []
            else:
                return [data]
    def now():
        return FAKE_CURRENT_TIME


def script_path(script, test_name=__name__):
    """Returns a fully qualified module path based on test_name."""
    return '{test_path}.{script}'.format(test_path=test_name, script=script)


def static_path(relative_path, test_path=os.path.dirname(__file__)):
    """Returns a fully qualified static file path based on test_path."""
    return os.path.join(test_path, relative_path)


FAKE_CURRENT_TIME = datetime.datetime(2015, 11, 30, 18)
FAKE_HTTP_HEADERS = appinfo.HttpHeadersDict()
FAKE_HTTP_HEADERS['X-Foo-Header'] = 'foo'
FAKE_HTTP_HEADERS['X-Bar-Header'] = 'bar value'

FAKE_HANDLERS = [
    appinfo.URLMap(url='/hello', script=script_path('hello_world')),
    appinfo.URLMap(url='/failure', script=script_path('nonexistent_function')),
    appinfo.URLMap(url='/env', script=script_path('dump_os_environ')),
    appinfo.URLMap(url='/sortenv', script=script_path('sort_os_environ_keys')),
    appinfo.URLMap(url='/setenv', script=script_path('add_to_os_environ')),
    appinfo.URLMap(url='/wait', script=script_path('wait_on_global_event')),
    appinfo.URLMap(url='/favicon.ico',
                   static_files=static_path('test_statics/favicon.ico'),
                   upload=static_path('test_statics/favicon.ico')),
    appinfo.URLMap(url='/faketype.ico',
                   static_files=static_path('test_statics/favicon.ico'),