Mercurial > p > roundup > code
comparison roundup/cgi/wsgi_handler.py @ 6084:3cba8949bfe0
reworked WSGI interface to not use the RequestDispatcher class for two
different purposes (but have a separate RequestHandler)
| author | Christof Meerwald <cmeerw@cmeerw.org> |
|---|---|
| date | Sat, 08 Feb 2020 09:56:04 +0000 |
| parents | f74d078cfd9a |
| children | 979cecdb70f8 |
comparison
equal
deleted
inserted
replaced
| 6083:f74d078cfd9a | 6084:3cba8949bfe0 |
|---|---|
| 52 f = self.request.get_wfile() | 52 f = self.request.get_wfile() |
| 53 self.write = f | 53 self.write = f |
| 54 return self.write(data) | 54 return self.write(data) |
| 55 | 55 |
| 56 | 56 |
| 57 class RequestHandler(object): | |
| 58 def __init__(self, environ, start_response): | |
| 59 self.__start_response = start_response | |
| 60 self.__wfile = None | |
| 61 self.headers = Headers(environ) | |
| 62 self.rfile, self.wfile = None, Writer(self) | |
| 63 | |
| 64 def start_response(self, headers, response_code): | |
| 65 """Set HTTP response code""" | |
| 66 message, explain = BaseHTTPRequestHandler.responses[response_code] | |
| 67 self.__wfile = self.__start_response('%d %s' % (response_code, | |
| 68 message), headers) | |
| 69 | |
| 70 def get_wfile(self): | |
| 71 if self.__wfile is None: | |
| 72 raise ValueError('start_response() not called') | |
| 73 return self.__wfile | |
| 74 | |
| 75 | |
| 57 class RequestDispatcher(object): | 76 class RequestDispatcher(object): |
| 58 def __init__(self, home, debug=False, timing=False, lang=None): | 77 def __init__(self, home, debug=False, timing=False, lang=None): |
| 59 assert os.path.isdir(home), '%r is not a directory' % (home,) | 78 assert os.path.isdir(home), '%r is not a directory' % (home,) |
| 60 self.home = home | 79 self.home = home |
| 61 self.debug = debug | 80 self.debug = debug |
| 66 else: | 85 else: |
| 67 self.translator = None | 86 self.translator = None |
| 68 | 87 |
| 69 def __call__(self, environ, start_response): | 88 def __call__(self, environ, start_response): |
| 70 """Initialize with `apache.Request` object""" | 89 """Initialize with `apache.Request` object""" |
| 71 self.environ = environ | 90 request = RequestHandler(environ, start_response) |
| 72 request = RequestDispatcher(self.home, self.debug, self.timing) | |
| 73 request.__start_response = start_response | |
| 74 | |
| 75 request.wfile = Writer(request) | |
| 76 request.__wfile = None | |
| 77 request.headers = Headers(environ) | |
| 78 | 91 |
| 79 if environ['REQUEST_METHOD'] == 'OPTIONS': | 92 if environ['REQUEST_METHOD'] == 'OPTIONS': |
| 80 if environ["PATH_INFO"][:5] == "/rest": | 93 if environ["PATH_INFO"][:5] == "/rest": |
| 81 # rest does support options | 94 # rest does support options |
| 82 # This I hope will result in self.form=None | 95 # This I hope will result in self.form=None |
| 87 request.start_response([('Content-Type', 'text/html'), | 100 request.start_response([('Content-Type', 'text/html'), |
| 88 ('Connection', 'close')], code) | 101 ('Connection', 'close')], code) |
| 89 request.wfile.write(s2b(DEFAULT_ERROR_MESSAGE % locals())) | 102 request.wfile.write(s2b(DEFAULT_ERROR_MESSAGE % locals())) |
| 90 return [] | 103 return [] |
| 91 | 104 |
| 92 tracker = roundup.instance.open(self.home, not self.debug) | 105 # need to strip the leading '/' |
| 106 environ["PATH_INFO"] = environ["PATH_INFO"][1:] | |
| 107 if self.timing: | |
| 108 environ["CGI_SHOW_TIMING"] = self.timing | |
| 109 | |
| 110 if environ['REQUEST_METHOD'] in ("OPTIONS", "DELETE"): | |
| 111 # these methods have no data. When we init tracker.Client | |
| 112 # set form to None to get a properly initialized empty | |
| 113 # form. | |
| 114 form = None | |
| 115 else: | |
| 116 form = BinaryFieldStorage(fp=environ['wsgi.input'], environ=environ) | |
| 93 | 117 |
| 94 with self.get_tracker() as tracker: | 118 with self.get_tracker() as tracker: |
| 95 # need to strip the leading '/' | |
| 96 environ["PATH_INFO"] = environ["PATH_INFO"][1:] | |
| 97 if request.timing: | |
| 98 environ["CGI_SHOW_TIMING"] = request.timing | |
| 99 | |
| 100 form = BinaryFieldStorage(fp=environ['wsgi.input'], environ=environ) | |
| 101 | |
| 102 if environ['REQUEST_METHOD'] in ("OPTIONS", "DELETE"): | |
| 103 # these methods have no data. When we init tracker.Client | |
| 104 # set form to None and request.rfile to None to get a | |
| 105 # properly initialized empty form. | |
| 106 form = None | |
| 107 request.rfile = None | |
| 108 | |
| 109 client = tracker.Client(tracker, request, environ, form, | 119 client = tracker.Client(tracker, request, environ, form, |
| 110 request.translator) | 120 self.translator) |
| 111 try: | 121 try: |
| 112 client.main() | 122 client.main() |
| 113 except roundup.cgi.client.NotFound: | 123 except roundup.cgi.client.NotFound: |
| 114 request.start_response([('Content-Type', 'text/html')], 404) | 124 request.start_response([('Content-Type', 'text/html')], 404) |
| 115 request.wfile.write(s2b('Not found: %s' % | 125 request.wfile.write(s2b('Not found: %s' % |
| 116 html_escape(client.path))) | 126 html_escape(client.path))) |
| 117 | 127 |
| 118 # all body data has been written using wfile | 128 # all body data has been written using wfile |
| 119 return [] | 129 return [] |
| 120 | 130 |
| 121 def start_response(self, headers, response_code): | |
| 122 """Set HTTP response code""" | |
| 123 message, explain = BaseHTTPRequestHandler.responses[response_code] | |
| 124 self.__wfile = self.__start_response('%d %s' % (response_code, | |
| 125 message), headers) | |
| 126 | |
| 127 def get_wfile(self): | |
| 128 if self.__wfile is None: | |
| 129 raise ValueError('start_response() not called') | |
| 130 return self.__wfile | |
| 131 | |
| 132 @contextmanager | 131 @contextmanager |
| 133 def get_tracker(self): | 132 def get_tracker(self): |
| 134 # get a new instance for each request | 133 # get a new instance for each request |
| 135 yield roundup.instance.open(self.home, not self.debug) | 134 yield roundup.instance.open(self.home, not self.debug) |
