comparison roundup/cgi/client.py @ 6638:e1588ae185dc issue2550923_computed_property

merge from default branch. Fix travis.ci so CI builds don't error out
author John Rouillard <rouilj@ieee.org>
date Thu, 21 Apr 2022 16:54:17 -0400
parents 91ab3e0ffcd0
children 33616bc80baf
comparison
equal deleted inserted replaced
6508:85db90cc1705 6638:e1588ae185dc
29 from roundup.cgi import templating, cgitb, TranslationService 29 from roundup.cgi import templating, cgitb, TranslationService
30 from roundup.cgi import actions 30 from roundup.cgi import actions
31 from roundup.exceptions import LoginError, Reject, RejectRaw, \ 31 from roundup.exceptions import LoginError, Reject, RejectRaw, \
32 Unauthorised, UsageError 32 Unauthorised, UsageError
33 from roundup.cgi.exceptions import ( 33 from roundup.cgi.exceptions import (
34 FormError, NotFound, NotModified, Redirect, SendFile, SendStaticFile, 34 FormError, IndexerQueryError, NotFound, NotModified, Redirect,
35 DetectorError, SeriousError) 35 SendFile, SendStaticFile, DetectorError, SeriousError)
36 from roundup.cgi.form_parser import FormParser 36 from roundup.cgi.form_parser import FormParser
37 from roundup.mailer import Mailer, MessageSendError 37 from roundup.mailer import Mailer, MessageSendError
38 from roundup.cgi import accept_language 38 from roundup.cgi import accept_language
39 from roundup import xmlrpc 39 from roundup import xmlrpc
40 from roundup import rest 40 from roundup import rest
338 338
339 # Cache_Control[key] = Cache-Control header value 339 # Cache_Control[key] = Cache-Control header value
340 # Key can be explicitly file basename - value applied to just that file 340 # Key can be explicitly file basename - value applied to just that file
341 # takes precedence over mime type. 341 # takes precedence over mime type.
342 # Key can be mime type - all files of that mimetype will get the value 342 # Key can be mime type - all files of that mimetype will get the value
343 Cache_Control = {} 343 Cache_Control = {
344 'application/javascript': "public, max-age=1209600", # 2 weeks
345 'text/css': "public, max-age=4838400", # 8 weeks/2 months
346 }
344 347
345 # list of valid http compression (Content-Encoding) algorithms 348 # list of valid http compression (Content-Encoding) algorithms
346 # we have available 349 # we have available
347 compressors = [] 350 compressors = []
348 try: 351 try:
643 output = handler.dispatch(self.env['REQUEST_METHOD'], 646 output = handler.dispatch(self.env['REQUEST_METHOD'],
644 self.path, self.form) 647 self.path, self.form)
645 648
646 # type header set by rest handler 649 # type header set by rest handler
647 # self.setHeader("Content-Type", "text/xml") 650 # self.setHeader("Content-Type", "text/xml")
648 self.setHeader("Content-Length", str(len(output))) 651 if self.response_code == 204: # no body with 204
649 self.write(output) 652 self.write("")
653 else:
654 self.setHeader("Content-Length", str(len(output)))
655 self.write(output)
650 656
651 def add_ok_message(self, msg, escape=True): 657 def add_ok_message(self, msg, escape=True):
652 add_message(self._ok_message, msg, escape) 658 add_message(self._ok_message, msg, escape)
653 659
654 def add_error_message(self, msg, escape=True): 660 def add_error_message(self, msg, escape=True):
846 self.send_error_to_admin(e.subject, e.html, e.txt) 852 self.send_error_to_admin(e.subject, e.html, e.txt)
847 self.write_html(e.html) 853 self.write_html(e.html)
848 else: 854 else:
849 # in debug mode, only write error to screen. 855 # in debug mode, only write error to screen.
850 self.write_html(e.html) 856 self.write_html(e.html)
851 except: 857 except Exception as e:
852 # Something has gone badly wrong. Therefore, we should 858 # Something has gone badly wrong. Therefore, we should
853 # make sure that the response code indicates failure. 859 # make sure that the response code indicates failure.
854 if self.response_code == http_.client.OK: 860 if self.response_code == http_.client.OK:
855 self.response_code = http_.client.INTERNAL_SERVER_ERROR 861 self.response_code = http_.client.INTERNAL_SERVER_ERROR
856 # Help the administrator work out what went wrong. 862 # Help the administrator work out what went wrong.
1773 def _serve_file(self, lmt, mime_type, content=None, filename=None): 1779 def _serve_file(self, lmt, mime_type, content=None, filename=None):
1774 """ guts of serve_file() and serve_static_file() 1780 """ guts of serve_file() and serve_static_file()
1775 """ 1781 """
1776 1782
1777 # spit out headers 1783 # spit out headers
1778 self.additional_headers['Content-Type'] = mime_type
1779 self.additional_headers['Last-Modified'] = email.utils.formatdate(lmt) 1784 self.additional_headers['Last-Modified'] = email.utils.formatdate(lmt)
1780 1785
1781 ims = None 1786 ims = None
1782 # see if there's an if-modified-since... 1787 # see if there's an if-modified-since...
1783 # used if this is run behind a non-caching http proxy 1788 # used if this is run behind a non-caching http proxy
1794 # set vary header as though we were returning 200 1799 # set vary header as though we were returning 200
1795 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary 1800 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary
1796 self.setVary("Accept-Encoding") 1801 self.setVary("Accept-Encoding")
1797 raise NotModified 1802 raise NotModified
1798 1803
1804 # don't set until we are sure we are sending a response body.
1805 self.additional_headers['Content-Type'] = mime_type
1806
1799 if filename: 1807 if filename:
1800 self.write_file(filename) 1808 self.write_file(filename)
1801 else: 1809 else:
1802 self.additional_headers['Content-Length'] = str(len(content)) 1810 self.additional_headers['Content-Length'] = str(len(content))
1803 self.write(content) 1811 self.write(content)
1913 'ok_message': self._ok_message, 1921 'ok_message': self._ok_message,
1914 'error_message': self._error_message 1922 'error_message': self._error_message
1915 } 1923 }
1916 pt = self.instance.templates.load(tplname) 1924 pt = self.instance.templates.load(tplname)
1917 # let the template render figure stuff out 1925 # let the template render figure stuff out
1918 result = pt.render(self, None, None, **args) 1926 try:
1927 result = pt.render(self, None, None, **args)
1928 except IndexerQueryError as e:
1929 result = self.renderError(e.args[0])
1930
1919 self.additional_headers['Content-Type'] = pt.content_type 1931 self.additional_headers['Content-Type'] = pt.content_type
1920 if self.env.get('CGI_SHOW_TIMING', ''): 1932 if self.env.get('CGI_SHOW_TIMING', ''):
1921 if self.env['CGI_SHOW_TIMING'].upper() == 'COMMENT': 1933 if self.env['CGI_SHOW_TIMING'].upper() == 'COMMENT':
1922 timings = {'starttag': '<!-- ', 'endtag': ' -->'} 1934 timings = {'starttag': '<!-- ', 'endtag': ' -->'}
1923 else: 1935 else:
1960 if sys.version_info[0] > 2: 1972 if sys.version_info[0] > 2:
1961 raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) 1973 raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
1962 else: 1974 else:
1963 exec('raise exc_info[0], exc_info[1], exc_info[2]') # nosec 1975 exec('raise exc_info[0], exc_info[1], exc_info[2]') # nosec
1964 1976
1977 def renderError(self, error, response_code=400, use_template=True):
1978 self.response_code = response_code
1979
1980 # see if error message already logged add if not
1981 if error not in self._error_message:
1982 self.add_error_message(error, escape=True)
1983
1984 # allow use of template for a specific code
1985 trial_templates = []
1986 if use_template:
1987 if response_code == 400:
1988 trial_templates = [ "400" ]
1989 else:
1990 trial_templates = [ str(response_code), "400" ]
1991
1992 tplname = None
1993 for rcode in trial_templates:
1994 try:
1995 tplname = self.selectTemplate(self.classname, rcode)
1996 break
1997 except templating.NoTemplate:
1998 pass
1999
2000 if not tplname:
2001 # call string of serious error to get basic html
2002 # response.
2003 return str(SeriousError(error))
2004
2005 args = {
2006 'ok_message': self._ok_message,
2007 'error_message': self._error_message
2008 }
2009
2010 try:
2011 pt = self.instance.templates.load(tplname)
2012 return pt.render(self, None, None, **args)
2013 except Exception:
2014 # report original error
2015 return str(SeriousError(error))
2016
1965 # these are the actions that are available 2017 # these are the actions that are available
1966 actions = ( 2018 actions = (
1967 ('edit', actions.EditItemAction), 2019 ('edit', actions.EditItemAction),
1968 ('editcsv', actions.EditCSVAction), 2020 ('editcsv', actions.EditCSVAction),
1969 ('new', actions.NewItemAction), 2021 ('new', actions.NewItemAction),
2161 # we changed the data, change existing content-length header 2213 # we changed the data, change existing content-length header
2162 # and add Content-Encoding and Vary header. 2214 # and add Content-Encoding and Vary header.
2163 self.additional_headers['Content-Length'] = str(len(new_content)) 2215 self.additional_headers['Content-Length'] = str(len(new_content))
2164 self.additional_headers['Content-Encoding'] = encoder 2216 self.additional_headers['Content-Encoding'] = encoder
2165 self.setVary('Accept-Encoding') 2217 self.setVary('Accept-Encoding')
2218 try:
2219 current_etag = self.additional_headers['ETag']
2220 except KeyError:
2221 pass # etag not set for non-rest endpoints
2222 else:
2223 etag_end = current_etag.rindex('"')
2224 self.additional_headers['ETag'] = ( current_etag[:etag_end] +
2225 '-' + encoder + current_etag[etag_end:])
2166 2226
2167 return new_content 2227 return new_content
2168 2228
2169 def write(self, content): 2229 def write(self, content):
2170 if not self.headers_done and self.env['REQUEST_METHOD'] != 'HEAD': 2230 if not self.headers_done and self.env['REQUEST_METHOD'] != 'HEAD':
2194 if not self.headers_done: 2254 if not self.headers_done:
2195 # at this point, we are sure about Content-Type 2255 # at this point, we are sure about Content-Type
2196 if 'Content-Type' not in self.additional_headers: 2256 if 'Content-Type' not in self.additional_headers:
2197 self.additional_headers['Content-Type'] = \ 2257 self.additional_headers['Content-Type'] = \
2198 'text/html; charset=%s' % self.charset 2258 'text/html; charset=%s' % self.charset
2259 if 'Content-Length' not in self.additional_headers:
2260 self.additional_headers['Content-Length'] = str(len(content))
2199 self.header() 2261 self.header()
2200 2262
2201 if self.env['REQUEST_METHOD'] == 'HEAD': 2263 if self.env['REQUEST_METHOD'] == 'HEAD':
2202 # client doesn't care about content 2264 # client doesn't care about content
2203 return 2265 return
2477 finally: 2539 finally:
2478 f.close() 2540 f.close()
2479 self.write(content) 2541 self.write(content)
2480 2542
2481 def setHeader(self, header, value): 2543 def setHeader(self, header, value):
2482 """Override a header to be returned to the user's browser. 2544 """Override or delete a header to be returned to the user's browser.
2483 """ 2545 """
2484 self.additional_headers[header] = value 2546 if value is None:
2547 try:
2548 del(self.additional_headers[header])
2549 except KeyError:
2550 pass
2551 else:
2552 self.additional_headers[header] = value
2485 2553
2486 def header(self, headers=None, response=None): 2554 def header(self, headers=None, response=None):
2487 """Put up the appropriate header. 2555 """Put up the appropriate header.
2488 """ 2556 """
2489 if headers is None: 2557 if headers is None:
2494 # update with additional info 2562 # update with additional info
2495 headers.update(self.additional_headers) 2563 headers.update(self.additional_headers)
2496 2564
2497 if headers.get('Content-Type', 'text/html') == 'text/html': 2565 if headers.get('Content-Type', 'text/html') == 'text/html':
2498 headers['Content-Type'] = 'text/html; charset=utf-8' 2566 headers['Content-Type'] = 'text/html; charset=utf-8'
2567
2568 if response in [ 204, 304]: # has no body so no content-type
2569 del(headers['Content-Type'])
2499 2570
2500 headers = list(headers.items()) 2571 headers = list(headers.items())
2501 2572
2502 for ((path, name), (value, expire)) in self._cookies.items(): 2573 for ((path, name), (value, expire)) in self._cookies.items():
2503 cookie = "%s=%s; Path=%s;"%(name, value, path) 2574 cookie = "%s=%s; Path=%s;"%(name, value, path)

Roundup Issue Tracker: http://roundup-tracker.org/