def get_cors_headers(options, request_headers, request_method): origins_to_set = get_cors_origins(options, request_headers.get('Origin')) headers = CIDict() if not origins_to_set: # CORS is not enabled for this route return headers for origin in origins_to_set: # TODO, with CIDict, with will only allow one origin headers[ACL_ORIGIN] = origin headers[ACL_EXPOSE_HEADERS] = options.get('expose_headers') if options.get('supports_credentials'): headers[ACL_CREDENTIALS] = 'true' # case sensative # This is a preflight request # http://www.w3.org/TR/cors/#resource-preflight-requests if request_method == 'OPTIONS': acl_request_method = request_headers.get(ACL_REQUEST_METHOD, '').upper() # If there is no Access-Control-Request-Method header or if parsing # failed, do not set any additional headers if acl_request_method and acl_request_method in options.get('methods'): # If method is not a case-sensitive match for any of the values in # list of methods do not set any additional headers and terminate # this set of steps. headers[ACL_ALLOW_HEADERS] = get_allow_headers( options, request_headers.get(ACL_REQUEST_HEADERS)) headers[ACL_MAX_AGE] = str(options.get( 'max_age')) # sanic cannot handle integers in header values. headers[ACL_METHODS] = options.get('methods') else: LOG.info( "The request's Access-Control-Request-Method header does not match allowed methods. " "CORS headers will not be applied.") # http://www.w3.org/TR/cors/#resource-implementation if options.get('vary_header'): # Only set header if the origin returned will vary dynamically, # i.e. if we are not returning an asterisk, and there are multiple # origins that can be matched. if headers[ACL_ORIGIN] == '*': pass elif (len(options.get('origins')) > 1 or len(origins_to_set) > 1 or any(map(probably_regex, options.get('origins')))): headers['Vary'] = 'Origin' return CIDict((k, v) for k, v in headers.items() if v)
def fake_request(url='/', method='GET', route='/', headers=None, body=None): headers = headers or {} headers_ = CIDict(host='127.0.0.1') [headers_.__setitem__(*x) for x in headers.items()] app = Rafter() app.add_route(view, route, methods=[method]) request = app.request_class(url.encode('utf-8'), headers_, '1.1', method, None) request.app = app request.body = body or b'' return request
async def decorated_filter(request, *args, **kwargs): data = { 'headers': CIDict(request.headers), 'path': request.app.router.get(request)[2], 'params': RequestParameters(request.args), 'body': {} } if request.body: # Get body if we have something there if request.form: data['body'] = RequestParameters(request.form) else: # will raise 400 if cannot parse json data['body'] = deepcopy(request.json) if hasattr(request_schema, 'body') and request.form: _convert_params(request_schema.body, data['body']) if hasattr(request_schema, 'params') and data['params']: _convert_params(request_schema.params, data['params']) # Now, validate the whole thing try: model = request_schema(data, strict=False, validate=False) model.validate() request.validated = model.to_native() except BaseError as e: raise ValidationErrors(e.to_primitive()) return await get_response(request, *args, **kwargs)
def set_cors_headers(req, resp, options): """ Performs the actual evaluation of Sanic-CORS options and actually modifies the response object. This function is used both in the decorator and the after_request callback :param sanic.request.Request req: """ # If CORS has already been evaluated via the decorator, skip if isinstance(req.headers, (dict, CIDict)) and SANIC_CORS_EVALUATED in req.headers: LOG.debug('CORS have been already evaluated, skipping') del req.headers[SANIC_CORS_EVALUATED] return resp # Some libraries, like OAuthlib, set resp.headers to non Multidict # objects (Werkzeug Headers work as well). This is a problem because # headers allow repeated values. if not isinstance(resp.headers, CIDict): resp.headers = CIDict(resp.headers) headers_to_set = get_cors_headers(options, req.headers, req.method) LOG.debug('Settings CORS headers: %s', str(headers_to_set)) for k, v in headers_to_set.items(): resp.headers[k] = v return resp
def fake_request(): headers = CIDict(host='127.0.0.1') app = Rafter() app.add_route(view, '/', methods=['GET']) request = app.request_class(b'/', headers, '1.1', 'GET', None) request.app = app return request
def set_cors_headers(req, resp, context, options): """ Performs the actual evaluation of Sanic-CORS options and actually modifies the response object. This function is used both in the decorator and the after_request callback :param sanic.request.Request req: """ try: request_context = context.request[id(req)] except AttributeError: LOG.debug("Cannot find the request context. Is request already finished?") return resp # If CORS has already been evaluated via the decorator, skip evaluated = request_context.get(SANIC_CORS_EVALUATED, False) if evaluated: LOG.debug('CORS have been already evaluated, skipping') return resp # `resp` can be None in the case of using Websockets # however this case should have been handled in the `extension` and `decorator` methods # before getting here. This is a final failsafe check to prevent crashing if resp is None: return None # Some libraries, like OAuthlib, set resp.headers to non Multidict # objects (Werkzeug Headers work as well). This is a problem because # headers allow repeated values. # TODO: In sanic, the CIDict is _not_ a multidict. if not isinstance(resp.headers, CIDict): # FYI this has the added (bonus) side-effect that if resp.headers is None # (response headers can be None on websocket responses for example) # Then CIDict(None) actually creates an empty headers dict, that is correct in this situation. resp.headers = CIDict(resp.headers) headers_to_set = get_cors_headers(options, req.headers, req.method) LOG.debug('Settings CORS headers: %s', str(headers_to_set)) # dict .extend() does not work on CIDict (or multidict) so iterate over them and add them individually. for k, v in headers_to_set.items(): resp.headers[k] = v return resp
def set_cors_headers(req, resp, options): """ Performs the actual evaluation of Sanic-CORS options and actually modifies the response object. This function is used both in the decorator and the after_request callback :param sanic.request.Request req: """ # If CORS has already been evaluated via the decorator, skip if isinstance(req.headers, (dict, CIDict)) and SANIC_CORS_EVALUATED in req.headers: LOG.debug('CORS have been already evaluated, skipping') del req.headers[SANIC_CORS_EVALUATED] return resp # Some libraries, like OAuthlib, set resp.headers to non Multidict # objects (Werkzeug Headers work as well). This is a problem because # headers allow repeated values. # TODO: In sanic, the CIDict is _not_ a multidict. if not isinstance(resp.headers, CIDict): # FYI this has the added (bonus) side-effect that if resp.headers is None # (response headers can be None on websocket responses for example) # Then CIDict(None) actually creates an empty headers dict, that is correct in this situation. resp.headers = CIDict(resp.headers) headers_to_set = get_cors_headers(options, req.headers, req.method) LOG.debug('Settings CORS headers: %s', str(headers_to_set)) # dict .extend() does not work on CIDict (or multidict) so iterate over them and add them individually. for k, v in headers_to_set.items(): resp.headers[k] = v return resp
def source(self, request): ''' Pulls values off the request in the provided location :param request: The flask request object to parse arguments from ''' if isinstance(self.location, six.string_types): value = getattr(request, self.location, CIDict()) if callable(value): value = value() if value is not None: return value else: values = CIDict() for l in self.location: value = getattr(request, l, None) if callable(value): value = value() if value is not None: values.update(value) return values return CIDict()
def test_existing_vary_headers(request): return HTTPResponse('', status=200, headers=CIDict({'Vary': 'Accept-Encoding'}))
def test_multiple_set_cookie_headers(request): resp = HTTPResponse(body="Foo bar baz") resp.headers = CIDict() resp.headers['set-cookie'] = 'foo' resp.headers['set-cookie'] = 'bar' return resp
def handle_error(self, request, e): ''' Error handler for the API transforms a raised exception into a Flask response, with the appropriate HTTP status code and body. :param Exception e: the raised Exception object ''' # todo: sanic: wtf is this? #got_request_exception.send(current_app._get_current_object(), exception=e) app = request.app headers = CIDict() if e.__class__ in self.error_handlers: handler = self.error_handlers[e.__class__] result = handler(e) default_data, code, headers = unpack(result, 500) elif isinstance(e, SanicException): code = e.status_code status = COMMON_STATUS_CODES.get(code) if not status: status = ALL_STATUS_CODES.get(code) if status and isinstance(status, bytes): status = status.decode('ascii') default_data = {'message': getattr(e, 'message', status)} # headers = e.get_response().headers elif self._default_error_handler: result = self._default_error_handler(e) default_data, code, headers = unpack(result, 500) else: code = 500 status = COMMON_STATUS_CODES.get(code, str(e)) if status and isinstance(status, bytes): status = status.decode('ascii') default_data = { 'message': status, } default_data['message'] = default_data.get('message', str(e)) data = getattr(e, 'data', default_data) fallback_mediatype = None if code >= 500: exc_info = sys.exc_info() if exc_info[1] is None: exc_info = None #current_app.log_exception(exc_info) elif code == 404 and app.config.get("ERROR_404_HELP", True): data['message'] = self._help_on_404(request, data.get('message', None)) elif code == 406 and self.default_mediatype is None: # if we are handling NotAcceptable (406), make sure that # make_response uses a representation we support as the # default mediatype (so that make_response doesn't throw # another NotAcceptable error). supported_mediatypes = list(self.representations.keys()) fallback_mediatype = supported_mediatypes[ 0] if supported_mediatypes else "text/plain" # Remove blacklisted headers for header in HEADERS_BLACKLIST: headers.pop(header, None) resp = self.make_response(request, data, code, headers, fallback_mediatype=fallback_mediatype) if code == 401: resp = self.unauthorized(resp) return resp
def handle_error(self, request, e): ''' Error handler for the API transforms a raised exception into a Flask response, with the appropriate HTTP status code and body. :param Exception e: the raised Exception object ''' context = restplus.get_context_from_spf(self.spf_reg) app = context.app headers = CIDict() if e.__class__ in self.error_handlers: handler = self.error_handlers[e.__class__] result = handler(e) default_data, code, headers = unpack( result, HTTPStatus.INTERNAL_SERVER_ERROR) elif isinstance(e, SanicException): code = e.status_code if code is 200: status = b'OK' elif code is 404: status = b'Not Found' elif code is 500: status = b'Internal Server Error' else: code = HTTPStatus(code) status = ALL_STATUS_CODES.get(code.value) if status and isinstance(status, bytes): status = status.decode('ascii') default_data = {'message': getattr(e, 'message', status)} # headers = e.get_response().headers elif self._default_error_handler: result = self._default_error_handler(e) default_data, code, headers = unpack( result, HTTPStatus.INTERNAL_SERVER_ERROR) else: code = HTTPStatus.INTERNAL_SERVER_ERROR status = ALL_STATUS_CODES.get(code.value, str(e)) if status and isinstance(status, bytes): status = status.decode('ascii') default_data = { 'message': status, } default_data['message'] = default_data.get('message', str(e)) data = getattr(e, 'data', default_data) fallback_mediatype = None if code >= HTTPStatus.INTERNAL_SERVER_ERROR: exc_info = sys.exc_info() if exc_info[1] is None: exc_info = None context.log(logging.ERROR, exc_info) elif code == HTTPStatus.NOT_FOUND and app.config.get( "ERROR_404_HELP", True): data['message'] = self._help_on_404(request, data.get('message', None)) elif code == HTTPStatus.NOT_ACCEPTABLE and self.default_mediatype is None: # if we are handling NotAcceptable (406), make sure that # make_response uses a representation we support as the # default mediatype (so that make_response doesn't throw # another NotAcceptable error). supported_mediatypes = list(self.representations.keys()) fallback_mediatype = supported_mediatypes[ 0] if supported_mediatypes else "text/plain" # Remove blacklisted headers for header in HEADERS_BLACKLIST: headers.pop(header, None) resp = self.make_response(request, data, code, headers, fallback_mediatype=fallback_mediatype) if code == HTTPStatus.UNAUTHORIZED: resp = self.unauthorized(resp) return resp