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)

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