Viewing file: Basic.py (41.32 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Http/Basic.py,v 1.108 2005/04/06 06:19:15 mbrown Exp $ """ 4Suite API module for the HTTP Handler
Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """
__all__ = ['BasicHttpHandler', 'XmlBody']
import re, urlparse, os, time, cgi, sha, cgi try: # Python 2.3+ from types import basestring as StringTypes except ImportError: from types import StringTypes
import BaseRequestHandler import HeaderDict import Status import Session
from Ft.Lib import Time, Uri from Ft.Server import RESERVED_NAMESPACE from Ft.Server.Common import Schema, ResourceTypes from Ft.Server.Common.ResourceTypes import * from Ft.Server.Server import FtServerServerException from Ft.Server.Server import Error as ServerError from Ft.Server.Server import SCore from Ft.Server.Server.Drivers.PathImp import RepoPathToUri from Ft.Server.Server.Http import FtServerHttpException, Error from Ft.Server.Server.SCore import GetRepository from Ft.Xml.Lib.XmlString import IsQName from Ft.Server.Server.Drivers import FtssInputSource
REPL_PAT = re.compile(r"(?<!\\)\\([0-9])")
class BasicHttpHandler(BaseRequestHandler.BaseRequestHandler):
def __init__(self, connection): BaseRequestHandler.BaseRequestHandler.__init__(self, connection) self.isSoap = self.isXmlBody = False config = vars(self.server) self.http_post_encoding_var = config.get('http_post_encoding_var', 'http-post-encoding') #prime config vars return
# Now handled by BaseRequestHandler #def do_HEAD(self): # body = self._process_request(self.args) # if body is not None: # self.headers_out['Content-Length'] = len(body) # return
def do_GET(self): body = self._process_request(self.args) # wfile is a buffering stream body and self.wfile.write(body) return
def do_POST(self): content_type = self.headers_in['Content-Type'][0] #SOAP 1.2, strictly self.isSoap = content_type == 'application/soap' self.isXmlBody = re.match(r'(?:text|application)/.*\+?xml', content_type) is not None
body = self._process_request(self.args) # wfile is a buffering stream body and self.wfile.write(body) return
def _process_request(self, args): #First, load all of our config options #This will set up the appropriate instance variables #FIXME: this seems wasteful. Isn't there a way to reload only if something has changed? self.load_config(args) #Now get a connection to the repo #There are four options here #1. An existing session #2. A new session #3. A 403 auth #4. An anon connection
if self.getRepo(): #Will have sent the error/response return False
self.commit = True new_response_headers = HeaderDict.HeaderDict() status = 0 try: try: #First of all, if there are no query args, try to match a #processing rule rule_applied = False if not self.args: rule_applied, data = self.apply_rules() if rule_applied: (res, chain, new_args) = data args.update(new_args) else: #If they specify an 'xslt' arg, use it, else, if there is a #default for this directory or resource type use that. if self.isXmlBody or self.isSoap: res = XmlBody(self.body, self.repo) else: res = self.load_resource(not len(args)) if res is None: #The response was handled return chain = self.load_stylesheets(res)
#If there are stylesheets, then go into the render/action loop #If not just fetch the resource and exit if len(chain): # prepare stylesheet params # (usable by the stylesheet; not modifiable outside of XSLT processing) # params = {} if self.isSoap: params[(RESERVED_NAMESPACE, 'soap-action')] = self.headers_in.get('SOAPAction', [''])[0]
#FIXME: This is a hack. We really need a parameter that #properly represents the resource ID of the source doc if not(res.__class__ == XmlBody): params[(RESERVED_NAMESPACE, 'resource-id')] = RepoPathToUri(res.getAbsolutePath()) params[(RESERVED_NAMESPACE, 'uri-path')] = self.path params[(RESERVED_NAMESPACE, 'http-host')] = self.hostname params[(RESERVED_NAMESPACE, 'server-name')] = self.hostname #FIXME: We need a more reliable way of getting the port than the incoming header host = self.headers_in.get("Host", [""])[0] host_list = host.split(":") port = len(host_list) > 1 and host_list[1] or "" params[(RESERVED_NAMESPACE, 'server-port')] = port params[(RESERVED_NAMESPACE, 'absolute-request-uri')] = "http://" + host + self.unparsed_uri #Deprecate request-uri, and remove it pre-1.0 params[(RESERVED_NAMESPACE, 'request-uri')] = params[(RESERVED_NAMESPACE, 'absolute-request-uri')] params[(RESERVED_NAMESPACE, 'request')] = self.unparsed_uri params[(RESERVED_NAMESPACE, 'user-name')] = self.user or BaseRequestHandler.ANONYMOUS_USER_NAME params[(RESERVED_NAMESPACE, 'document-root')] = self.documentRoot or u'' if not(res.__class__ == XmlBody): params[(RESERVED_NAMESPACE, 'source-uri')] = res.getAbsolutePath()[len(self.documentRoot or u''):] #The stylesheet URI of the highest precedence stylesheet #in that is first in the chain main_xform = type(chain[0]) in [type([]), type(())] and chain[0][0] or chain[0] params[(RESERVED_NAMESPACE, 'xslt-uri')] = main_xform.getAbsolutePath()[len(self.documentRoot or u''):] #Holdover name from legacy params[(RESERVED_NAMESPACE, 'stylesheet-uri')] = params[(RESERVED_NAMESPACE, 'xslt-uri')] if self.session: params[(RESERVED_NAMESPACE, 'sessionId')] = self.session.id else: params[(RESERVED_NAMESPACE, 'sessionId')] = '' # query args are stylesheet params too for name, value in args.items(): # colons and characters not allowed in # XSLT parameter names become hyphens. if name.find(u':') != -1: name = name.replace(u':', u'-') if not IsQName(name): newname = u'' for c in name: if not IsQName(c): c = u'-' newname += c name = newname del newname # Treat given parameters the same as 4XSLT does # (if multiple values for same param, only use # the first value) if len(value) > 1: params[name] = value else: params[name] = value[0]
# prepare extension params for extension elements and functions to use # (they belong to the processor and can be modified) # ext_params = { (RESERVED_NAMESPACE, 'commit'): 0, (RESERVED_NAMESPACE, 'handler'): self, (RESERVED_NAMESPACE, 'error-log'): self.server.errorLog, (RESERVED_NAMESPACE, 'access-log'): self.server.accessLog, }
#pass query arguments as a dictionary to extension parameters ext_params[(RESERVED_NAMESPACE,'queryArgs')] = args self.server.errorLog.debug("Begin Rendering") #chain is a list of lists. The outer list is the chain #of stylesheets. Each inner list represents all #the transforms that make up each link in the chain body, imt = res.applyXslt(chain, params, ignorePis=(not self.enable_pis), extParams=ext_params) self.server.errorLog.debug("End Rendering")
if ext_params.get((RESERVED_NAMESPACE, 'rollback')): self.server.errorLog.info("Ext Param Config Argument: rollback --> 1\n") self.commit = False
redirect = ext_params.get((RESERVED_NAMESPACE, 'response-uri'))
login = ext_params.get((RESERVED_NAMESPACE, 'login')) if login: (user, password, ttl, success_redirect, failure_redirect, timeout_redirect ) = login self.server.errorLog.debug("Session-based login requested via proc. ext. params for user %s" % user) #Use *current* repo to check credentials because they #may have added a user or changed a password in #this tx if self.repo.checkLogin(user, password): failed = False else: failed = True self.server.errorLog.error("Repository access denied for user %s; will redirect" % user) redirect = failure_redirect ## #Now create a new repo for setting up the session ## #because we want to use the new username, not the ## #old one ## try: ## temp_repo = GetRepository(user, password, ## self.server.errorLog, ## self.server.properties) ## failed = False ## except FtServerServerException, e: ## failed = True ## if e.errorCode == ServerError.INVALID_LOGIN: ## redirect = failure_redirect ## else: ## raise if not failed: if self.repo.hasSession(): self.repo.invalidateSession() sid = self.repo.createSession('', ttl or self.session_ttl, overriddenLogin=(user, password)) session = Session.Create(sid, self.session_method, self.server.errorLog) new_response_headers = HeaderDict.HeaderDict() if session: self.server.errorLog.info("Login successful; new session created; will redirect") session.userAgentVariables['timeout-redirect'] = timeout_redirect session.updateHeaders( self.repo, new_response_headers, self.server.errorLog ) self.sendRedirect(success_redirect, new_response_headers) #self.repo.txCommit() return '' else: self.server.errorLog.error("Login successful, but error creating new session")
if redirect: self.server.errorLog.info("Redirecting to: %s\n" % redirect) new_response_headers = HeaderDict.HeaderDict() if self.session: self.session.updateHeaders( self.repo, new_response_headers, self.server.errorLog) self.sendRedirect(redirect, new_response_headers) return ''
#Fall through to std fallback #charset = 'iso-8859-1' charset = None status = Status.HTTP_OK if self.session: self.session.updateHeaders(self.repo, self.headers_out, self.server.errorLog)
else: # Always add a Last-Modified HTTP header to the response # to help browsers cache entities. # If the browser requested an entity only #"If-Modified-Since" sometime after or equal to # the entity's last modified date in 4SS, then respond # with a 304 ("Not Modified").
#FIXME: rfc2068 sction 14.26 seems to indicate that #we must handle "If-None-Match: *", even though that #header would only really make sense for PUTs
last_modified = res.getLastModifiedDate() #print "last mod:", repr(last_modified)
# Netscape 4 sends If-Modified-Since headers that have the # full weekday name and "; extra crap" on the end check_last_mod = self.headers_in.terse().get('If-Modified-Since') if check_last_mod: lmd = Time.FromISO8601(last_modified) clm = Time.FromRFC822(check_last_mod.split(';')[0])
#print "lmd: ", lmd #print "clm: ", clm #print "cmp: ", lmd <= clm
if lmd <= clm: #Do not add cookie headers for requests that #don't involve them, since these do not concern #app logic, and the 'expires' Set-Cookie field #prevents browsers from caching. #if self.session: # self.session.updateHeaders(self.repo, new_response_headers, self.server.errorLog) self.headers_out.update(new_response_headers) self.status = Status.HTTP_NOT_MODIFIED return '' #Set the Last-Modified Header, e.g. # Mon, 17 Dec 2001 19:29:08 GMT # and ensure it doesn't exceed the Date header # as per RFC 2616 sec. 14.19. dt = Time.FromISO8601(last_modified) if dt.asPythonTime() > time.mktime(time.gmtime(self.request_time)): dt = Time.FromPythonTime(self.request_time) new_response_headers['Last-Modified'] = dt.asRFC822DateTime() #print "Setting LMT: %s" % headers['Last-Modified'] body = res.getContent() imt = res.getImt()
#charset = 'iso-8859-1' charset = None status = Status.HTTP_OK
except FtServerServerException, e: #import sys, traceback; traceback.print_exc(1000, sys.stdout) self.commit = False if e.errorCode in [ServerError.UNKNOWN_PATH, ServerError.INVALID_PATH]: self.server.errorLog.debug("Access to unknown path %s" % e.params['path']) self.send_error(Status.HTTP_NOT_FOUND, uri=self.path) return elif e.errorCode == ServerError.PERMISSION_DENIED: #import traceback, cStringIO #st = cStringIO.StringIO() #traceback.print_exc(st) #self.server.errorLog.debug(st.getvalue()) if not self.authChecker(mandatory=True): self.server.errorLog.warning('Attempted Unauthorized "%(level)s" Access to: %(path)s'%e.params)
if self.session_method and self.session_permission_denied_uri: self.sendRedirect(self.session_permission_denied_uri, HeaderDict.HeaderDict()) else: # send_error will determine if the response should be HTTP_FORBIDDEN or HTTP_UNAUTHORIZED self.send_error(Status.HTTP_FORBIDDEN, uri=self.path) return raise except: self.commit = False raise finally: if self.repo is not None: if self.commit: self.repo.txCommit() else: self.repo.txRollback() # if we reached this point, there were no errors getting the resource # or transform result from the repo, and status is HTTP_OK, so send # the response line and headers, and return body to do_GET or do_POST if charset: content_type = '%s; charset=%s' % (imt, charset) else: content_type = imt new_response_headers['Content-Type'] = content_type
#for name,value in headers.items(): # self.headers_out[name] = value self.headers_out.update(new_response_headers) self.status = status return body
def load_config(self, args): #This is probably OK to do config = vars(self.server)
#This is what config might look like: #config= { # 'accessLog': None # 'admin': u'root@localhost', # 'backlog': 512, # 'defaultXslt': {1: '/ftss/data/container.xslt'}, # 'documentRoot': u'/ftss/demos', # 'Driver': {'TYPE': 'FlatFile', 'Root': u'xmlserver'}, # 'LogFile': u'\\_Dev\\4ss.log', # 'LogLevel': ('debug', 7), # 'TemporaryReapInterval': -1, # 'DynamicReloadInterval': -1, # 'ConfigFile': 'C:\\_dev\\4ss.conf', # 'CoreId': u'Core' # }, # 'errorFilename': None, # 'errorLog': <Ft.Server.Server.Lib.LogUtil.Logger instance at 012A11B4>, # 'handler': <class Ft.Server.Server.Http.Basic.RequestHandler at 00D39884>, # 'hostname': u'localhost', # 'logLevel': ('debug', 7), # 'names': [], # 'port': 8081, # 'properties': {'PidFile': u'\\_Dev\\4ss.pid', # 'redirects': {u'/ftss/demos': u'index.html'}, # 'session_anonymous_flag': u'anonymous-login', # 'session_invalid_uri': u'/buyerbase/?xslt=home.xslt&expired=1', # 'session_method': u'Cookie', # 'session_password': u'password', # 'session_post_invalid_login_uri_qs': u'invalid-login', # 'session_post_login_uri_qs': u'post-login', # 'session_user_name': u'user-name', # }
configOptionsToLog = {}
self.documentRoot = config.get('documentRoot') if self.documentRoot: configOptionsToLog['documentRoot'] = str(self.documentRoot)
##URI Query Path #The URI query path option allows us to override what path is used #As the source object in this request. There are two options associated with it #1. What path must be specified in the request URL to initiate this processing #2. What query arg must be present to define the new source path self.uri_query_path = config.get('uri_query_path','/resourcebyuri') self.uri_param = config.get('uri_param','uri') self.uri_path = args.get(self.uri_param,[None])[0] configOptionsToLog['uri_query_path'] = str(self.uri_query_path) configOptionsToLog['uri_param'] = str(self.uri_param) configOptionsToLog['uri_path'] = str(self.uri_path)
##XSLT Param #The xslt param is used to specify what stylesheets are applied to the source object #It consists of a name of the param, and a list of stylesheets #Ignore PIS tells us the param to send to the processor self.xslt_param = config.get('xslt_param', 'xslt') self.stylesheets = args.get(self.xslt_param, []) configOptionsToLog['xslt_param'] = str(self.xslt_param) configOptionsToLog['stylesheets'] = str(self.stylesheets)
self.enable_pis_param = config.get('enable_pis_param','enable-pis') self.enable_pis = self.enable_pis_param in args configOptionsToLog['enable_pis_param'] = str(self.enable_pis_param) configOptionsToLog['enable_pis'] = str(self.enable_pis)
###Sessions #To define how sessions work, there are many options. First is what type of session are # supported. Then, where is the user name and password for sesion found. How is anon session #created. What are the uris to redirect to on an invalid and valid login #lastly, what page is displayed when a session is invalidated self.session_method = config.get('session_method') configOptionsToLog['session_method'] = str(self.session_method)
if self.session_method: self.session_user_name_qs = config.get('session_user_name', 'user-name') self.session_user_name = args.get(self.session_user_name_qs, [None])[0] configOptionsToLog['session_user_name (%s)' % self.session_user_name_qs] = str(self.session_user_name)
self.session_password_qs = config.get('session_password','password') self.session_password = args.get(self.session_password_qs,[None])[0] configOptionsToLog['session_password (%s)' % self.session_password_qs] = str(self.session_password)
self.session_ttl = int(config.get('session_ttl', '1800')) configOptionsToLog['session_ttl'] = str(self.session_ttl)
self.session_anonymous_qs = config.get('session_anonymous_flag','anonymous-login') self.session_anonymous = len(args.get(self.session_anonymous_qs,[])) > 0 configOptionsToLog['session_anonymous (%s)' % self.session_anonymous_qs] = str(self.session_anonymous)
self.session_invalid_uri = config.get('session_invalid_uri',None) configOptionsToLog['session_invalid_uri'] = str(self.session_invalid_uri)
self.session_post_login_uri_qs = config.get('session_post_login_uri_qs', None) self.session_post_login_uri = args.get(self.session_post_login_uri_qs, [None])[0] configOptionsToLog['session_post_login_uri (%s)' % self.session_post_login_uri_qs] = str(self.session_post_login_uri)
self.session_post_invalid_login_uri_qs = config.get('session_post_invalid_login_uri_qs',None) self.session_post_invalid_login_uri = args.get(self.session_post_invalid_login_uri_qs,[None])[0] configOptionsToLog['session_post_invalid_login_uri (%s)' % self.session_post_invalid_login_uri_qs] = str(self.session_post_invalid_login_uri)
self.session_permission_denied_uri = config.get('session_permission_denied_uri',None) configOptionsToLog['session_permission_denied_uri'] = str(self.session_permission_denied_uri)
self.http_post_encoding_var = config.get('http_post_encoding_var', 'http-post-encoding') ###Actions #Give us a hint as to what to do at a high level self.action_qs = config.get('action-name', 'action-name') self.action = args.get(self.action_qs, ['pass'])[0] configOptionsToLog['action (%s)' % self.action_qs] = str(self.action)
#Response URI is where we redirect to. It is used with some of the actions. self.response_uri_qs = config.get('response_uri', 'response-uri') self.response_uri = args.get(self.response_uri_qs,[None])[0] configOptionsToLog['response_uri (%s)' % self.response_uri_qs] = str(self.response_uri)
#Default XSLT allows us to set a stylesheet up for a resource type self.xsltDefaults = {} for resourceType, path in config['defaultXslt'].items(): self.xsltDefaults[resourceType] = path configOptionsToLog['default XSLT for resource type %s' % Schema.g_rdfResourceTypes[resourceType]] = path
#Set the polymorphic types here if ResourceTypes.ResourceType.DOCUMENT_DEFINITION in self.xsltDefaults: for rt in ResourceTypes.DOCUMENT_DEFINITIONS: if rt not in self.xsltDefaults: self.xsltDefaults[rt] = self.xsltDefaults[ResourceTypes.ResourceType.DOCUMENT_DEFINITION]
if ResourceTypes.ResourceType.CONTAINER in self.xsltDefaults: for rt in ResourceTypes.CONTAINERS: if rt not in self.xsltDefaults: self.xsltDefaults[rt] = self.xsltDefaults[ResourceTypes.ResourceType.CONTAINER]
if ResourceTypes.ResourceType.XML_DOCUMENT in self.xsltDefaults: for rt in ResourceTypes.XML_DOCUMENTS: if rt not in self.xsltDefaults: self.xsltDefaults[rt] = self.xsltDefaults[ResourceTypes.ResourceType.XML_DOCUMENT]
self.redirects = {} for src, dest in config['redirects'].items(): self.redirects[src] = dest configOptionsToLog['redirect destination for %s' % src] = dest
#Disable XSLT defaults is a query param that will override default processing self.disable_default_xslt_qs = config.get('disable_default_xslt_param', 'disable-default-xslt') self.disable_default_xslt = args.get(self.disable_default_xslt_qs,[None])[0] configOptionsToLog['disable_default_xslt (%s)' % self.disable_default_xslt_qs] = str(self.disable_default_xslt)
#Processing rules provide generic server-side processing tools #In order for proper overriding of rules self.processing_rules = [ #(pattern, src, xforms, args, pat_str, chain) (re.compile(r[0]), r[1], r[2], r[3], r[0], r[4] == 'yes') for r in config['processing_rules'] ] self.processing_rules.reverse()
#Log all the loaded config options self.server.errorLog.debug("Loaded config options from server config file and request query args:\n") keys = configOptionsToLog.keys() keys.sort() for optionkey in keys: self.server.errorLog.debug(' %s: %s\n' % (optionkey, configOptionsToLog.get(optionkey))) return
def getRepo(self): """ Return 1 if a response was already sent """ self.repo = None self.session = None
if self.action == 'login': ##Perform a login. Get the query args user-name and password #Create a new session, then return the response uri
if not self.session_method: raise FtServerHttpException(Error.SESSIONS_NOT_ENABLED, login_query_arg=self.action_qs)
if self.session_anonymous or (not self.session_user_name and not self.session_password): self.user = None self.password = None self.server.errorLog.debug("Session-based login requested for anonymous user") else: self.user = self.session_user_name self.password = sha.new(self.session_password or '').hexdigest() self.server.errorLog.debug("Session-based login requested for user %s" % self.user)
try: # sendError=False will prevent a failed login from generating an error response # that would normally be for HTTP authentication; we will generate our own # response, if needed repo = self.getRepository(sendError=False) except FtServerServerException, e: if e.errorCode == ServerError.INVALID_LOGIN: if not self.session_post_invalid_login_uri: raise FtServerHttpException(Error.BAD_LOGIN_FAILURE_REDIRECT)
#Send back a 302 self.server.errorLog.error("Repository access denied for user %s; will redirect" % self.user) self.sendRedirect(self.session_post_invalid_login_uri, HeaderDict.HeaderDict()) return True raise sid = repo.createSession('', self.session_ttl) self.session = Session.Create(sid, self.session_method, self.server.errorLog)
if self.session_post_login_uri: self.server.errorLog.info("Login successful; new session created; will redirect") new_response_headers = HeaderDict.HeaderDict() if self.session: self.session.updateHeaders(repo, new_response_headers, self.server.errorLog) self.sendRedirect(self.session_post_login_uri, new_response_headers) repo.txCommit() return True #If not, then they want to do more with this self.server.errorLog.info("Login successful; new session created; no redirect") self.repo = repo return False elif self.session_method: # Try to use the request headers to associate the request # with a session that has been stored in the repo. # This call will raise an exception if there is a sessionId # or login info in the request but no session in the repo. try: session = Session.Retrieve(self.args, self.headers_in, self.session_method, self.server.errorLog) if session: self.session = session #FIXME: Get client address and check for exceptions self.repo = SCore.RetrieveSession(session.id, '', self.server.errorLog, self.server.properties) self.server.errorLog.info("Retrieved Session for user %s" % self.repo.getUserName()) return False except FtServerServerException, e: if session and session.userAgentVariables.get('timeout-redirect'): self.session_invalid_uri = session.userAgentVariables.get('timeout-redirect')
# in case of bogus or expired sessionId, redirect if e.errorCode == ServerError.INVALID_SESSION and self.session_invalid_uri: self.server.errorLog.info("Session invalid; redirecting to %s" % self.session_invalid_uri) new_response_headers = HeaderDict.HeaderDict() self.session.updateHeaders(None, new_response_headers, self.server.errorLog) self.sendRedirect(self.session_invalid_uri, new_response_headers) return True elif e.errorCode != ServerError.INVALID_SESSION: from Ft.Server.Server import MessageSource self.server.errorLog.error("Error (%d) during session retrieval: %s" % (e.errorCode, e.message))
#If we are here, then there is no login or session (except maybe 403 auth) #Try the old-fashioned way self.repo = self.getRepository() return self.repo is None
def getHostHeader(self): #Apache reverse proxies add X-Forwarded-Host and munge Host headers = self.headers_in.terse() host_port = headers.get( 'X-Forwarded-Host', headers.get( 'Host', '%s:%s' % (self.server.hostname, self.server.port) ) ) return host_port
def sendRedirect(self, response_uri, headers, permanent=False): """Generates in the response a Location: header from the given response_uri, and sets the response code to 302 or 301, depending on whether permanent was 0 or 1. Defaults to 302 (temporary). The Location header value must be an absolute URI, optionally with a fragment. In the event of a relative URI, the base URI is calculated from the request's Host header, if any, or from the server's configured hostname and port.""" # fragment allowed in Location header per RFC 2616 errata # (see http://skrb.org/ietf/http_errata.html#location-fragments ) self.server.errorLog.debug("Redirect to '%s' requested" % response_uri) base_uri = 'http://' + self.getHostHeader() + self.path response_uri = Uri.Absolutize(response_uri, base_uri) self.server.errorLog.info("Preparing response to redirect to %s" % response_uri) self.headers_out['Location'] = response_uri tempHeaders = headers or HeaderDict.HeaderDict() self.headers_out.update(tempHeaders) if permanent: self.status = Status.HTTP_MOVED_PERMANENTLY else: self.status = Status.HTTP_MOVED_TEMPORARILY return ''
def load_resource(self, allowRedirect): if self.isSoap: res = self.repo.fetchResource(self.filename) self.stylesheets.append(self.filename) return res # if the requested resource path is the magic path that indicates that we want # to use a query argument value as the resource's path, then try to use it # # example: /resourcebyuri?uri=/ftss/data/us-states.xml # if self.path == self.uri_query_path: uri_arg = self.uri_path if not uri_arg: # Malformed msg = _("Requests for %s must have the query argument '%s'" % (self.uri_query_path, self.uri_param)) self.send_error(Status.HTTP_BAD_REQUEST, error=msg) return path = uri_arg else: path = self.filename self.server.errorLog.info("Loading Resource: %s\n" % str(path)) res = self.repo.fetchResource(path) #If resource is a container and the resource path in the request did #not have a trailing slash, prepare to redirect to the container with the slash #NOTE: use unparsed_path because path can contain ;no-traverse perm = False if self.unparsed_path[-1] != '/' and res.resourceType == ResourceType.CONTAINER: allowRedirect = True perm = True newURI = '%s/' % self.unparsed_path # ('', '', '/buyerbase', '', 'foo=bar', '') if self.unparsed_params: newURI += ';%s' % self.unparsed_params if self.unparsed_args: newURI += '?%s' % self.unparsed_args self.redirects[res.getAbsolutePath()] = newURI self.server.errorLog.debug("Container resource requested without trailing '/'. Redirecting.")
# handle redirects. # if there were no query args, allowRedirect will be false. if res.getAbsolutePath() in self.redirects and allowRedirect: new_response_headers = HeaderDict.HeaderDict() if self.session: self.session.updateHeaders(self.repo, new_response_headers, self.server.errorLog) self.sendRedirect(self.redirects[res.getAbsolutePath()], new_response_headers, perm) return None return res
def load_stylesheets(self, resource): """Load all available stylesheets""" if not self.stylesheets and not self.disable_default_xslt: #See if there is a default if resource.resourceType in self.xsltDefaults: self.stylesheets.append( self.xsltDefaults[resource.resourceType] ) stys = []
for sty in self.stylesheets: #Raise to let the exception handling take action if resource.__class__ == XmlBody: s = self.repo.fetchResource(sty) stys.append(s) else: s = resource.fetchResource(sty) stys.append(s) self.server.errorLog.info( "Loaded Stylesheet: %s\n" % str(s.getAbsolutePath()) )
return stys
def apply_rules(self): new_args = {} rule_applied, data = (0, None) for (pattern, src, xforms, args, pat_str, chain) in self.processing_rules: #path = res.getPath().absolutePath m = pattern.match(self.path) #Hmm. Is there a case where m is not None *and* m.group() != self.path if m and m.group() == self.path: self.server.errorLog.info("Applying rule matching pattern '%s'\n" % pat_str) try: res = self.repo.fetchResource(self.filename) except: res = self.repo if src: #Handle regular expression back references in xslt-source #e.g. <Rule pattern="a(b)c" xslt-source="\1"/> #Would produce a source esource of "b" if matched src = REPL_PAT.sub(lambda sm, pm=m: pm.group(int(sm.group(1))), src) res = res.fetchResource(src) #Handle regular expression back references in xslt-transform xforms = [ REPL_PAT.sub(lambda sm, pm=m: pm.group(int(sm.group(1))), xform) for xform in xforms.split() ] stys = [ res.fetchResource(xform) for xform in xforms ] if chain: stys = [ (i,) for i in stys ] new_args = cgi.parse_qs(args, keep_blank_values=True) matched_args = m.groupdict("") for k in matched_args: new_args[k] = [matched_args[k]] rule_applied, data = (1, (res, stys, new_args)) break return rule_applied, data
def get_form_encoding(self, form): if self.form_encoding: return import cgi encoding = 'ISO-8859-1' fields = cgi.parse_qs(form) if self.http_post_encoding_var in fields: encoding = fields[self.http_post_encoding_var][0] self.form_encoding = encoding
class XmlBody: def __init__(self, text, repo): self.body = text self.repo = repo return
def applyXslt(self, stylesheets, params=None, ignorePis=True, extParams=None, extModules=None): """ applies the specified stylesheets (with the specified transformation parameters) on the given string """ import Ft.Xml.Xslt.Processor from Ft.Server.Common import ResourceTypes, Schema, XmlLib from Ft.Server.Server import FtServerServerException, Error from Ft.Xml.InputSource import InputSourceFactory
extModules = extModules or []
params = params or {} extParams = extParams or {}
#First map the stylesheets if not isinstance(stylesheets, (list, tuple)): stylesheets = (stylesheets,) p = Ft.Xml.Xslt.Processor.Processor()
p.extensionParams = extParams
p._repository = self.repo mods = p.registerExtensionModules( ['Ft.Server.Server.Xslt']+extModules ) p.setDocumentReader( FtssInputSource.NonvalidatingReader ) for s in stylesheets: if isinstance(s, StringTypes): p.appendStylesheet(p.inputSourceFactory.fromUri(s)) elif hasattr(s,'toStylesheet'): #A document reference p.appendStylesheetInstance(s.toStylesheet(self.repo.getRoot())) elif hasattr(s,'asStylesheet'): #A reference to a document in the system p.appendStylesheetInstance(s.asStylesheet())
p.inputSourceFactory = FtssInputSource._FtssInputSourceFactory( self.repo._driver) rt = p.run(p.inputSourceFactory.fromString(self.body,'ftss:///'), ignorePis=ignorePis, topLevelParams=params) if extParams is not None and hasattr(p, 'extensionParams'): extParams.update(p.extensionParams)
imt = p._lastOutputParams.mediaType return rt, imt
|