blob: e9ed40e6b3dfa27211a33da8e9b20e8fa00231da [file] [log] [blame]
Daniel Dunbar2b0662c2008-09-19 23:32:11 +00001import BaseHTTPServer
2import SimpleHTTPServer
3import os
4import sys
5import urllib, urlparse
6import posixpath
7import StringIO
8import re
9import shutil
10import threading
11import time
12import socket
13
Daniel Dunbar61717b32008-09-20 01:43:16 +000014import Reporter
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000015import ConfigParser
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000016
17# Keys replaced by server.
18
Daniel Dunbar14cad832008-09-21 20:34:58 +000019kReportColRE = re.compile('<!-- REPORTBUGCOL -->')
20kReportColRepl = '<td></td>'
Daniel Dunbarf5d446f2008-09-22 18:05:49 +000021#<td></td>'
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000022kReportBugRE = re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->')
Daniel Dunbarf5d446f2008-09-22 18:05:49 +000023kReportBugRepl = '<td class="Button"><a href="report/\\1">Report Bug</a></td>'
24# +
25# '<td class="Button"><a href="open/\\1">Open File</a></td>')
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000026kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
27
Daniel Dunbar14cad832008-09-21 20:34:58 +000028kReportReplacements = [(kReportColRE, kReportColRepl),
29 (kReportBugRE, kReportBugRepl)]
30
Daniel Dunbar2b353482008-09-21 23:02:25 +000031# Other simple parameters
32
33kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000034kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbar2b353482008-09-21 23:02:25 +000035
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000036###
37
38__version__ = "0.1"
39
40__all__ = ["create_server"]
41
42class ReporterThread(threading.Thread):
43 def __init__(self, report, reporter, parameters, server):
44 threading.Thread.__init__(self)
45 self.report = report
46 self.server = server
47 self.reporter = reporter
48 self.parameters = parameters
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000049 self.success = False
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000050 self.status = None
51
52 def run(self):
53 result = None
54 try:
55 if self.server.options.debug:
56 print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000057 self.status = self.reporter.fileReport(self.report, self.parameters)
58 self.success = True
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000059 time.sleep(3)
60 if self.server.options.debug:
61 print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
Daniel Dunbar61717b32008-09-20 01:43:16 +000062 except Reporter.ReportFailure,e:
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000063 self.status = e.value
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000064 except Exception,e:
65 s = StringIO.StringIO()
66 import traceback
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000067 print >>s,'<b>Unhandled Exception</b><br><pre>'
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000068 traceback.print_exc(e,file=s)
69 print >>s,'</pre>'
70 self.status = s.getvalue()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000071
72class ScanViewServer(BaseHTTPServer.HTTPServer):
73 def __init__(self, address, handler, root, reporters, options):
74 BaseHTTPServer.HTTPServer.__init__(self, address, handler)
75 self.root = root
76 self.reporters = reporters
77 self.options = options
78 self.halted = False
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000079 self.config = None
80 self.load_config()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000081
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000082 def load_config(self):
83 self.config = ConfigParser.RawConfigParser()
84
85 # Add defaults
86 self.config.add_section('ScanView')
Daniel Dunbar3266baa2008-09-22 02:53:12 +000087 for r in self.reporters:
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000088 self.config.add_section(r.getName())
89 for p in r.getParameterNames():
90 self.config.set(r.getName(), p, '')
91
92 # Ignore parse errors
93 try:
94 self.config.read([kConfigPath])
95 except:
96 pass
97
98 # Save on exit
99 import atexit
100 atexit.register(lambda: self.save_config())
101
102 def save_config(self):
103 # Ignore errors (only called on exit).
104 try:
105 f = open(kConfigPath,'w')
106 self.config.write(f)
107 f.close()
108 except:
109 pass
110
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000111 def halt(self):
112 self.halted = True
113 if self.options.debug:
114 print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
115
116 def serve_forever(self):
117 while not self.halted:
118 if self.options.debug > 1:
119 print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
120 try:
121 self.handle_request()
122 except OSError,e:
123 print 'OSError',e.errno
124
Daniel Dunbar14cad832008-09-21 20:34:58 +0000125 def finish_request(self, request, client_address):
126 if self.options.autoReload:
127 import ScanView
128 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
129 BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
130
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000131 def handle_error(self, request, client_address):
132 # Ignore socket errors
133 info = sys.exc_info()
134 if info and isinstance(info[1], socket.error):
135 if self.options.debug > 1:
136 print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
137 return
Daniel Dunbar14cad832008-09-21 20:34:58 +0000138 BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000139
140# Borrowed from Quixote, with simplifications.
141def parse_query(qs, fields=None):
142 if fields is None:
143 fields = {}
144 for chunk in filter(None, qs.split('&')):
145 if '=' not in chunk:
146 name = chunk
147 value = ''
148 else:
149 name, value = chunk.split('=', 1)
150 name = urllib.unquote(name.replace('+', ' '))
151 value = urllib.unquote(value.replace('+', ' '))
152 fields[name] = value
153 return fields
154
155class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
156 server_version = "ScanViewServer/" + __version__
Daniel Dunbar14cad832008-09-21 20:34:58 +0000157 dynamic_mtime = time.time()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000158
159 def do_HEAD(self):
160 try:
161 SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
162 except Exception,e:
163 self.handle_exception(e)
164
165 def do_GET(self):
166 try:
167 SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
168 except Exception,e:
169 self.handle_exception(e)
170
171 def do_POST(self):
172 """Serve a POST request."""
173 try:
174 length = self.headers.getheader('content-length') or "0"
175 try:
176 length = int(length)
177 except:
178 length = 0
179 content = self.rfile.read(length)
180 fields = parse_query(content)
181 f = self.send_head(fields)
182 if f:
183 self.copyfile(f, self.wfile)
184 f.close()
185 except Exception,e:
186 self.handle_exception(e)
187
188 def log_message(self, format, *args):
189 if self.server.options.debug:
190 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
191 (sys.argv[0],
192 self.address_string(),
193 self.log_date_time_string(),
194 format%args))
195
196 def load_report(self, report):
197 path = os.path.join(self.server.root, 'report-%s.html'%report)
198 data = open(path).read()
199 keys = {}
200 for item in kBugKeyValueRE.finditer(data):
201 k,v = item.groups()
202 keys[k] = v
203 return keys
204
205 def handle_exception(self, exc):
206 import traceback
207 s = StringIO.StringIO()
208 print >>s, "INTERNAL ERROR\n"
209 traceback.print_exc(exc, s)
210 f = self.send_string(s.getvalue(), 'text/plain')
211 if f:
212 self.copyfile(f, self.wfile)
213 f.close()
214
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000215 def submit_bug(self):
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000216 title = self.fields.get('title')
217 description = self.fields.get('description')
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000218 report = self.fields.get('report')
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000219 reporterIndex = self.fields.get('reporter')
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000220
221 # Type check form parameters.
222 reportPath = posixpath.join(self.server.root,
223 'report-%s.html' % report)
224 if not posixpath.exists(reportPath):
225 return (False, "Invalid report ID.")
226 if not title:
227 return (False, "Missing title.")
228 if not description:
229 return (False, "Missing description.")
230 try:
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000231 reporterIndex = int(reporterIndex)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000232 except:
233 return (False, "Invalid report method.")
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000234
235 # Get the reporter and parameters.
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000236 reporter = self.server.reporters[reporterIndex]
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000237 parameters = {}
238 for o in reporter.getParameterNames():
239 name = '%s_%s'%(reporter.getName(),o)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000240 if name not in self.fields:
241 return (False,
242 'Missing field "%s" for %s report method.'%(name,
243 reporter.getName()))
244 parameters[o] = self.fields[name]
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000245
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000246 # Update config defaults.
247 self.server.config.set('ScanView', 'reporter', reporterIndex)
248 for o in reporter.getParameterNames():
249 self.server.config.set(reporter.getName(), o, parameters[o])
250
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000251 # Create the report.
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000252 bug = Reporter.BugReport(title, description, [reportPath])
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000253
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000254 # Kick off a reporting thread.
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000255 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000256 t.start()
257
258 # Wait for thread to die...
259 while t.isAlive():
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000260 time.sleep(.25)
261 submitStatus = t.status
262
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000263 return (t.success, t.status)
264
265 def send_report_submit(self):
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000266 report = self.fields.get('report')
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000267 title = self.fields.get('title')
268 description = self.fields.get('description')
269
270 res,message = self.submit_bug()
271
272 if res:
273 statusClass = 'SubmitOk'
274 statusName = 'Succeeded'
275 else:
276 statusClass = 'SubmitFail'
277 statusName = 'Failed'
278
279 result = """
280<head>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000281 <title>Bug Submission</title>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000282 <link rel="stylesheet" type="text/css" href="/scanview.css" />
283</head>
284<body>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000285<h3>
286<a href="/">Summary</a> >
287<a href="/report-%(report)s.html">Report %(report)s</a> >
288<a href="/report/%(report)s">File Bug</a> >
289Submit</h3>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000290<form name="form" action="">
291<table class="form">
292<tr><td>
293<table class="form_group">
294<tr>
295 <td class="form_clabel">Title:</td>
296 <td class="form_value">
297 <input type="text" name="title" size="50" value="%(title)s" disabled>
298 </td>
299</tr>
300<tr>
301 <td class="form_label">Description:</td>
302 <td class="form_value">
303<textarea rows="10" cols="80" name="description" disabled>
304%(description)s
305</textarea>
306 </td>
307</table>
308</td></tr>
309</table>
310</form>
311<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
312%(message)s
313<p>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000314<hr>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000315<a href="/">Return to Summary</a>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000316</body>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000317</html>"""%locals()
318 return self.send_string(result)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000319
Daniel Dunbarf5d446f2008-09-22 18:05:49 +0000320 def send_open_report(self, report):
321 try:
322 keys = self.load_report(report)
323 except IOError:
324 return self.send_error(400, 'Invalid report.')
325
326 file = keys.get('FILE')
327 if not file or not posixpath.exists(file):
328 return self.send_error(400, 'File does not exist: "%s"' % file)
329
330 import startfile
331 if self.server.options.debug:
332 print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
333 file)
334
335 status = startfile.open(file)
336 if status:
337 res = 'Opened: "%s"' % file
338 else:
339 res = 'Open failed: "%s"' % file
340
341 return self.send_string(res, 'text/plain')
342
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000343 def send_report(self, report):
344 try:
345 keys = self.load_report(report)
346 except IOError:
Daniel Dunbarfe421482008-09-22 01:40:14 +0000347 return self.send_error(400, 'Invalid report.')
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000348
349 initialTitle = keys.get('DESC','')
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000350 initialDescription = """\
351Bug generated by the clang static analyzer.
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000352
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000353Description: %s
354File: %s
355Line: %s
356"""%(initialTitle,
357 keys.get('FILE','<unknown>'),
358 keys.get('LINE','<unknown>'))
359
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000360 reporterSelections = []
361 reporterOptions = []
362
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000363 try:
364 active = self.server.config.getint('ScanView','reporter')
365 except:
366 active = 0
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000367 for i,r in enumerate(self.server.reporters):
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000368 selected = (i == active)
369 if selected:
370 selectedStr = ' selected'
371 else:
372 selectedStr = ''
373 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Daniel Dunbar0174b282008-09-22 00:11:51 +0000374 options = '\n'.join(["""\
375<tr>
376 <td class="form_clabel">%s:</td>
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000377 <td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
378</tr>"""%(o,r.getName(),o,self.server.config.get(r.getName(), o)) for o in r.getParameterNames()])
379 display = ('none','')[selected]
Daniel Dunbar0174b282008-09-22 00:11:51 +0000380 reporterOptions.append("""\
381<tr id="%sReporterOptions" style="display:%s">
382 <td class="form_label">%s Options</td>
383 <td class="form_value">
384 <table class="form_inner_group">
385%s
386 </table>
387 </td>
388</tr>
389"""%(r.getName(),display,r.getName(),options))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000390 reporterSelections = '\n'.join(reporterSelections)
391 reporterOptionsDivs = '\n'.join(reporterOptions)
392 reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
393
394 result = """<html>
395<head>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000396 <title>File Bug</title>
Daniel Dunbar0174b282008-09-22 00:11:51 +0000397 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000398</head>
399<script language="javascript" type="text/javascript">
400var reporters = %(reportersArray)s;
401function updateReporterOptions() {
402 index = document.getElementById('reporter').selectedIndex;
403 for (var i=0; i < reporters.length; ++i) {
404 o = document.getElementById(reporters[i] + "ReporterOptions");
405 if (i == index) {
Daniel Dunbar0174b282008-09-22 00:11:51 +0000406 o.style.display = "";
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000407 } else {
408 o.style.display = "none";
409 }
410 }
411}
412</script>
Daniel Dunbarfe421482008-09-22 01:40:14 +0000413<body onLoad="updateReporterOptions()">
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000414<h3>
415<a href="/">Summary</a> >
416<a href="/report-%(report)s.html">Report %(report)s</a> >
417File Bug</h3>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000418<form name="form" action="/report_submit" method="post">
Daniel Dunbar0174b282008-09-22 00:11:51 +0000419<input type="hidden" name="report" value="%(report)s">
420
421<table class="form">
422<tr><td>
423<table class="form_group">
424<tr>
425 <td class="form_clabel">Title:</td>
426 <td class="form_value">
427 <input type="text" name="title" size="50" value="%(initialTitle)s">
428 </td>
429</tr>
430<tr>
431 <td class="form_label">Description:</td>
432 <td class="form_value">
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000433<textarea rows="10" cols="80" name="description">
434%(initialDescription)s
Daniel Dunbar0174b282008-09-22 00:11:51 +0000435</textarea>
436 </td>
437</table>
438<br>
439<table class="form_group">
440<tr>
441 <td class="form_clabel">Method:</td>
442 <td class="form_value">
443 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
444 %(reporterSelections)s
445 </select>
446 </td>
447</tr>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000448%(reporterOptionsDivs)s
Daniel Dunbar0174b282008-09-22 00:11:51 +0000449</table>
450<br>
451</td></tr>
452<tr><td class="form_submit">
453 <input align="right" type="submit" name="Submit" value="Submit">
454</td></tr>
455</table>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000456</form>
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000457
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000458<iframe src="/report-%(report)s.html" width="100%%" height="40%%"
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000459 scrolling="auto" frameborder="1">
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000460 <a href="/report-%(report)s.html">View Bug Report</a>
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000461</iframe>
462
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000463</body>
464</html>"""%locals()
465 return self.send_string(result)
466
467 def send_head(self, fields=None):
468 if fields is None:
469 fields = {}
470 self.fields = fields
471
472 o = urlparse.urlparse(self.path)
473 self.fields = parse_query(o.query, fields)
474 path = posixpath.normpath(urllib.unquote(o.path))
475
476 # Split the components and strip the root prefix.
477 components = path.split('/')[1:]
478
479 # Special case some top-level entries.
480 if components:
481 name = components[0]
Daniel Dunbar0174b282008-09-22 00:11:51 +0000482 if len(components)==2:
483 if name=='report':
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000484 return self.send_report(components[1])
Daniel Dunbarf5d446f2008-09-22 18:05:49 +0000485 elif name=='open':
486 return self.send_open_report(components[1])
Daniel Dunbar0174b282008-09-22 00:11:51 +0000487 elif len(components)==1:
488 if name=='quit':
489 self.server.halt()
490 return self.send_string('Goodbye.', 'text/plain')
491 elif name=='report_submit':
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000492 return self.send_report_submit()
Daniel Dunbar0174b282008-09-22 00:11:51 +0000493 elif name=='favicon.ico':
Daniel Dunbar2b353482008-09-21 23:02:25 +0000494 return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000495
496 # Match directory entries.
497 if components[-1] == '':
498 components[-1] = 'index.html'
Daniel Dunbar2b353482008-09-21 23:02:25 +0000499
500 suffix = '/'.join(components)
501
502 # The summary may reference source files on disk using rooted
503 # paths. Make sure these resolve correctly for now.
504 # FIXME: This isn't a very good idea... we should probably
505 # mark rooted paths somehow.
506 if os.path.exists(posixpath.join('/', suffix)):
507 path = posixpath.join('/', suffix)
508 else:
509 path = posixpath.join(self.server.root, suffix)
510
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000511 if self.server.options.debug > 1:
512 print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
513 path)
514 return self.send_path(path)
515
516 def send_404(self):
517 self.send_error(404, "File not found")
518 return None
519
520 def send_path(self, path):
521 ctype = self.guess_type(path)
522 if ctype.startswith('text/'):
523 # Patch file instead
524 return self.send_patched_file(path, ctype)
525 else:
526 mode = 'rb'
527 try:
528 f = open(path, mode)
529 except IOError:
530 return self.send_404()
531 return self.send_file(f, ctype)
532
533 def send_file(self, f, ctype):
534 # Patch files to add links, but skip binary files.
535 self.send_response(200)
536 self.send_header("Content-type", ctype)
537 fs = os.fstat(f.fileno())
538 self.send_header("Content-Length", str(fs[6]))
539 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
540 self.end_headers()
541 return f
542
543 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
544 if headers:
545 self.send_response(200)
546 self.send_header("Content-type", ctype)
547 self.send_header("Content-Length", str(len(s)))
Daniel Dunbar14cad832008-09-21 20:34:58 +0000548 if mtime is None:
549 mtime = self.dynamic_mtime
550 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000551 self.end_headers()
552 return StringIO.StringIO(s)
553
554 def send_patched_file(self, path, ctype):
Daniel Dunbarfe421482008-09-22 01:40:14 +0000555 try:
556 f = open(path,'r')
557 except IOError:
558 return self.send_404()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000559 fs = os.fstat(f.fileno())
560 data = f.read()
Daniel Dunbar14cad832008-09-21 20:34:58 +0000561 for a,b in kReportReplacements:
562 data = a.sub(b, data)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000563 return self.send_string(data, ctype, mtime=fs.st_mtime)
564
565
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000566def create_server(address, options, root):
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000567 import Reporter
568
569 reporters = Reporter.getReporters()
570
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000571 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000572 root,
573 reporters,
574 options)