This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
mandaye/mandaye/server.py

245 lines
10 KiB
Python

import Cookie
import json
import urllib2
import random
import re
import os
import time
from md5 import md5
from urlparse import urlparse
from mandaye import config
from mandaye.backends.default import storage_conn
from mandaye.dispatcher import Dispatcher
from mandaye.exceptions import ImproperlyConfigured
from mandaye.log import logger, UuidFilter
from mandaye.handlers.default import MandayeRedirectHandler, MandayeErrorHandler
from mandaye.http import HTTPHeader, HTTPRequest, HTTPResponse
from mandaye.response import _404, _502, _500
def get_response(env, request, url, cookiejar=None):
""" request: Mandaye Request
url: the target url
"""
# Cleaning url
if config.debug:
opener = urllib2.build_opener(MandayeErrorHandler, MandayeRedirectHandler,
urllib2.HTTPSHandler(debuglevel=1), urllib2.HTTPHandler(debuglevel=1))
else:
opener = urllib2.build_opener(MandayeErrorHandler, MandayeRedirectHandler)
urllib2.install_opener(opener)
url = re.sub('(?<!:)/+', '/', url)
if not '://' in url:
url = env['target'].geturl() + url
if request.req_method == 'POST':
req = urllib2.Request(url, request.msg, request.headers.getheaders())
logger.info('Mandaye POST %s' % url)
logger.debug('POST content %s' % request.msg)
else:
req = urllib2.Request(url, headers=request.headers.getheaders())
logger.info('Mandaye GET %s' % url)
# Load the cookies
if request.cookies:
req.add_header('cookie', request.cookies.output(attrs=[], sep=';', header=''))
try:
if cookiejar != None:
opener.add_handler(urllib2.HTTPCookieProcessor(cookiejar))
resp = opener.open(req)
except Exception, e:
logger.info("%s failed with error : %s" % (url, str(e)))
response = _502(env['PATH_INFO'], req.get_host(), e)
else:
response = HTTPResponse()
response.load_from_urllib(resp)
if cookiejar:
response.cookies = Cookie.BaseCookie()
for cookie in cookiejar:
if response.cookies.get(cookie.name) == cookie.value:
continue
if response.cookies.has_key(cookie.name):
del response.cookies[cookie.name]
response.cookies[cookie.name] = cookie.value
if cookie.expires:
response.cookies[cookie.name]['expires'] = Cookie._getdate(cookie.expires-time.time())
if cookie.domain.startswith('.'):
response.cookies[cookie.name]['domain'] = cookie.domain
if cookie.path:
response.cookies[cookie.name]['path'] = cookie.path
# TODO: add the other RFC 2109 cookie values
resp.close()
return response
def strize(v):
'''Flatten unicode strings to str encoded as UTF-8'''
if isinstance(v, unicode):
return v.encode('utf-8')
if isinstance(v, (list, tuple)):
return type(v)([strize(e) for e in v])
if isinstance(v, dict):
return dict((strize(k), strize(e)) for k, e in v.iteritems())
return v
class MandayeApp(object):
def __init__(self):
self.env = None
self.dispatcher = None
self.raven_client = None
if config.raven_dsn:
from raven import Client
self.raven_client = Client(config.raven_dsn)
def __call__(self, env, start_response):
""" called by the WSGI server
env: standard WSGI env
start_response: stanard WSGI / CGI function
"""
response = []
try:
self.env = env
if env.has_key('HTTP_X_FORWARDED_SCHEME'):
self.env['mandaye.scheme'] = env['HTTP_X_FORWARDED_SCHEME']
else:
self.env['mandaye.scheme'] = env['wsgi.url_scheme']
self.env['mandaye.uuid'] = self._get_uuid()
self.dispatcher = None
local_host = env['HTTP_HOST']
path_info = env['PATH_INFO']
UuidFilter.uuid = self.env['mandaye.uuid']
logger.info("Client %s - %s %s://%s%s" %\
(self.env['REMOTE_ADDR'], self.env['REQUEST_METHOD'],
self.env['wsgi.url_scheme'], self.env['HTTP_HOST'],
self.env['PATH_INFO']))
for conf in os.listdir(config.config_root):
conf_file = os.path.join(config.config_root, conf)
if os.path.isfile(conf_file):
with open(conf_file, 'r') as f:
conf = json.loads(f.read())
for param in ['site_name', 'location', 'target', 'server_name', 'mapper', 'auth_type']:
if not conf.has_key(param):
error = 'you must set %s option in vhost : %s' % \
(param, conf_file)
logger.error(error)
raise ImproperlyConfigured, error
for param in ['location', 'target', 'server_name', 'mapper', 'auth_type']:
if param in conf:
conf[param] = strize(conf[param])
if not config.mappers.has_key(conf['mapper']):
err = '%s: mapper %s not found' % \
(conf_file, conf['mapper'])
logger.error(err)
raise ImproperlyConfigured, err
if not config.authentifications.has_key(conf['auth_type']):
err = '%s: authentification %s not found' % \
(conf_file, conf['auth_type'])
logger.error(err)
raise ImproperlyConfigured, err
if local_host in conf['server_name'] and \
re.match(re.compile(conf['location']), path_info):
if conf.get('force_ssl'):
self.env['mandaye.scheme'] = 'https'
self.env['mandaye.config'] = conf
self.env['mandaye.vhost'] = conf_file
self.dispatcher = Dispatcher(self.env, conf['target'],
conf['mapper'])
response = self.on_request(start_response)
if not response:
response = self.on_response(start_response, _404(env['PATH_INFO']))
if config.storage_backend == 'mandaye.backends.sql':
storage_conn.commit()
except Exception, e:
if config.storage_backend == 'mandaye.backends.sql':
storage_conn.rollback()
if self.raven_client:
self.raven_client.captureException()
response = self.on_response(start_response, _500(env['PATH_INFO'], "Unhandled exception",
exception=e, env=env))
finally:
if config.storage_backend == 'mandaye.backends.sql':
storage_conn.close()
return response
def _get_uuid(self):
id_str = "%f%s%f%s" % (
time.time(),
id({}),
random.random(),
os.getpid
)
return md5(md5(id_str).hexdigest()).hexdigest()
def _get_request(self):
""" Return a Mandaye HTTP Request
"""
headers = HTTPHeader()
if self.env.get('CONTENT_LENGTH'):
headers.addheader('Content-Length', self.env['CONTENT_LENGTH'])
if self.env.get('CONTENT_TYPE'):
headers.addheader('Content-Type', self.env['CONTENT_TYPE'])
for name in (name for name in self.env if name.startswith('HTTP_')):
value = self.env[name]
if name != "HTTP_HOST" and name != "HTTP_COOKIE":
name = name.split('HTTP_')[1].replace('_', '-')
headers.addheader(name, value)
cookies = Cookie.BaseCookie()
env_cookies = self.env.get('HTTP_COOKIE', '').split(';')
ok_cookies = []
for cookie in env_cookies:
try:
cookies.load(cookie)
ok_cookies.append(cookie)
except Cookie.CookieError:
logger.warning("Can't parse cookie %r", cookie)
self.env['HTTP_COOKIE'] = ';'.join(ok_cookies)
if self.env['REQUEST_METHOD'] == 'POST':
msg = self.env['wsgi.input']
else:
msg = None
request = HTTPRequest(cookies, headers, self.env['REQUEST_METHOD'], msg, None)
return self.dispatcher.set_request_target(request)
def on_request(self, start_response):
request = self._get_request()
# Calling the dispatcher hook for the request
response = self.dispatcher.mod_request(request)
if response:
return response
if not request.target:
response = self.dispatcher.get_response(request)
elif not "://" in request.target:
url = urlparse(request.target)
self.env['PATH_INFO'] = url.path
self.env['RAW_URI'] = url.path
self.env['QUERY_STRING'] = url.query
if self.env['QUERY_STRING']:
self.env['RAW_URI'] += url.query
self.env['REQUEST_METHOD'] = request.req_method
self.env['wsgi.input'] = request.msg
self.dispatcher = Dispatcher(self.env, self.env['target'].geturl(),
self.dispatcher.mapper_name)
response = self.dispatcher.get_response(request)
else:
response = get_response(self.env, request, request.target)
if response.code != 304:
response = self.dispatcher.mod_response(request, response)
return self.on_response(start_response, response)
def on_response(self, start_response, response):
""" start_response: wsgi start_response
response: an instance of HTTPResponse """
response.headers.addsetcookies(response.cookies)
start_response('%d %s' % (response.code, response.reason),
response.headers.items())
return [response.msg]