def get_resource(environ, start_response): slaveinfo, space_tag = setup_request(environ) #space_tag = shift_path_info(environ) #Forward the query to the slave, and get the result #FIXME: Zen should do some processing in order to assess/override/suppress etc. the rulesheet #Though this may be in the form of utility functions for the driver resource = slaveinfo.resource_factory() if resource is None or resource == '': start_response(status_response(slaveinfo.resp_status), slaveinfo.resp_headers) return ["Unable to access resource\n"] imt = requested_imt(environ) lang = requested_lang(environ) handler = resource.type.run_rulesheet(environ, 'GET', imt, lang) rendered = handler(resource) headers = [("Content-Type", str(handler.imt))] vary_header = "Accept" if handler.ttl: headers.append(("Cache-Control", "max-age="+str(handler.ttl))) if handler.lang: vary_header += ",Accept-Language" headers.append(("Content-Language", str(handler.lang))) headers.append(("Vary",vary_header)) start_response(status_response(httplib.OK), headers) return rendered
def moin_error_wrapper(wsgiapp): @wraps(wsgiapp) def handler(environ, start_response): status_info = {} # Dictionary of collected status information # Replacement for the WSGI start_response function. This merely # collects response data in a dictionary for later use if no errors occur def local_start_response(status, headers): status_info['status'] = status status_info['headers'] = headers # Try to run the supplied WSGI handler try: body = wsgiapp(environ, local_start_response) # If control reaches here, no errors. Proceed with normal WSGI response start_response(status_info['status'],status_info['headers']) return body # Error handling for specifying an invalid moin target name (i.e., not configured, misspelled) except BadTargetError,e: start_response(status_response(httplib.NOT_FOUND), [ ('Content-Type','text/plain') ]) return error_badtarget.safe_substitute(e.parms) # Error handling for back-end HTTP authorization failure. For example, # if the HTTP server hosting MoinMoin has rejected our requests due to # bad HTTP authorization. except HTTPAuthorizationError,e: start_response(status_response(httplib.FORBIDDEN), [ ('Content-Type','text/plain') ]) return error_httpforbidden.safe_substitute(e.parms)
def post_resource(environ, start_response): ''' Create a new record with a resource type ''' slaveinfo, space_tag = setup_request(environ) temp_fpath = read_http_body_to_temp(environ, start_response) body = open(temp_fpath, "r").read() resource_type = slaveinfo.resource_factory() imt = environ['CONTENT_TYPE'].split(';')[0] lang = environ.get('CONTENT_LANGUAGE') handler = resource_type.run_rulesheet(environ, environ['REQUEST_METHOD'], imt, lang) new_path, content = handler(resource_type, body) logger.debug('rulesheet transform output & new uri path (post_resource): ' + repr((content[:100], new_path))) #Comes back as Unicode, but we need to feed it to slave as encoded byte string content = content.encode('utf-8') environ['wsgi.input'] = cStringIO.StringIO(content) environ['CONTENT_LENGTH'] = len(content) response = slaveinfo.create_resource(new_path) if not slaveinfo.resp_status.startswith('2'): start_response(status_response(slaveinfo.resp_status), slaveinfo.resp_headers) return ["Unable to create resource\n"] start_response(slaveinfo.resp_status, slaveinfo.resp_headers) return response
def moin_error_wrapper(wsgiapp): @wraps(wsgiapp) def handler(environ, start_response): status_info = {} # Dictionary of collected status information # Replacement for the WSGI start_response function. This merely # collects response data in a dictionary for later use if no errors occur def local_start_response(status, headers): status_info['status'] = status status_info['headers'] = headers # Try to run the supplied WSGI handler try: body = wsgiapp(environ, local_start_response) # If control reaches here, no errors. Proceed with normal WSGI response start_response(status_info['status'], status_info['headers']) return body # Error handling for specifying an invalid moin target name (i.e., not configured, misspelled) except BadTargetError, e: start_response(status_response(httplib.NOT_FOUND), [('Content-Type', 'text/plain')]) return error_badtarget.safe_substitute(e.parms) # Error handling for back-end HTTP authorization failure. For example, # if the HTTP server hosting MoinMoin has rejected our requests due to # bad HTTP authorization. except HTTPAuthorizationError, e: start_response(status_response(httplib.FORBIDDEN), [('Content-Type', 'text/plain')]) return error_httpforbidden.safe_substitute(e.parms)
def delete_resource(environ, start_response): slaveinfo, space_tag = setup_request(environ) response = slaveinfo.delete_resource() if not slaveinfo.resp_status.startswith('2'): start_response(status_response(slaveinfo.resp_status), slaveinfo.resp_headers) return ["Unable to delete resource\n"] start_response(slaveinfo.resp_status, slaveinfo.resp_headers) return response
def prep_slave_response(self, resp): ''' Convert CouchDB response to Zen response ''' # Keep it conservative. etag? cache-control? COPY_HEADERS = lambda x: x[0] in ['date','content-type'] self.resp_headers = filter(COPY_HEADERS,resp.iteritems()) self.resp_status = status_response(resp.get('status') or '500')
def put_resource(environ, start_response): slaveinfo, space_tag = setup_request(environ) resource_type = check_forced_type(environ, start_response, slaveinfo) imt = environ['CONTENT_TYPE'].split(';')[0] lang = environ.get('CONTENT_LANGUAGE') #FIXME support multiple temp_fpath = read_http_body_to_temp(environ, start_response) body = open(temp_fpath, "r").read() if not resource_type: resource = slaveinfo.resource_factory() resource_type = resource.type handler = resource_type.run_rulesheet(environ, environ['REQUEST_METHOD'], imt, lang) content = handler(resource_type, body) logger.debug('rulesheet transform output (put_resource): ' + repr(content[:100])) #Comes back as Unicode, but we need to feed it to slave as encoded byte string content = content.encode('utf-8') environ['wsgi.input'] = cStringIO.StringIO(content) environ['CONTENT_LENGTH'] = len(content) #headers = req_headers #headers['Content-Type'] = 'text/plain' #if creds: # user, passwd = creds # H.add_credentials(user, passwd) #resp, content = H.request(zenuri_to_moinrest(environ), "PUT", body=wikified.encode('UTF-8'), headers=headers) #start_response(status_response(slave_resp_status), [("Content-Type", resp['content-type'])]) response = slaveinfo.update_resource() if not slaveinfo.resp_status.startswith('2'): start_response(status_response(slaveinfo.resp_status), slaveinfo.resp_headers) return ["Unable to update resource\n"] start_response(slaveinfo.resp_status, slaveinfo.resp_headers) return response
def handler(environ, start_response): status_info = {} # Dictionary of collected status information # Replacement for the WSGI start_response function. This merely # collects response data in a dictionary for later use if no errors occur def local_start_response(status, headers): status_info['status'] = status status_info['headers'] = headers # Try to run the supplied WSGI handler try: body = wsgiapp(environ, local_start_response) # If control reaches here, no errors. Proceed with normal WSGI response start_response(status_info['status'], status_info['headers']) return body # Error handling for specifying an invalid moin target name (i.e., not configured, misspelled) except BadTargetError, e: start_response(status_response(httplib.NOT_FOUND), [('Content-Type', 'text/plain')]) return error_badtarget.safe_substitute(e.parms)
def handler(environ, start_response): status_info = {} # Dictionary of collected status information # Replacement for the WSGI start_response function. This merely # collects response data in a dictionary for later use if no errors occur def local_start_response(status, headers): status_info['status'] = status status_info['headers'] = headers # Try to run the supplied WSGI handler try: body = wsgiapp(environ, local_start_response) # If control reaches here, no errors. Proceed with normal WSGI response start_response(status_info['status'],status_info['headers']) return body # Error handling for specifying an invalid moin target name (i.e., not configured, misspelled) except BadTargetError,e: start_response(status_response(httplib.NOT_FOUND), [ ('Content-Type','text/plain') ]) return error_badtarget.safe_substitute(e.parms)
def get_page(environ, start_response): #logger.debug('get_page: ' + repr((environ['SCRIPT_NAME'], environ['PATH_INFO']))) req_headers = copy_headers_to_dict(environ,exclude=['HTTP_ACCEPT_ENCODING']) wiki_id, base, opener, original_page, wrapped_wiki_base = target(environ) page = environ['PATH_INFO'].lstrip('/') check_auth(environ, start_response, base, opener, req_headers) upstream_handler = None status = httplib.OK params = cgi.parse_qs(environ['QUERY_STRING']) #Note: probably a better solution here: http://code.google.com/p/mimeparse/ accepted_imts = environ.get('HTTP_ACCEPT', '').split(',') #logger.debug('accepted_imts: ' + repr(accepted_imts)) imt = first_item(dropwhile(lambda x: '*' in x, accepted_imts)) #logger.debug('imt: ' + repr(imt)) params_for_moin = {} cache_max_age = CACHE_MAX_AGE # max-age of this response. If set to None, it will not be used if NO_CACHE_PATHS and first_item(dropwhile(lambda x: x not in page, NO_CACHE_PATHS)): cache_max_age = None if 'rev' in params: #XXX: Not compatible with search #params_for_moin = {'rev' : params['rev'][0], 'action': 'recall'} params_for_moin = {'rev' : params['rev'][0]} if 'search' in params: searchq = params['search'][0] query = urllib.urlencode({'value' : searchq, 'action': 'fullsearch', 'context': '180', 'fullsearch': 'Text'}) #?action=fullsearch&context=180&value=foo&=Text url = absolutize('?'+query, base) request = urllib2.Request(url, None, req_headers) ctype = moin.RDF_IMT cache_max_age = None #elif 'action' in params and params['action'][0] == 'recall': elif moin.HTML_IMT in environ.get('HTTP_ACCEPT', ''): params = urllib.urlencode(params_for_moin) url = absolutize(page+'?'+params, base) request = urllib2.Request(url, None, req_headers) ctype = moin.HTML_IMT elif moin.RDF_IMT in environ.get('HTTP_ACCEPT', ''): #FIXME: Make unique flag optional #url = base + '/RecentChanges?action=rss_rc&unique=1&ddiffs=1' url = absolutize('RecentChanges?action=rss_rc&unique=1&ddiffs=1', base) #print >> sys.stderr, (url, base, '/RecentChanges?action=rss_rc&unique=1&ddiffs=1', ) request = urllib2.Request(url, None, req_headers) ctype = moin.RDF_IMT elif moin.ATTACHMENTS_IMT in environ.get('HTTP_ACCEPT', ''): url = absolutize(page + '?action=AttachFile', base) request = urllib2.Request(url, None, req_headers) ctype = moin.ATTACHMENTS_IMT def upstream_handler(): #Sigh. Sometimes you have to break some Tag soup eggs to make a RESTful omlette with closing(opener.open(request)) as resp: rbody = resp.read() doc = htmlparse(rbody) raise_embedded_error(doc) attachment_nodes = doc.xml_select(u'//*[contains(@href, "action=AttachFile") and contains(@href, "do=view")]') targets = [] for node in attachment_nodes: target = [ param.split('=', 1)[1] for param in node.href.split(u'&') if param.startswith('target=') ][0] targets.append(target) output = structencoder(indent=u"yes") output.feed( ROOT( E((u'attachments'), (E(u'attachment', {u'href': unicode(t)}) for t in targets) ) )) return output.read(), ctype #Notes on use of URI parameters - http://markmail.org/message/gw6xbbvx4st6bksw elif ';attachment=' in page: page, attachment = page.split(';attachment=', 1) url = absolutize(page + '?action=AttachFile&do=get&target=' + attachment, base) request = urllib2.Request(url, None, req_headers) def upstream_handler(): with closing(opener.open(request)) as resp: rbody = resp.read() return rbody, dict(resp.info())['content-type'] # elif ';history' in page: cache_max_age = None page, discard = page.split(';history', 1) ctype = moin.XML_IMT def upstream_handler(): revs = scrape_page_history(page, base, opener, req_headers) output = structencoder(indent=u"yes") output.feed( ROOT( E((u'history'), (E(u'rev', {u'id': unicode(r['rev']), u'editor': unicode(r['editor']), u'date': unicode(r['date']).replace(' ', 'T')}) for r in revs) ) )) return output.read(), ctype elif imt: params_for_moin.update({'mimetype': imt}) params = urllib.urlencode(params_for_moin) url = absolutize(page, base) + '?' + params request = urllib2.Request(url, None, req_headers) ctype = moin.DOCBOOK_IMT else: params_for_moin.update({'action': 'raw'}) params = urllib.urlencode(params_for_moin) url = absolutize(page, base) + '?' + params request = urllib2.Request(url, None, req_headers) ctype = moin.WIKITEXT_IMT try: if upstream_handler: rbody, ctype = upstream_handler() else: with closing(opener.open(request)) as resp: rbody = resp.read() #headers = {moin.ORIG_BASE_HEADER: base} #moin_base = absolutize(wiki_id, base) moin_base_info = base + ' ' + wrapped_wiki_base + ' ' + original_page response_headers = [("Content-Type", ctype), ("Vary", "Accept"), (moin.ORIG_BASE_HEADER, moin_base_info)] if cache_max_age: response_headers.append(("Cache-Control","max-age="+str(cache_max_age))) start_response(status_response(status), response_headers) return rbody except urllib2.URLError, e: if e.code == 401: raise HTTPAuthorizationError(url=request.get_full_url()) if e.code == 403: raise MoinMustAuthenticateError(url=request.get_full_url(),target=wiki_id) if e.code == 404: raise MoinNotFoundError(fronturl=request_uri(environ),backurl=url) else: raise UnexpectedResponseError(url=url,code=e.code,error=str(e))
]) return error_badtarget.safe_substitute(e.parms) # Error handling for back-end HTTP authorization failure. For example, # if the HTTP server hosting MoinMoin has rejected our requests due to # bad HTTP authorization. except HTTPAuthorizationError,e: start_response(status_response(httplib.FORBIDDEN), [ ('Content-Type','text/plain') ]) return error_httpforbidden.safe_substitute(e.parms) # Error handling for MoinMoin authorization failure. This occurs # if the user and password supplied to MoinMoin is rejected. except MoinAuthorizationError,e: start_response(status_response(httplib.FORBIDDEN), [ ('Content-Type','text/plain') ]) return error_moinauthforbidden.safe_substitute(e.parms) # Error handling for unexpected HTTP status codes except UnexpectedResponseError,e: start_response(status_response(httplib.INTERNAL_SERVER_ERROR), [ ('Content-Type','text/plain') ]) return error_unexpectedresponse.safe_substitute(e.parms) # Authentication required by MoinMoin. This isn't an error, but we # have to translate this into a 401 response to send back to the client # in order to get them to supply the appropriate username/password except MoinMustAuthenticateError,e:
def get_page(environ, start_response): #logger.debug('get_page: ' + repr((environ['SCRIPT_NAME'], environ['PATH_INFO']))) req_headers = copy_headers_to_dict(environ, exclude=['HTTP_ACCEPT_ENCODING']) wiki_id, base, opener, original_page, wrapped_wiki_base = target(environ) page = environ['PATH_INFO'].lstrip('/') check_auth(environ, start_response, base, opener, req_headers) upstream_handler = None status = httplib.OK params = cgi.parse_qs(environ['QUERY_STRING']) #Note: probably a better solution here: http://code.google.com/p/mimeparse/ accepted_imts = environ.get('HTTP_ACCEPT', '').split(',') #logger.debug('accepted_imts: ' + repr(accepted_imts)) imt = first_item(dropwhile(lambda x: '*' in x, accepted_imts)) #logger.debug('imt: ' + repr(imt)) params_for_moin = {} cache_max_age = CACHE_MAX_AGE # max-age of this response. If set to None, it will not be used if NO_CACHE_PATHS and first_item( dropwhile(lambda x: x not in page, NO_CACHE_PATHS)): cache_max_age = None if 'rev' in params: #XXX: Not compatible with search #params_for_moin = {'rev' : params['rev'][0], 'action': 'recall'} params_for_moin = {'rev': params['rev'][0]} if 'search' in params: searchq = params['search'][0] query = urllib.urlencode({ 'value': searchq, 'action': 'fullsearch', 'context': '180', 'fullsearch': 'Text' }) #?action=fullsearch&context=180&value=foo&=Text url = absolutize('?' + query, base) request = urllib2.Request(url, None, req_headers) ctype = moin.RDF_IMT cache_max_age = None #elif 'action' in params and params['action'][0] == 'recall': elif moin.HTML_IMT in environ.get('HTTP_ACCEPT', ''): params = urllib.urlencode(params_for_moin) url = absolutize(page + '?' + params, base) request = urllib2.Request(url, None, req_headers) ctype = moin.HTML_IMT elif moin.RDF_IMT in environ.get('HTTP_ACCEPT', ''): #FIXME: Make unique flag optional #url = base + '/RecentChanges?action=rss_rc&unique=1&ddiffs=1' url = absolutize('RecentChanges?action=rss_rc&unique=1&ddiffs=1', base) #print >> sys.stderr, (url, base, '/RecentChanges?action=rss_rc&unique=1&ddiffs=1', ) request = urllib2.Request(url, None, req_headers) ctype = moin.RDF_IMT elif moin.ATTACHMENTS_IMT in environ.get('HTTP_ACCEPT', ''): url = absolutize(page + '?action=AttachFile', base) request = urllib2.Request(url, None, req_headers) ctype = moin.ATTACHMENTS_IMT def upstream_handler(): #Sigh. Sometimes you have to break some Tag soup eggs to make a RESTful omlette with closing(opener.open(request)) as resp: rbody = resp.read() doc = htmlparse(rbody) raise_embedded_error(doc) attachment_nodes = doc.xml_select( u'//*[contains(@href, "action=AttachFile") and contains(@href, "do=view")]' ) targets = [] for node in attachment_nodes: target = [ param.split('=', 1)[1] for param in node.href.split(u'&') if param.startswith('target=') ][0] targets.append(target) output = structencoder(indent=u"yes") output.feed( ROOT( E((u'attachments'), (E(u'attachment', {u'href': unicode(t)}) for t in targets)))) return output.read(), ctype #Notes on use of URI parameters - http://markmail.org/message/gw6xbbvx4st6bksw elif ';attachment=' in page: page, attachment = page.split(';attachment=', 1) url = absolutize( page + '?action=AttachFile&do=get&target=' + attachment, base) request = urllib2.Request(url, None, req_headers) def upstream_handler(): with closing(opener.open(request)) as resp: rbody = resp.read() return rbody, dict(resp.info())['content-type'] # elif ';history' in page: cache_max_age = None page, discard = page.split(';history', 1) ctype = moin.XML_IMT def upstream_handler(): revs = scrape_page_history(page, base, opener, req_headers) output = structencoder(indent=u"yes") output.feed( ROOT( E((u'history'), (E( u'rev', { u'id': unicode(r['rev']), u'editor': unicode(r['editor']), u'date': unicode(r['date']).replace(' ', 'T') }) for r in revs)))) return output.read(), ctype elif imt: params_for_moin.update({'mimetype': imt}) params = urllib.urlencode(params_for_moin) url = absolutize(page, base) + '?' + params request = urllib2.Request(url, None, req_headers) ctype = moin.DOCBOOK_IMT else: params_for_moin.update({'action': 'raw'}) params = urllib.urlencode(params_for_moin) url = absolutize(page, base) + '?' + params request = urllib2.Request(url, None, req_headers) ctype = moin.WIKITEXT_IMT try: if upstream_handler: rbody, ctype = upstream_handler() else: with closing(opener.open(request)) as resp: rbody = resp.read() #headers = {moin.ORIG_BASE_HEADER: base} #moin_base = absolutize(wiki_id, base) moin_base_info = base + ' ' + wrapped_wiki_base + ' ' + original_page response_headers = [("Content-Type", ctype), ("Vary", "Accept"), (moin.ORIG_BASE_HEADER, moin_base_info)] if cache_max_age: response_headers.append( ("Cache-Control", "max-age=" + str(cache_max_age))) start_response(status_response(status), response_headers) return rbody except urllib2.URLError, e: if e.code == 401: raise HTTPAuthorizationError(url=request.get_full_url()) if e.code == 403: raise MoinMustAuthenticateError(url=request.get_full_url(), target=wiki_id) if e.code == 404: raise MoinNotFoundError(fronturl=request_uri(environ), backurl=url) else: raise UnexpectedResponseError(url=url, code=e.code, error=str(e))
start_response(status_response(httplib.NOT_FOUND), [('Content-Type', 'text/plain')]) return error_badtarget.safe_substitute(e.parms) # Error handling for back-end HTTP authorization failure. For example, # if the HTTP server hosting MoinMoin has rejected our requests due to # bad HTTP authorization. except HTTPAuthorizationError, e: start_response(status_response(httplib.FORBIDDEN), [('Content-Type', 'text/plain')]) return error_httpforbidden.safe_substitute(e.parms) # Error handling for MoinMoin authorization failure. This occurs # if the user and password supplied to MoinMoin is rejected. except MoinAuthorizationError, e: start_response(status_response(httplib.FORBIDDEN), [('Content-Type', 'text/plain')]) return error_moinauthforbidden.safe_substitute(e.parms) # Error handling for unexpected HTTP status codes except UnexpectedResponseError, e: start_response(status_response(httplib.INTERNAL_SERVER_ERROR), [('Content-Type', 'text/plain')]) return error_unexpectedresponse.safe_substitute(e.parms) # Authentication required by MoinMoin. This isn't an error, but we # have to translate this into a 401 response to send back to the client # in order to get them to supply the appropriate username/password except MoinMustAuthenticateError, e: start_response(status_response(httplib.UNAUTHORIZED), [('Content-Type', 'text/plain'),