Fix dom_distiller_content_java deps
[chromium-blink-merge.git] / tools / oopif / iframe_server.py
blobace584220035595e9bfd1d3e874f1a388f61f730
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Test server for generating nested iframes with different sites.
7 Very simple python server for creating a bunch of iframes. The page generation
8 is randomized based on query parameters. See the __init__ function of the
9 Params class for a description of the parameters.
11 This server relies on gevent. On Ubuntu, install it via:
13 sudo apt-get install python-gevent
15 Run the server using
17 python iframe_server.py
19 To use the server, run chrome as follows:
21 google-chrome --host-resolver-rules='map *.invalid 127.0.0.1'
23 Change 127.0.0.1 to be the IP of the machine this server is running on. Then
24 in this chrome instance, navigate to any domain in .invalid
25 (eg., http://1.invalid:8090) to run this test.
27 """
29 import colorsys
30 import copy
31 import random
32 import urllib
33 import urlparse
35 from gevent import pywsgi # pylint: disable=F0401
37 MAIN_PAGE = """
38 <html>
39 <head>
40 <style>
41 body {
42 background-color: %(color)s;
44 </style>
45 </head>
46 <body>
47 <center>
48 <h1><a href="%(url)s">%(site)s</a></h1>
49 <p><small>%(url)s</small>
50 </center>
51 <br />
52 %(iframe_html)s
53 </body>
54 </html>
55 """
57 IFRAME_FRAGMENT = """
58 <iframe src="%(src)s" width="%(width)s" height="%(height)s">
59 </iframe>
60 """
62 class Params(object):
63 """Simple object for holding parameters"""
64 def __init__(self, query_dict):
65 # Basic params:
66 # nframes is how many frames per page.
67 # nsites is how many sites to random choose out of.
68 # depth is how deep to make the frame tree
69 # pattern specifies how the sites are layed out per depth. An empty string
70 # uses a random N = [0, nsites] each time to generate a N.invalid URL.
71 # Otherwise sepcify with single letters like 'ABCA' and frame
72 # A.invalid will embed B.invalid will embed C.invalid will embed A.
73 # jitter is the amount of randomness applied to nframes and nsites.
74 # Should be from [0,1]. 0.0 means no jitter.
75 # size_jitter is like jitter, but for width and height.
76 self.nframes = int(query_dict.get('nframes', [4] )[0])
77 self.nsites = int(query_dict.get('nsites', [10] )[0])
78 self.depth = int(query_dict.get('depth', [1] )[0])
79 self.jitter = float(query_dict.get('jitter', [0] )[0])
80 self.size_jitter = float(query_dict.get('size_jitter', [0.5] )[0])
81 self.pattern = query_dict.get('pattern', [''] )[0]
82 self.pattern_pos = int(query_dict.get('pattern_pos', [0] )[0])
84 # Size parameters. Values are percentages.
85 self.width = int(query_dict.get('width', [60])[0])
86 self.height = int(query_dict.get('height', [50])[0])
88 # Pass the random seed so our pages are reproduceable.
89 self.seed = int(query_dict.get('seed',
90 [random.randint(0, 2147483647)])[0])
93 def get_site(urlpath):
94 """Takes a urlparse object and finds its approximate site.
96 Site is defined as registered domain name + scheme. We approximate
97 registered domain name by preserving the last 2 elements of the DNS
98 name. This breaks for domains like co.uk.
99 """
100 no_port = urlpath.netloc.split(':')[0]
101 host_parts = no_port.split('.')
102 site_host = '.'.join(host_parts[-2:])
103 return '%s://%s' % (urlpath.scheme, site_host)
106 def generate_host(rand, params):
107 """Generates the host to be used as an iframes source.
109 Uses the .invalid domain to ensure DNS will not resolve to any real
110 address.
112 if params.pattern:
113 host = params.pattern[params.pattern_pos]
114 params.pattern_pos = (params.pattern_pos + 1) % len(params.pattern)
115 else:
116 host = rand.randint(1, apply_jitter(rand, params.jitter, params.nsites))
117 return '%s.invalid' % host
120 def apply_jitter(rand, jitter, n):
121 """Reduce n by random amount from [0, jitter]. Ensures result is >=1."""
122 if jitter <= 0.001:
123 return n
124 v = n - int(n * rand.uniform(0, jitter))
125 if v:
126 return v
127 else:
128 return 1
131 def get_color_for_site(site):
132 """Generate a stable (and pretty-ish) color for a site."""
133 val = hash(site)
134 # The constants below are arbitrary chosen emperically to look "pretty."
135 # HSV is used because it is easier to control the color than RGB.
136 # Reducing the H to 0.6 produces a good range of colors. Preserving
137 # > 0.5 saturation and value means the colors won't be too washed out.
138 h = (val % 100)/100.0 * 0.6
139 s = 1.0 - (int(val/100) % 100)/200.
140 v = 1.0 - (int(val/10000) % 100)/200.0
141 (r, g, b) = colorsys.hsv_to_rgb(h, s, v)
142 return 'rgb(%d, %d, %d)' % (int(r * 255), int(g * 255), int(b * 255))
145 def make_src(scheme, netloc, path, params):
146 """Constructs the src url that will recreate the given params."""
147 if path == '/':
148 path = ''
149 return '%(scheme)s://%(netloc)s%(path)s?%(params)s' % {
150 'scheme': scheme,
151 'netloc': netloc,
152 'path': path,
153 'params': urllib.urlencode(params.__dict__),
157 def make_iframe_html(urlpath, params):
158 """Produces the HTML fragment for the iframe."""
159 if (params.depth <= 0):
160 return ''
161 # Ensure a stable random number per iframe.
162 rand = random.Random()
163 rand.seed(params.seed)
165 netloc_paths = urlpath.netloc.split(':')
166 netloc_paths[0] = generate_host(rand, params)
168 width = apply_jitter(rand, params.size_jitter, params.width)
169 height = apply_jitter(rand, params.size_jitter, params.height)
170 iframe_params = {
171 'src': make_src(urlpath.scheme, ':'.join(netloc_paths),
172 urlpath.path, params),
173 'width': '%d%%' % width,
174 'height': '%d%%' % height,
176 return IFRAME_FRAGMENT % iframe_params
179 def create_html(environ):
180 """Creates the current HTML page. Also parses out query parameters."""
181 urlpath = urlparse.urlparse('%s://%s%s?%s' % (
182 environ['wsgi.url_scheme'],
183 environ['HTTP_HOST'],
184 environ['PATH_INFO'],
185 environ['QUERY_STRING']))
186 site = get_site(urlpath)
187 params = Params(urlparse.parse_qs(urlpath.query))
189 rand = random.Random()
190 rand.seed(params.seed)
192 iframe_htmls = []
193 for frame in xrange(0, apply_jitter(rand, params.jitter, params.nframes)):
194 # Copy current parameters into iframe and make modifications
195 # for the recursive generation.
196 iframe_params = copy.copy(params)
197 iframe_params.depth = params.depth - 1
198 # Base the new seed off the current seed, but have it skip enough that
199 # different frame trees are unlikely to collide. Numbers and skips
200 # not chosen in any scientific manner at all.
201 iframe_params.seed = params.seed + (frame + 1) * (
202 1000000 + params.depth + 333)
203 iframe_htmls.append(make_iframe_html(urlpath, iframe_params))
204 template_params = dict(params.__dict__)
205 template_params.update({
206 'color': get_color_for_site(site),
207 'iframe_html': '\n'.join(iframe_htmls),
208 'site': site,
209 'url': make_src(urlpath.scheme, urlpath.netloc, urlpath.path, params),
211 return MAIN_PAGE % template_params
214 def application(environ, start_response):
215 start_response('200 OK', [('Content-Type', 'text/html')])
216 if environ['PATH_INFO'] == '/favicon.ico':
217 yield ''
218 else:
219 yield create_html(environ)
222 server = pywsgi.WSGIServer(('', 8090), application)
224 server.serve_forever()