| |
@@ -19,170 +19,8 @@
|
| |
# Authors:
|
| |
# Mike McLean <mikem@redhat.com>
|
| |
|
| |
- import sys
|
| |
- import traceback
|
| |
- from koji.util import LazyDict
|
| |
-
|
| |
- try:
|
| |
- from mod_python import apache
|
| |
- except ImportError: # pragma: no cover
|
| |
- apache = None
|
| |
-
|
| |
-
|
| |
class ServerError(Exception):
|
| |
"""Base class for our server-side-only exceptions"""
|
| |
|
| |
class ServerRedirect(ServerError):
|
| |
"""Used to handle redirects"""
|
| |
-
|
| |
-
|
| |
- class WSGIWrapper(object):
|
| |
- """A very thin wsgi compat layer for mod_python
|
| |
-
|
| |
- This class is highly specific to koji and is not fit for general use.
|
| |
- It does not support the full wsgi spec
|
| |
- """
|
| |
-
|
| |
- def __init__(self, req):
|
| |
- self.req = req
|
| |
- self._env = None
|
| |
- host, port = req.connection.remote_addr
|
| |
- environ = {
|
| |
- 'REMOTE_ADDR': req.connection.remote_ip,
|
| |
- # or remote_addr[0]?
|
| |
- # or req.get_remote_host(apache.REMOTE_NOLOOKUP)?
|
| |
- 'REMOTE_PORT': str(req.connection.remote_addr[1]),
|
| |
- 'REMOTE_USER': req.user,
|
| |
- 'REQUEST_METHOD': req.method,
|
| |
- 'REQUEST_URI': req.uri,
|
| |
- 'PATH_INFO': req.path_info,
|
| |
- 'SCRIPT_FILENAME': req.filename,
|
| |
- 'QUERY_STRING': req.args or '',
|
| |
- 'SERVER_NAME': req.hostname,
|
| |
- 'SERVER_PORT': str(req.connection.local_addr[1]),
|
| |
- 'wsgi.version': (1, 0),
|
| |
- 'wsgi.input': InputWrapper(req),
|
| |
- 'wsgi.errors': sys.stderr,
|
| |
- #TODO - file_wrapper support
|
| |
- }
|
| |
- environ = LazyDict(environ)
|
| |
- environ.lazyset('wsgi.url_scheme', self.get_scheme, [])
|
| |
- environ.lazyset('modpy.env', self.env, [])
|
| |
- environ.lazyset('modpy.opts', req.get_options, [])
|
| |
- environ.lazyset('modpy.conf', req.get_config, [])
|
| |
- environ.lazyset('SCRIPT_NAME', self.script_name, [], cache=True)
|
| |
- env_keys = ['SSL_CLIENT_VERIFY', 'HTTPS', 'SSL_CLIENT_S_DN']
|
| |
- for key in env_keys:
|
| |
- environ.lazyset(key, self.envget, [key])
|
| |
- # The component of the DN used for the username is usually the CN,
|
| |
- # but it is configurable.
|
| |
- # Allow retrieval of some common DN components from the environment.
|
| |
- for comp in ['C', 'ST', 'L', 'O', 'OU', 'CN', 'Email']:
|
| |
- key = 'SSL_CLIENT_S_DN_' + comp
|
| |
- environ.lazyset(key, self.envget, [key])
|
| |
- #gather the headers we care about
|
| |
- for key in req.headers_in:
|
| |
- k2 = key.upper()
|
| |
- k2 = k2.replace('-', '_')
|
| |
- if k2 not in ['CONTENT_TYPE', 'CONTENT_LENGTH']:
|
| |
- k2 = 'HTTP_' + k2
|
| |
- environ[k2] = req.headers_in[key]
|
| |
- self.environ = environ
|
| |
- self.set_headers = False
|
| |
-
|
| |
- def env(self):
|
| |
- if self._env is None:
|
| |
- self.req.add_common_vars()
|
| |
- self._env = self.req.subprocess_env
|
| |
- return self._env
|
| |
-
|
| |
- def envget(self, *args):
|
| |
- return self.env().get(*args)
|
| |
-
|
| |
- def script_name(self):
|
| |
- uri = self.req.uri
|
| |
- path_info = self.req.path_info
|
| |
- if uri.endswith(path_info):
|
| |
- uri = uri[:-len(path_info)]
|
| |
- uri = uri.rstrip('/')
|
| |
- return uri
|
| |
-
|
| |
- def get_scheme(self):
|
| |
- if self.envget('HTTPS') in ('yes', 'on', '1'):
|
| |
- return 'https'
|
| |
- else:
|
| |
- return 'http'
|
| |
-
|
| |
- def no_write(self, string):
|
| |
- """a fake write() callable returned by start_response
|
| |
-
|
| |
- we don't use the write() callable in koji, so it will raise an error if called
|
| |
- """
|
| |
- raise RuntimeError("wsgi write() callable not supported")
|
| |
-
|
| |
- def start_response(self, status, headers, exc_info=None):
|
| |
- #XXX we don't deal with exc_info
|
| |
- if self.set_headers:
|
| |
- raise RuntimeError("start_response() already called")
|
| |
- self.req.status = int(status[:3])
|
| |
- for key, val in headers:
|
| |
- if key.lower() == 'content-length':
|
| |
- self.req.set_content_length(int(val))
|
| |
- elif key.lower() == 'content-type':
|
| |
- self.req.content_type = val
|
| |
- else:
|
| |
- self.req.headers_out.add(key, val)
|
| |
- self.set_headers = True
|
| |
- return self.no_write
|
| |
-
|
| |
- def run(self, handler):
|
| |
- try:
|
| |
- result = handler(self.environ, self.start_response)
|
| |
- self.write_result(result)
|
| |
- return apache.OK
|
| |
- except:
|
| |
- sys.stderr.write(''.join(traceback.format_exception(*sys.exc_info())))
|
| |
- sys.stderr.flush()
|
| |
- raise apache.SERVER_RETURN(apache.HTTP_INTERNAL_SERVER_ERROR)
|
| |
-
|
| |
- def write_result(self, result):
|
| |
- """called by run() to handle the application's result value"""
|
| |
- req = self.req
|
| |
- write = req.write
|
| |
- if self.set_headers:
|
| |
- for chunk in result:
|
| |
- write(chunk)
|
| |
- else:
|
| |
- #slower version -- need to check for set_headers
|
| |
- for chunk in result:
|
| |
- if chunk and not self.set_headers:
|
| |
- raise RuntimeError("write() called before start_response()")
|
| |
- write(chunk)
|
| |
- if not req.bytes_sent:
|
| |
- #application sent nothing back
|
| |
- req.set_content_length(0)
|
| |
-
|
| |
-
|
| |
-
|
| |
- class InputWrapper(object):
|
| |
-
|
| |
- def __init__(self, req):
|
| |
- self.req = req
|
| |
-
|
| |
- def close(self):
|
| |
- pass
|
| |
-
|
| |
- def read(self, size=-1):
|
| |
- return self.req.read(size)
|
| |
-
|
| |
- def readline(self):
|
| |
- return self.req.readline()
|
| |
-
|
| |
- def readlines(self, hint=-1):
|
| |
- return self.req.readlines(hint)
|
| |
-
|
| |
- def __iter__(self):
|
| |
- line = self.readline()
|
| |
- while line:
|
| |
- yield line
|
| |
- line = self.readline()
|
| |
Fixes: https://pagure.io/koji/issue/466