blob: 45517f7eb5a53ee413b332f0fa090c1127d45089 [file] [log] [blame]
#! /usr/bin/python
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A simple http server for running local integration tests.
This chooses a port dynamically and so can communicate that back to its spawner
via a named pipe at --fifo. Sources are served from the tree named at
--source_dir.
"""
import argparse
import cgi
import json
import os.path
import logging
import re
import time
import wsgiref.simple_server
_CONTENT_TYPE_FOR_SUFFIX = {
'css': 'text/css',
'html': 'text/html',
'jpg': 'image/jpeg',
'js': 'text/javascript',
'json': 'application/json',
'png': 'image/png',
'ttf': 'font/ttf',}
# Name of the JSON file containing per file custom response headers located in
# the --source_dir.
# This file should structured like:
# {
# 'mydocument.html': [
# ['Cache-Control', 'max-age=3600'],
# ['Content-Encoding', 'gzip'],
# ]
# }
RESPONSE_HEADERS_PATH = 'RESPONSE_HEADERS.json'
class ServerApp(object):
"""WSGI App.
Dispatches by matching, in order, against GetPaths.
"""
def __init__(self, source_dir):
self._source_dir = source_dir
self._response_headers = {}
response_header_path = os.path.join(source_dir, RESPONSE_HEADERS_PATH)
if os.path.exists(response_header_path):
with open(response_header_path) as response_headers_file:
self._response_headers = json.load(response_headers_file)
def __call__(self, environ, start_response):
"""WSGI dispatch.
Args:
environ: environment list.
start_response: WSGI response start.
Returns:
Iterable server result.
"""
path = environ.get('PATH_INFO', '')
while path.startswith('/'):
path = path[1:]
filename = os.path.join(self._source_dir, path)
if not os.path.exists(filename):
logging.info('%s not found', filename)
start_response('404 Not Found', [('Content-Type', 'text/html')])
return ["""<!DOCTYPE html>
<html>
<head>
<title>Not Found</title>
<body>%s not found</body>
</html>""" % path]
logging.info('responding with %s', filename)
suffix = path[path.rfind('.') + 1:]
headers = [('Content-Type', _CONTENT_TYPE_FOR_SUFFIX[suffix])]
if path in self._response_headers:
for header in self._response_headers[path]:
headers.append((str(header[0]), str(header[1])))
start_response('200 OK', headers)
return [file(filename).read()]
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
parser = argparse.ArgumentParser()
parser.add_argument('--fifo', default=None,
help='Named pipe used to communicate port')
parser.add_argument('--source_dir', required=True,
help='Directory holding sources to serve.')
args = parser.parse_args()
server_app = ServerApp(args.source_dir)
server = wsgiref.simple_server.make_server(
'localhost', 0, server_app)
ip, port = server.server_address
logging.info('Listening on port %s at %s', port, args.source_dir)
if args.fifo:
fifo = file(args.fifo, 'w')
fifo.write('%s\n' % port)
fifo.flush()
fifo.close()
server.serve_forever()