blob: f9a8cbd879d5954700fb3be27fe4c468f9dd6437 [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 Dunbar2b0662c2008-09-19 23:32:11 +000019kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
20
Daniel Dunbar6fd6bd02008-09-22 18:44:46 +000021kReportReplacements = []
Daniel Dunbar14cad832008-09-21 20:34:58 +000022
Daniel Dunbar6fd6bd02008-09-22 18:44:46 +000023# Add custom javascript.
24kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
25<script language="javascript" type="text/javascript">
26function load(url) {
27 if (window.XMLHttpRequest) {
28 req = new XMLHttpRequest();
29 } else if (window.ActiveXObject) {
30 req = new ActiveXObject("Microsoft.XMLHTTP");
31 }
32 if (req != undefined) {
33 req.open("GET", url, true);
34 req.send("");
35 }
36}
37</script>"""))
38
39# Insert additional columns.
40kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
41 '<td></td><td></td>'))
42
43# Insert report bug and open file links.
44kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
45 ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
46 '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
47
48###
Daniel Dunbar2b353482008-09-21 23:02:25 +000049# Other simple parameters
50
51kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000052kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbar2b353482008-09-21 23:02:25 +000053
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000054###
55
56__version__ = "0.1"
57
58__all__ = ["create_server"]
59
60class ReporterThread(threading.Thread):
61 def __init__(self, report, reporter, parameters, server):
62 threading.Thread.__init__(self)
63 self.report = report
64 self.server = server
65 self.reporter = reporter
66 self.parameters = parameters
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000067 self.success = False
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000068 self.status = None
69
70 def run(self):
71 result = None
72 try:
73 if self.server.options.debug:
74 print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000075 self.status = self.reporter.fileReport(self.report, self.parameters)
76 self.success = True
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000077 time.sleep(3)
78 if self.server.options.debug:
79 print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
Daniel Dunbar61717b32008-09-20 01:43:16 +000080 except Reporter.ReportFailure,e:
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000081 self.status = e.value
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000082 except Exception,e:
83 s = StringIO.StringIO()
84 import traceback
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000085 print >>s,'<b>Unhandled Exception</b><br><pre>'
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000086 traceback.print_exc(e,file=s)
87 print >>s,'</pre>'
88 self.status = s.getvalue()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000089
90class ScanViewServer(BaseHTTPServer.HTTPServer):
91 def __init__(self, address, handler, root, reporters, options):
92 BaseHTTPServer.HTTPServer.__init__(self, address, handler)
93 self.root = root
94 self.reporters = reporters
95 self.options = options
96 self.halted = False
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000097 self.config = None
98 self.load_config()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000099
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000100 def load_config(self):
101 self.config = ConfigParser.RawConfigParser()
102
103 # Add defaults
104 self.config.add_section('ScanView')
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000105 for r in self.reporters:
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000106 self.config.add_section(r.getName())
107 for p in r.getParameterNames():
108 self.config.set(r.getName(), p, '')
109
110 # Ignore parse errors
111 try:
112 self.config.read([kConfigPath])
113 except:
114 pass
115
116 # Save on exit
117 import atexit
118 atexit.register(lambda: self.save_config())
119
120 def save_config(self):
121 # Ignore errors (only called on exit).
122 try:
123 f = open(kConfigPath,'w')
124 self.config.write(f)
125 f.close()
126 except:
127 pass
128
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000129 def halt(self):
130 self.halted = True
131 if self.options.debug:
132 print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
133
134 def serve_forever(self):
135 while not self.halted:
136 if self.options.debug > 1:
137 print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
138 try:
139 self.handle_request()
140 except OSError,e:
141 print 'OSError',e.errno
142
Daniel Dunbar14cad832008-09-21 20:34:58 +0000143 def finish_request(self, request, client_address):
144 if self.options.autoReload:
145 import ScanView
146 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
147 BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
148
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000149 def handle_error(self, request, client_address):
150 # Ignore socket errors
151 info = sys.exc_info()
152 if info and isinstance(info[1], socket.error):
153 if self.options.debug > 1:
154 print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
155 return
Daniel Dunbar14cad832008-09-21 20:34:58 +0000156 BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000157
158# Borrowed from Quixote, with simplifications.
159def parse_query(qs, fields=None):
160 if fields is None:
161 fields = {}
162 for chunk in filter(None, qs.split('&')):
163 if '=' not in chunk:
164 name = chunk
165 value = ''
166 else:
167 name, value = chunk.split('=', 1)
168 name = urllib.unquote(name.replace('+', ' '))
169 value = urllib.unquote(value.replace('+', ' '))
170 fields[name] = value
171 return fields
172
173class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
174 server_version = "ScanViewServer/" + __version__
Daniel Dunbar14cad832008-09-21 20:34:58 +0000175 dynamic_mtime = time.time()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000176
177 def do_HEAD(self):
178 try:
179 SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
180 except Exception,e:
181 self.handle_exception(e)
182
183 def do_GET(self):
184 try:
185 SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
186 except Exception,e:
187 self.handle_exception(e)
188
189 def do_POST(self):
190 """Serve a POST request."""
191 try:
192 length = self.headers.getheader('content-length') or "0"
193 try:
194 length = int(length)
195 except:
196 length = 0
197 content = self.rfile.read(length)
198 fields = parse_query(content)
199 f = self.send_head(fields)
200 if f:
201 self.copyfile(f, self.wfile)
202 f.close()
203 except Exception,e:
204 self.handle_exception(e)
205
206 def log_message(self, format, *args):
207 if self.server.options.debug:
208 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
209 (sys.argv[0],
210 self.address_string(),
211 self.log_date_time_string(),
212 format%args))
213
214 def load_report(self, report):
215 path = os.path.join(self.server.root, 'report-%s.html'%report)
216 data = open(path).read()
217 keys = {}
218 for item in kBugKeyValueRE.finditer(data):
219 k,v = item.groups()
220 keys[k] = v
221 return keys
222
223 def handle_exception(self, exc):
224 import traceback
225 s = StringIO.StringIO()
226 print >>s, "INTERNAL ERROR\n"
227 traceback.print_exc(exc, s)
228 f = self.send_string(s.getvalue(), 'text/plain')
229 if f:
230 self.copyfile(f, self.wfile)
231 f.close()
232
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000233 def submit_bug(self):
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000234 title = self.fields.get('title')
235 description = self.fields.get('description')
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000236 report = self.fields.get('report')
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000237 reporterIndex = self.fields.get('reporter')
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000238
239 # Type check form parameters.
240 reportPath = posixpath.join(self.server.root,
241 'report-%s.html' % report)
242 if not posixpath.exists(reportPath):
243 return (False, "Invalid report ID.")
244 if not title:
245 return (False, "Missing title.")
246 if not description:
247 return (False, "Missing description.")
248 try:
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000249 reporterIndex = int(reporterIndex)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000250 except:
251 return (False, "Invalid report method.")
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000252
253 # Get the reporter and parameters.
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000254 reporter = self.server.reporters[reporterIndex]
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000255 parameters = {}
256 for o in reporter.getParameterNames():
257 name = '%s_%s'%(reporter.getName(),o)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000258 if name not in self.fields:
259 return (False,
260 'Missing field "%s" for %s report method.'%(name,
261 reporter.getName()))
262 parameters[o] = self.fields[name]
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000263
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000264 # Update config defaults.
265 self.server.config.set('ScanView', 'reporter', reporterIndex)
266 for o in reporter.getParameterNames():
267 self.server.config.set(reporter.getName(), o, parameters[o])
268
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000269 # Create the report.
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000270 bug = Reporter.BugReport(title, description, [reportPath])
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000271
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000272 # Kick off a reporting thread.
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000273 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000274 t.start()
275
276 # Wait for thread to die...
277 while t.isAlive():
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000278 time.sleep(.25)
279 submitStatus = t.status
280
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000281 return (t.success, t.status)
282
283 def send_report_submit(self):
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000284 report = self.fields.get('report')
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000285 title = self.fields.get('title')
286 description = self.fields.get('description')
287
288 res,message = self.submit_bug()
289
290 if res:
291 statusClass = 'SubmitOk'
292 statusName = 'Succeeded'
293 else:
294 statusClass = 'SubmitFail'
295 statusName = 'Failed'
296
297 result = """
298<head>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000299 <title>Bug Submission</title>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000300 <link rel="stylesheet" type="text/css" href="/scanview.css" />
301</head>
302<body>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000303<h3>
304<a href="/">Summary</a> >
305<a href="/report-%(report)s.html">Report %(report)s</a> >
306<a href="/report/%(report)s">File Bug</a> >
307Submit</h3>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000308<form name="form" action="">
309<table class="form">
310<tr><td>
311<table class="form_group">
312<tr>
313 <td class="form_clabel">Title:</td>
314 <td class="form_value">
315 <input type="text" name="title" size="50" value="%(title)s" disabled>
316 </td>
317</tr>
318<tr>
319 <td class="form_label">Description:</td>
320 <td class="form_value">
321<textarea rows="10" cols="80" name="description" disabled>
322%(description)s
323</textarea>
324 </td>
325</table>
326</td></tr>
327</table>
328</form>
329<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
330%(message)s
331<p>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000332<hr>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000333<a href="/">Return to Summary</a>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000334</body>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000335</html>"""%locals()
336 return self.send_string(result)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000337
Daniel Dunbarf5d446f2008-09-22 18:05:49 +0000338 def send_open_report(self, report):
339 try:
340 keys = self.load_report(report)
341 except IOError:
342 return self.send_error(400, 'Invalid report.')
343
344 file = keys.get('FILE')
345 if not file or not posixpath.exists(file):
346 return self.send_error(400, 'File does not exist: "%s"' % file)
347
348 import startfile
349 if self.server.options.debug:
350 print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
351 file)
352
353 status = startfile.open(file)
354 if status:
355 res = 'Opened: "%s"' % file
356 else:
357 res = 'Open failed: "%s"' % file
358
359 return self.send_string(res, 'text/plain')
360
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000361 def send_report(self, report):
362 try:
363 keys = self.load_report(report)
364 except IOError:
Daniel Dunbarfe421482008-09-22 01:40:14 +0000365 return self.send_error(400, 'Invalid report.')
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000366
367 initialTitle = keys.get('DESC','')
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000368 initialDescription = """\
369Bug generated by the clang static analyzer.
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000370
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000371Description: %s
372File: %s
373Line: %s
374"""%(initialTitle,
375 keys.get('FILE','<unknown>'),
376 keys.get('LINE','<unknown>'))
377
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000378 reporterSelections = []
379 reporterOptions = []
380
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000381 try:
382 active = self.server.config.getint('ScanView','reporter')
383 except:
384 active = 0
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000385 for i,r in enumerate(self.server.reporters):
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000386 selected = (i == active)
387 if selected:
388 selectedStr = ' selected'
389 else:
390 selectedStr = ''
391 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Daniel Dunbar0174b282008-09-22 00:11:51 +0000392 options = '\n'.join(["""\
393<tr>
394 <td class="form_clabel">%s:</td>
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000395 <td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
396</tr>"""%(o,r.getName(),o,self.server.config.get(r.getName(), o)) for o in r.getParameterNames()])
397 display = ('none','')[selected]
Daniel Dunbar0174b282008-09-22 00:11:51 +0000398 reporterOptions.append("""\
399<tr id="%sReporterOptions" style="display:%s">
400 <td class="form_label">%s Options</td>
401 <td class="form_value">
402 <table class="form_inner_group">
403%s
404 </table>
405 </td>
406</tr>
407"""%(r.getName(),display,r.getName(),options))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000408 reporterSelections = '\n'.join(reporterSelections)
409 reporterOptionsDivs = '\n'.join(reporterOptions)
410 reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
411
412 result = """<html>
413<head>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000414 <title>File Bug</title>
Daniel Dunbar0174b282008-09-22 00:11:51 +0000415 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000416</head>
417<script language="javascript" type="text/javascript">
418var reporters = %(reportersArray)s;
419function updateReporterOptions() {
420 index = document.getElementById('reporter').selectedIndex;
421 for (var i=0; i < reporters.length; ++i) {
422 o = document.getElementById(reporters[i] + "ReporterOptions");
423 if (i == index) {
Daniel Dunbar0174b282008-09-22 00:11:51 +0000424 o.style.display = "";
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000425 } else {
426 o.style.display = "none";
427 }
428 }
429}
430</script>
Daniel Dunbarfe421482008-09-22 01:40:14 +0000431<body onLoad="updateReporterOptions()">
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000432<h3>
433<a href="/">Summary</a> >
434<a href="/report-%(report)s.html">Report %(report)s</a> >
435File Bug</h3>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000436<form name="form" action="/report_submit" method="post">
Daniel Dunbar0174b282008-09-22 00:11:51 +0000437<input type="hidden" name="report" value="%(report)s">
438
439<table class="form">
440<tr><td>
441<table class="form_group">
442<tr>
443 <td class="form_clabel">Title:</td>
444 <td class="form_value">
445 <input type="text" name="title" size="50" value="%(initialTitle)s">
446 </td>
447</tr>
448<tr>
449 <td class="form_label">Description:</td>
450 <td class="form_value">
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000451<textarea rows="10" cols="80" name="description">
452%(initialDescription)s
Daniel Dunbar0174b282008-09-22 00:11:51 +0000453</textarea>
454 </td>
455</table>
456<br>
457<table class="form_group">
458<tr>
459 <td class="form_clabel">Method:</td>
460 <td class="form_value">
461 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
462 %(reporterSelections)s
463 </select>
464 </td>
465</tr>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000466%(reporterOptionsDivs)s
Daniel Dunbar0174b282008-09-22 00:11:51 +0000467</table>
468<br>
469</td></tr>
470<tr><td class="form_submit">
471 <input align="right" type="submit" name="Submit" value="Submit">
472</td></tr>
473</table>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000474</form>
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000475
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000476<iframe src="/report-%(report)s.html" width="100%%" height="40%%"
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000477 scrolling="auto" frameborder="1">
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000478 <a href="/report-%(report)s.html">View Bug Report</a>
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000479</iframe>
480
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000481</body>
482</html>"""%locals()
483 return self.send_string(result)
484
485 def send_head(self, fields=None):
486 if fields is None:
487 fields = {}
488 self.fields = fields
489
490 o = urlparse.urlparse(self.path)
491 self.fields = parse_query(o.query, fields)
492 path = posixpath.normpath(urllib.unquote(o.path))
493
494 # Split the components and strip the root prefix.
495 components = path.split('/')[1:]
496
497 # Special case some top-level entries.
498 if components:
499 name = components[0]
Daniel Dunbar0174b282008-09-22 00:11:51 +0000500 if len(components)==2:
501 if name=='report':
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000502 return self.send_report(components[1])
Daniel Dunbarf5d446f2008-09-22 18:05:49 +0000503 elif name=='open':
504 return self.send_open_report(components[1])
Daniel Dunbar0174b282008-09-22 00:11:51 +0000505 elif len(components)==1:
506 if name=='quit':
507 self.server.halt()
508 return self.send_string('Goodbye.', 'text/plain')
509 elif name=='report_submit':
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000510 return self.send_report_submit()
Daniel Dunbar0174b282008-09-22 00:11:51 +0000511 elif name=='favicon.ico':
Daniel Dunbar2b353482008-09-21 23:02:25 +0000512 return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000513
514 # Match directory entries.
515 if components[-1] == '':
516 components[-1] = 'index.html'
Daniel Dunbar2b353482008-09-21 23:02:25 +0000517
518 suffix = '/'.join(components)
519
520 # The summary may reference source files on disk using rooted
521 # paths. Make sure these resolve correctly for now.
522 # FIXME: This isn't a very good idea... we should probably
523 # mark rooted paths somehow.
524 if os.path.exists(posixpath.join('/', suffix)):
525 path = posixpath.join('/', suffix)
526 else:
527 path = posixpath.join(self.server.root, suffix)
528
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000529 if self.server.options.debug > 1:
530 print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
531 path)
532 return self.send_path(path)
533
534 def send_404(self):
535 self.send_error(404, "File not found")
536 return None
537
538 def send_path(self, path):
539 ctype = self.guess_type(path)
540 if ctype.startswith('text/'):
541 # Patch file instead
542 return self.send_patched_file(path, ctype)
543 else:
544 mode = 'rb'
545 try:
546 f = open(path, mode)
547 except IOError:
548 return self.send_404()
549 return self.send_file(f, ctype)
550
551 def send_file(self, f, ctype):
552 # Patch files to add links, but skip binary files.
553 self.send_response(200)
554 self.send_header("Content-type", ctype)
555 fs = os.fstat(f.fileno())
556 self.send_header("Content-Length", str(fs[6]))
557 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
558 self.end_headers()
559 return f
560
561 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
562 if headers:
563 self.send_response(200)
564 self.send_header("Content-type", ctype)
565 self.send_header("Content-Length", str(len(s)))
Daniel Dunbar14cad832008-09-21 20:34:58 +0000566 if mtime is None:
567 mtime = self.dynamic_mtime
568 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000569 self.end_headers()
570 return StringIO.StringIO(s)
571
572 def send_patched_file(self, path, ctype):
Daniel Dunbarfe421482008-09-22 01:40:14 +0000573 try:
574 f = open(path,'r')
575 except IOError:
576 return self.send_404()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000577 fs = os.fstat(f.fileno())
578 data = f.read()
Daniel Dunbar14cad832008-09-21 20:34:58 +0000579 for a,b in kReportReplacements:
580 data = a.sub(b, data)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000581 return self.send_string(data, ctype, mtime=fs.st_mtime)
582
583
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000584def create_server(address, options, root):
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000585 import Reporter
586
587 reporters = Reporter.getReporters()
588
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000589 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000590 root,
591 reporters,
592 options)