def keep_status_start_response(status, headers, exc_info=None): for header, value in headers: if header.lower() == "set-cookie": self.headers.append((header, value)) else: replace_header(self.headers, header, value) return start_response(self.status, self.headers, exc_info)
def add_metadata(self, environ, identity): request = Request(environ) log.debug("CSRFMetadataProvider.add_metadata(%s)" % request.path) session_id = environ.get(self.auth_session_id) if not session_id: session_id = request.cookies.get(self.session_cookie) log.debug('session_id = %r' % session_id) if session_id and session_id != 'Set-Cookie:': token = sha1(session_id).hexdigest() identity.update({self.csrf_token_id: token}) log.debug("Identity updated with CSRF token") path = self.strip_script(environ, request.path) if path == self.login_handler: log.debug('Setting CSRF_AUTH_STATE') environ[self.auth_state] = True environ[self.token_env] = token else: environ[self.token_env] = self.extract_csrf_token(request) app = environ.get('repoze.who.application') if app: # This occurs during login in some application configurations if isinstance(app, HTTPFound) and environ.get(self.auth_state): log.debug('Got HTTPFound(302) from repoze.who.application') loc = update_qs(app.location(), {self.csrf_token_id: str(token)}) replace_header(app.headers, 'location', loc) log.debug('Altered headers: %s' % str(app.headers)) else: log.warning("Invalid session cookie %r, not setting CSRF token!" %session_id)
def keep_status_start_response(status, headers, exc_info=None): for header, value in headers: if header.lower() == 'set-cookie': self.headers.append((header, value)) else: replace_header(self.headers, header, value) return start_response(self.status, self.headers, exc_info)
def request(self, uri, method="GET", body=None, headers=None, wsgi_request=None, input=None, output=None, trusted=False): method = method.upper() wsgi_request = self._coerce_wsgi_request(wsgi_request) headers = self._coerce_headers(headers) if isinstance(output, basestring) and output.startswith('name '): output = get_format(output[5:].strip()) input, body, headers = self._coerce_input( input, body, headers) if body and not header_value(headers, 'content-type'): # We have to add a content type... content_type = input.choose_mimetype(headers, body) replace_header(headers, 'content-type', content_type) headers = self._set_accept(headers, output) if wsgi_request is not None: uri = self._resolve_uri(uri, wsgi_request) if self._internally_resolvable(uri, wsgi_request): return self._internal_request( uri, method=method, body=body, headers=headers, wsgi_request=wsgi_request, input=input, output=output, trusted=trusted) else: if not scheme_re.search(uri): raise ValueError( 'You gave a non-absolute URI (%r) and no wsgi_request to ' 'normalize it against' % uri) return self._external_request( uri, method=method, body=body, headers=headers, wsgi_request=wsgi_request, input=input, output=output, trusted=trusted)
def filter(self, environ, headers, data): status = environ.pop('flexfilter.status') if status == HTTPTemporaryRedirect().status: response = ''' <error> <code>%s</code> <location>%s</location> <message>%s</message> </error> ''' % (status, header_value(headers, 'Location'), header_value(headers, 'Warning')) replace_header(headers, 'Content-Length', len(response)) else: root = etree.HTML(data) message = escape(etree.tostring(root.find('.//body'), method="text").strip()) if not message: message = root.find('.//title').text details = "" code_node = root.find('.//code') if code_node is not None and code_node.text is not None: details = escape(code_node.text) # Shorten a bit. pos = details.find(',') if pos != -1: details = details[:pos] response = ''' <error> <code>%s</code> <message>%s</message> <details>%s</details> </error> ''' % (status, message, details) replace_header(headers, 'Content-Length', len(response)) return response
def add_metadata(self, environ, identity): request = Request(environ) log.debug("CSRFMetadataProvider.add_metadata(%s)" % request.path) session_id = environ.get(self.auth_session_id) if not session_id: session_id = request.cookies.get(self.session_cookie) log.debug('session_id = %r' % session_id) if session_id and session_id != 'Set-Cookie:': token = sha1(session_id).hexdigest() identity.update({self.csrf_token_id: token}) log.debug("Identity updated with CSRF token") path = self.strip_script(environ, request.path) if path == self.login_handler: log.debug('Setting CSRF_AUTH_STATE') environ[self.auth_state] = True environ[self.token_env] = token else: environ[self.token_env] = self.extract_csrf_token(request) app = environ.get('repoze.who.application') if app: # This occurs during login in some application configurations if isinstance(app, HTTPFound) and environ.get(self.auth_state): log.debug('Got HTTPFound(302) from repoze.who.application') loc = update_qs(app.location(), {self.csrf_token_id: str(token)}) replace_header(app.headers, 'location', loc) log.debug('Altered headers: %s' % str(app.headers)) else: log.warning("Invalid session cookie %r, not setting CSRF token!" % session_id)
def start_response_wrapper(status, headers, exc_info=None): location_header = 'location' status_code = int(status.split(None,1)[0]) if (status_code >= 301 and status_code <= 303) or status_code == 307: location = header_value(headers, location_header) if location: replace_header(headers, location_header, resolve_relative_url(location, environ)) return start_response(status, headers, exc_info)
def replace_app_header(app, header_name, value): from paste.response import replace_header if app.headers: headers = list(app.headers) else: headers = [] replace_header(headers, header_name, value) app.headers = headers
def _set_accept(self, headers, output): if not output: # We apparently don't care what we get return headers if isinstance(output, Format): accept = output.content_types elif isinstance(output, basestring): # Can't be a name, we already resolved that already accept = find_accept_for_type(output) else: raise TypeError( "output should be a mimetype or Format object, not %r" % output) replace_header(headers, 'Accept', ', '.join(accept)) return headers
def add_metadata(self, environ, identity): request = Request(environ) log.debug( b_('CSRFMetadataProvider.add_metadata(%(r_path)s)') % {'r_path': to_bytes(request.path)}) session_id = environ.get(self.auth_session_id) if not session_id: session_id = request.cookies.get(self.session_cookie) log.debug(b_('session_id = %(s_id)r') % {'s_id': to_bytes(session_id)}) if session_id and session_id != 'Set-Cookie:': environ[self.auth_session_id] = session_id token = sha1(session_id).hexdigest() identity.update({self.csrf_token_id: token}) log.debug(b_('Identity updated with CSRF token')) path = self.strip_script(environ, request.path) if path == self.login_handler: log.debug(b_('Setting CSRF_AUTH_STATE')) environ[self.auth_state] = True environ[self.token_env] = token else: environ[self.token_env] = self.extract_csrf_token(request) app = environ.get('repoze.who.application') if app: # This occurs during login in some application configurations if isinstance(app, HTTPFound) and environ.get(self.auth_state): log.debug( b_('Got HTTPFound(302) from' ' repoze.who.application')) # What possessed people to make this a string or # a function? location = app.location if hasattr(location, '__call__'): location = location() loc = update_qs(location, {self.csrf_token_id: str(token)}) headers = app.headers.items() replace_header(headers, 'location', loc) app.headers = ResponseHeaders(headers) log.debug( b_('Altered headers: %(headers)s') % {'headers': to_bytes(app.headers)}) else: log.warning( b_('Invalid session cookie %(s_id)r, not setting CSRF' ' token!') % {'s_id': to_bytes(session_id)})
def add_metadata(self, environ, identity): request = Request(environ) log.debug('CSRFMetadataProvider.add_metadata(%(r_path)s)' % {'r_path': to_bytes(request.path)}) session_id = environ.get(self.auth_session_id) if not session_id: session_id = request.cookies.get(self.session_cookie) log.debug('session_id = %(s_id)r' % {'s_id': to_bytes(session_id)}) if session_id and session_id != 'Set-Cookie:': environ[self.auth_session_id] = session_id token = sha1(session_id).hexdigest() identity.update({self.csrf_token_id: token}) log.debug('Identity updated with CSRF token') path = self.strip_script(environ, request.path) if path == self.login_handler: log.debug('Setting CSRF_AUTH_STATE') environ[self.auth_state] = True environ[self.token_env] = token else: environ[self.token_env] = self.extract_csrf_token(request) app = environ.get('repoze.who.application') if app: # This occurs during login in some application configurations if isinstance(app, HTTPFound) and environ.get(self.auth_state): log.debug('Got HTTPFound(302) from' ' repoze.who.application') # What possessed people to make this a string or # a function? location = app.location if hasattr(location, '__call__'): location = location() loc = update_qs(location, {self.csrf_token_id: str(token)}) headers = app.headers.items() replace_header(headers, 'location', loc) app.headers = ResponseHeaders(headers) log.debug('Altered headers: %(headers)s' % { 'headers': to_bytes(app.headers)}) else: log.warning('Invalid session cookie %(s_id)r, not setting CSRF' ' token!' % {'s_id': to_bytes(session_id)})
def __call__(self, environ, start_response): path = environ['PATH_INFO'] if not path: path = environ['PATH_INFO'] = '/' status, headers, body = intercept_output(environ, self.app, self.should_intercept, start_response) # self.should_intercept returned nada if status is None: return body # don't style if the url should not be styled for pattern in self.ignore_paths: if pattern.match(path): start_response(status, headers) return [body] # short circuit from theming if this is not likely to be HTML content_url = construct_url(environ) if self.should_ignore_url(content_url): start_response(status, headers) return [body] # short circuit if we have a 3xx, 204 or 401 error code status_code = status.split()[0] if status_code.startswith( '3') or status_code == '204' or status_code == '401': start_response(status, headers) return [body] # all good - apply the transform body = self.apply_transform(environ, body) replace_header(headers, 'content-length', str(len(body))) replace_header(headers, 'content-type', 'text/html; charset=utf-8') start_response(status, headers) return [body]
def __call__(self, environ, start_response): path = environ['PATH_INFO'] if not path: path = environ['PATH_INFO'] = '/' status, headers, body = intercept_output(environ, self.app, self.should_intercept, start_response) # self.should_intercept returned nada if status is None: return body # don't style if the url should not be styled for pattern in self.ignore_paths: if pattern.match(path): start_response(status, headers) return [body] # short circuit from theming if this is not likely to be HTML content_url = construct_url(environ) if self.should_ignore_url(content_url): start_response(status, headers) return [body] # short circuit if we have a 3xx, 204 or 401 error code status_code = status.split()[0] if status_code.startswith('3') or status_code == '204' or status_code == '401': start_response(status, headers) return [body] # all good - apply the transform body = self.apply_transform(environ, body) replace_header(headers, 'content-length', str(len(body))) replace_header(headers, 'content-type', 'text/html; charset=utf-8') start_response(status, headers) return [body]
def prepare_content(self, environ): if self.headers: headers = list(self.headers) else: headers = [] if 'html' in environ.get('HTTP_ACCEPT','') or \ '*/*' in environ.get('HTTP_ACCEPT',''): replace_header(headers, 'content-type', 'text/html') content = self.html(environ) else: replace_header(headers, 'content-type', 'text/plain') content = self.plain(environ) if isinstance(content, unicode): content = content.encode('utf8') cur_content_type = (header_value(headers, 'content-type') or 'text/html') replace_header(headers, 'content-type', cur_content_type + '; charset=utf8') return headers, content
def prepare_content(self, environ): if self.headers: headers = list(self.headers) else: headers = [] if 'html' in environ.get('HTTP_ACCEPT','') or \ '*/*' in environ.get('HTTP_ACCEPT',''): replace_header(headers, 'content-type', 'text/html') content = self.html(environ) else: replace_header(headers, 'content-type', 'text/plain') content = self.plain(environ) if isinstance(content, six.text_type): content = content.encode('utf8') cur_content_type = ( header_value(headers, 'content-type') or 'text/html') replace_header( headers, 'content-type', cur_content_type + '; charset=utf8') return headers, content
def __call__(self, environ, start_response): """Parse the request body as JSON, look up the method on the controller and if it exists, dispatch to it. """ length = 0 if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") abort(411) else: if environ['CONTENT_LENGTH'] == '': abort(411) length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: log.debug("Content-Length is 0") abort(411) raw_body = environ['wsgi.input'].read(length) json_body = json.loads(urllib.unquote_plus(raw_body)) self._req_id = json_body['id'] self._req_method = json_body['method'] self._req_params = json_body['params'] log.debug('id: %s, method: %s, params: %s', self._req_id, self._req_method, self._req_params) self._error = None try: self._func = self._find_method() except AttributeError: err = jsonrpc_error(self._req_id, 'method_not_found') return err(environ, start_response) # now that we have a method, make sure we have enough # parameters and pass off control to the controller. if not isinstance(self._req_params, dict): # JSON-RPC version 1 request. arglist = inspect.getargspec(self._func)[0][1:] if len(self._req_params) < len(arglist): err = jsonrpc_error(self._req_id, 'invalid_params') return err(environ, start_response) else: kargs = dict(zip(arglist, self._req_params)) else: # JSON-RPC version 2 request. Params may be default, and # are already a dict, so skip the parameter length check here. kargs = self._req_params # XX Fix this namespace clash. One cannot use names below as # method argument names as this stands! kargs['action'], kargs['environ'] = self._req_method, environ kargs['start_response'] = start_response self._rpc_args = kargs status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'application/json') start_response(status[0], headers, exc_info[0]) return output
def __call__(self, environ, start_response): if not environ.get('transcluder.transclude_response', True): return self.app(environ, start_response) environ = environ.copy() environ['transcluder.outcookies'] = {} if environ.has_key('HTTP_COOKIE'): environ['transcluder.incookies'] = parse_cookie_header(environ['HTTP_COOKIE']) else: environ['transcluder.incookies'] = [] if environ.has_key('HTTP_IF_NONE_MATCH'): environ['transcluder.etags'] = parse_merged_etag(environ['HTTP_IF_NONE_MATCH']) else: environ['transcluder.etags'] = {} request_url = construct_url(environ) environ[TRANSCLUDED_HTTP_HEADER] = request_url variables = self.get_template_vars(request_url) tc = Transcluder(variables, None, should_include=self.include_predicate, should_recurse=self.recursion_predicate) pm = PageManager(request_url, environ, self.deptracker, tc.find_dependencies, self.tasklist, self.etree_subrequest) def simple_fetch(url): status, headers, body, parsed = pm.fetch(url) if status.startswith('200'): return parsed else: raise Exception, 'Status was: %s' % status tc.fetch = simple_fetch if is_conditional_get(environ) and not pm.is_modified(): headers = [] pm.merge_headers_into(headers) start_response('304 Not Modified', headers) return [] pm.begin_speculative_gets() status, headers, body, parsed = pm.fetch(request_url) if parsed is not None: if tc.transclude(parsed, request_url): # XXX doctype body = lxmlutils.tostring(parsed, doctype_pair=("-//W3C//DTD HTML 4.01 Transitional//EN", "http://www.w3.org/TR/html4/loose.dtd")) #else no need to change body at all if isinstance(body, unicode): body = body.encode('utf-8') content_length = str(len(body)) replace_header(headers, 'content-length', content_length) replace_header(headers, 'content-type', 'text/html; charset=utf-8') pm.merge_headers_into(headers) start_response(status, headers) if isinstance(body, unicode): body = body.encode('utf-8') return [body]
self._rpc_args['environ'] = environ self._rpc_args['start_response'] = start_response status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'application/json') start_response(status[0], headers, exc_info[0]) log.info('IP: %s Request to %s time: %.3fs' % ( self._get_ip_addr(environ), safe_unicode(_get_access_path(environ)), time.time() - start) ) return output def _dispatch_call(self): """ Implement dispatch interface specified by WSGIController """ raw_response = '' try: raw_response = self._inspect_call(self._func) if isinstance(raw_response, HTTPError):
self._rpc_args["environ"] = environ self._rpc_args["start_response"] = start_response status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(("Content-Length", str(len(output[0])))) replace_header(headers, "Content-Type", "application/json") start_response(status[0], headers, exc_info[0]) return output def _dispatch_call(self): """ Implement dispatch interface specified by WSGIController """ try: raw_response = self._inspect_call(self._func) if isinstance(raw_response, HTTPError): self._error = str(raw_response) except JSONRPCError, e: self._error = str(e) except Exception, e:
self._rpc_args['environ'] = environ self._rpc_args['start_response'] = start_response status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'application/json') start_response(status[0], headers, exc_info[0]) return output def _dispatch_call(self): """ Implement dispatch interface specified by WSGIController """ try: raw_response = self._inspect_call(self._func) if isinstance(raw_response, HTTPError): self._error = str(raw_response) except JSONRPCError, e: self._error = str(e) except Exception, e:
class JSONRPCController(WSGIController): """ A WSGI-speaking JSON-RPC 2.0 controller class See the specification: `<http://groups.google.com/group/json-rpc/web/json-rpc-2-0>`. Many parts of this controller are modelled after XMLRPCController from Pylons 0.9.7 Valid controller return values should be json-serializable objects. Sub-classes should catch their exceptions and raise JSONRPCError if they want to pass meaningful errors to the client. Unhandled errors should be caught and return JSONRPC_INTERNAL_ERROR to the client. Parts of the specification not supported (yet): - Notifications - Batch """ def _get_method_args(self): """Return `self._rpc_args` to dispatched controller method chosen by __call__""" return self._rpc_args def __call__(self, environ, start_response): """Parse the request body as JSON, look up the method on the controller and if it exists, dispatch to it. """ length = 0 if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") abort(411) else: if environ['CONTENT_LENGTH'] == '': abort(411) length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: log.debug("Content-Length is 0") abort(411) raw_body = environ['wsgi.input'].read(length) json_body = json.loads(urllib.unquote_plus(raw_body)) self._req_id = json_body['id'] self._req_method = json_body['method'] self._req_params = json_body['params'] log.debug('id: %s, method: %s, params: %s', self._req_id, self._req_method, self._req_params) self._error = None try: self._func = self._find_method() except AttributeError, e: err = jsonrpc_error(self._req_id, 'method_not_found') return err(environ, start_response) # now that we have a method, make sure we have enough # parameters and pass off control to the controller. if not isinstance(self._req_params, dict): # JSON-RPC version 1 request. arglist = inspect.getargspec(self._func)[0][1:] if len(self._req_params) < len(arglist): err = jsonrpc_error(self._req_id, 'invalid_params') return err(environ, start_response) else: kargs = dict(zip(arglist, self._req_params)) else: # JSON-RPC version 2 request. Params may be default, and # are already a dict, so skip the parameter length check here. kargs = self._req_params # XX Fix this namespace clash. One cannot use names below as # method argument names as this stands! kargs['action'], kargs['environ'] = self._req_method, environ kargs['start_response'] = start_response self._rpc_args = kargs status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'application/json') start_response(status[0], headers, exc_info[0]) return output
def __call__(self, environ, start_response): """Parse an XMLRPC body for the method, and call it with the appropriate arguments""" # Pull out the length, return an error if there is no valid # length or if the length is larger than the max_body_length. log_debug = self._pylons_log_debug length = environ.get('CONTENT_LENGTH') if length: length = int(length) else: # No valid Content-Length header found if log_debug: log.debug("No Content-Length found, returning 411 error") abort(411) if length > self.max_body_length or length == 0: if log_debug: log.debug( "Content-Length larger than max body length. Max: " "%s, Sent: %s. Returning 413 error", self.max_body_length, length) abort(413, "XML body too large") body = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) rpc_args, orig_method = xmlrpclib.loads(body) method = self._find_method_name(orig_method) func = self._find_method(method) if not func: if log_debug: log.debug("Method: %r not found, returning xmlrpc fault", method) return xmlrpc_fault(0, "No such method name %r" % method)( environ, start_response) # Signature checking for params if hasattr(func, 'signature'): if log_debug: log.debug("Checking XMLRPC argument signature") valid_args = False params = xmlrpc_sig(rpc_args) for sig in func.signature: # Next sig if we don't have the same amount of args if len(sig) - 1 != len(rpc_args): continue # If the params match, we're valid if params == sig[1:]: valid_args = True break if not valid_args: if log_debug: log.debug("Bad argument signature recieved, returning " "xmlrpc fault") msg = ("Incorrect argument signature. %r recieved does not " "match %r signature for method %r" % \ (params, func.signature, orig_method)) return xmlrpc_fault(0, msg)(environ, start_response) # Change the arg list into a keyword dict based off the arg # names in the functions definition arglist = inspect.getargspec(func)[0][1:] kargs = dict(zip(arglist, rpc_args)) kargs['action'], kargs['environ'] = method, environ kargs['start_response'] = start_response self.rpc_kargs = kargs self._func = func # Now that we know the method is valid, and the args are valid, # we can dispatch control to the default WSGIController status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'text/xml') start_response(status[0], headers, exc_info[0]) return output
def __call__(self, environ, start_response): """Parse an XMLRPC body for the method, and call it with the appropriate arguments""" # Pull out the length, return an error if there is no valid # length or if the length is larger than the max_body_length. log_debug = self._pylons_log_debug length = environ.get('CONTENT_LENGTH') if length: length = int(length) else: # No valid Content-Length header found if log_debug: log.debug("No Content-Length found, returning 411 error") abort(411) if length > self.max_body_length or length == 0: if log_debug: log.debug("Content-Length larger than max body length. Max: " "%s, Sent: %s. Returning 413 error", self.max_body_length, length) abort(413, "XML body too large") body = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) rpc_args, orig_method = xmlrpclib.loads(body) method = self._find_method_name(orig_method) func = self._find_method(method) if not func: if log_debug: log.debug("Method: %r not found, returning xmlrpc fault", method) return xmlrpc_fault(0, "No such method name %r" % method)(environ, start_response) # Signature checking for params if hasattr(func, 'signature'): if log_debug: log.debug("Checking XMLRPC argument signature") valid_args = False params = xmlrpc_sig(rpc_args) for sig in func.signature: # Next sig if we don't have the same amount of args if len(sig)-1 != len(rpc_args): continue # If the params match, we're valid if params == sig[1:]: valid_args = True break if not valid_args: if log_debug: log.debug("Bad argument signature recieved, returning " "xmlrpc fault") msg = ("Incorrect argument signature. %r recieved does not " "match %r signature for method %r" % (params, func.signature, orig_method)) return xmlrpc_fault(0, msg)(environ, start_response) # Change the arg list into a keyword dict based off the arg # names in the functions definition arglist = inspect.getargspec(func)[0][1:] kargs = dict(zip(arglist, rpc_args)) kargs['action'], kargs['environ'] = method, environ kargs['start_response'] = start_response self.rpc_kargs = kargs self._func = func # Now that we know the method is valid, and the args are valid, # we can dispatch control to the default WSGIController status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'text/xml') start_response(status[0], headers, exc_info[0]) return output
def filter(self, environ, headers, data): url = construct_url(environ) static_url = environ['olpcproxy.static_url'] found = environ['olpcproxy.keys'] action = False if self.save_key in found: self.store.save_page_set(url, headers, data) action = True if self.remove_key in found: self.store.remove_page(url) action = True if environ.get('olpcproxy.downloads'): for index in environ['olpcproxy.downloads']: self.save_download(url, data, index) action = True if action: exc = httpexceptions.HTTPTemporaryRedirect( headers=[('Location', url)]) raise exc if '?' not in url: url_query = url + '?' else: url_query = url + '&' has_page = self.store.has_url(url) page = HTML(data) try: head = page.xpath('//head')[0] body = page.xpath('//body')[0] except IndexError: # Not a full HTML page return data self.sub_links(url, page, static_url) if has_page: time_diff = time.time() - self.store.url_cache_time(url) time_diff = format_time_diff(time_diff) message = ['This page was cached %s ago. You may ' % time_diff, tag.a('remove it from the cache', href=url_query+self.remove_key)] div_class = 'olpc-cached' else: message = ['This page is NOT cached. You may ', tag.a('add it to the cache', href=url_query+self.save_key)] div_class = None if head_style: insert_beginning( head, tag.style(head_style % {'static_url': static_url}, type="text/css")) image_location = static_url + '/x-small.gif' msg = tag.div( message, tag.a(tag.img(src=image_location, border=0, id="olpc-close-image"), href="#", onclick="document.getElementById('olpc-top-message').style.display='none'", valign="top"), id="olpc-top-message", class_=div_class) bundles = elquery.get_elements_by_class(body, 'olpc-bundle') if bundles: image_location = static_url + '/caution.gif' append( msg, tag.br(), tag.img(src=image_location), "Bundles were found in this page") for index, bundle in enumerate(bundles): b_msg = tag.div( tag.a(tag.img(src=static_url+'/arrow-down-red.gif', border=0), "You may download this bundle", href=url_query+self.download_key+'='+str(index))) insert_beginning(bundle, b_msg) insert_beginning(body, msg, tag.br(clear="all")) data = tostring(page, True) # Now fix up the content-type: content_type = header_value(headers, 'content-type') or '' content_type = self._charset_re.sub('', content_type).strip().lstrip(';') content_type += '; charset=utf' replace_header(headers, 'content-type', content_type) return data
def _handle_request(self, environ, start_response): start = time.time() ip_addr = self.ip_addr = self._get_ip_addr(environ) self._req_id = None if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") return jsonrpc_error(retid=self._req_id, message="No Content-Length in request") else: length = environ['CONTENT_LENGTH'] or 0 length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: log.debug("Content-Length is 0") return jsonrpc_error(retid=self._req_id, message="Content-Length is 0") raw_body = environ['wsgi.input'].read(length) try: json_body = json.loads(raw_body) except ValueError as e: # catch JSON errors Here return jsonrpc_error(retid=self._req_id, message="JSON parse error ERR:%s RAW:%r" % (e, raw_body)) # check AUTH based on API key try: self._req_api_key = json_body['api_key'] self._req_id = json_body['id'] self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug( 'method: %s, params: %s', self._req_method, self._request_params ) except KeyError as e: return jsonrpc_error(retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: return jsonrpc_error(retid=self._req_id, message='Invalid API key') auth_u = AuthUser(dbuser=u) if not AuthUser.check_ip_allowed(auth_u, ip_addr): return jsonrpc_error(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr,)) else: log.info('Access for IP:%s allowed', ip_addr) except Exception as e: return jsonrpc_error(retid=self._req_id, message='Invalid API key') self._error = None try: self._func = self._find_method() except AttributeError as e: return jsonrpc_error(retid=self._req_id, message=str(e)) # now that we have a method, add self._req_params to # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] defaults = map(type, argspec[3] or []) default_empty = types.NotImplementedType # kw arguments required by this method func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults), fillvalue=default_empty)) # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have # authuser attribute set self.authuser = auth_u # This attribute will need to be first param of a method that uses # api_key, which is translated to instance of user at that name USER_SESSION_ATTR = 'apiuser' if USER_SESSION_ATTR not in arglist: return jsonrpc_error( retid=self._req_id, message='This method [%s] does not support ' 'authentication (missing %s param)' % ( self._func.__name__, USER_SESSION_ATTR) ) # get our arglist and check if we provided them as args for arg, default in func_kwargs.iteritems(): if arg == USER_SESSION_ATTR: # USER_SESSION_ATTR is something translated from API key and # this is checked before so we don't need validate it continue # skip the required param check if it's default value is # NotImplementedType (default_empty) if default == default_empty and arg not in self._request_params: return jsonrpc_error( retid=self._req_id, message=( 'Missing non optional `%s` arg in JSON DATA' % arg ) ) self._rpc_args = {USER_SESSION_ATTR: u} self._rpc_args.update(self._request_params) self._rpc_args['action'] = self._req_method self._rpc_args['environ'] = environ self._rpc_args['start_response'] = start_response status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'application/json') start_response(status[0], headers, exc_info[0]) log.info('IP: %s Request to %s time: %.3fs' % ( self._get_ip_addr(environ), safe_unicode(_get_access_path(environ)), time.time() - start) ) return output