def get_cors_headers(options, request_headers, request_method): origins_to_set = get_cors_origins(options, request_headers.get('Origin')) headers = CIMultiDict() 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 # With CIMultiDict it should work with multiple 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 CIMultiDict((k, v) for k, v in headers.items() if v)
def on_headers_complete(self): remote_addr = self.transport.get_extra_info('peername') if remote_addr: self.headers.append(('Remote-Addr', '%s:%s' % remote_addr)) self.request = Request(url_bytes=self.url, headers=CIMultiDict(self.headers), version=self.parser.get_http_version(), method=self.parser.get_method().decode())
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 if resp.headers is None: resp.headers = CIMultiDict() 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 so # iterate over them and add them individually. try: resp.headers.extend(headers_to_set) except Exception as e1: for k, v in headers_to_set.items(): try: resp.headers.add(k, v) except Exception as e2: 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, CIMultiDict()) if callable(value): value = value() if value is not None: return value else: values = CIMultiDict() 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 CIMultiDict()
def test_multiple_set_cookie_headers(request): resp = HTTPResponse(body="Foo bar baz") resp.headers = CIMultiDict() resp.headers['set-cookie'] = 'foo' resp.headers['set-cookie'] = 'bar' return resp
def test_existing_vary_headers(request): return HTTPResponse('', status=200, headers=CIMultiDict( {'Vary': 'Accept-Encoding'}))
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 = CIMultiDict() 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