def get_scope_user(scope): if "_cached_user" not in scope: # We need to fake a request so the auth code works scope['method'] = "FAKE" from channels.http import AsgiRequest fake_request = AsgiRequest(scope, b'') fake_request.session = scope["session"] scope["_cached_user"] = _get_user(fake_request) return scope["_cached_user"]
def connect(self): self.service = self.scope["service"] scope = dict(self.scope) scope["method"] = "get" request = AsgiRequest(scope, b"") request._request = request request.user = self.scope["user"] request.session = self.scope["session"] if not self.scope["user"].is_authenticated: self.authenticate(request) if self.check_permissions(request): raise AcceptConnection() else: raise DenyConnection()
def from_scope(cls, viewset_action, scope, view_kwargs, query_params): """ "This is the magic." (reference: https://github.com/encode/django-rest-framework/blob/1e383f/rest_framework/viewsets.py#L47) This method initializes a view properly so that calls to methods like get_queryset() and get_serializer_class(), and permission checks have all the properties set, like self.kwargs and self.request, that they would expect. The production of a Django HttpRequest object from a base websocket asgi scope, rather than an actual HTTP request, is probably the largest "hack" in this project. By inspection of the ASGI spec, however, the only difference between websocket and HTTP scopes is the existence of an HTTP method (https://asgi.readthedocs.io/en/latest/specs/www.html). This is because websocket connections are established over an HTTP connection, and so headers and everything else are set just as they would be in a normal HTTP request. Therefore, the base of the request object for every broadcast is the initial HTTP request. Subscriptions are retrieval operations, so the method is hard-coded as GET. """ self = cls() self.format_kwarg = None self.action_map = dict() self.args = [] self.kwargs = view_kwargs base_request = AsgiRequest( { **scope, "method": "GET", "query_string": urlencode(query_params) }, BytesIO(), ) # TODO: Run other middleware? base_request.user = scope.get("user", None) base_request.session = scope.get("session", None) self.request = self.initialize_request(base_request) self.action = viewset_action # TODO: custom subscription actions? return self
async def handle(self, body): from django_grip import GripMiddleware from .eventrequest import EventRequest from .eventstream import EventPermissionError from .utils import sse_error_response self.listener = None request = AsgiRequest(self.scope, body) gm = GripMiddleware() gm.process_request(request) if 'user' in self.scope: request.user = self.scope['user'] if 'session' in self.scope: request.session = self.scope['session'] try: event_request = await self.parse_request(request) response = None except EventRequest.ResumeNotAllowedError as e: response = HttpResponseBadRequest('Invalid request: %s.\n' % str(e)) except EventRequest.GripError as e: if request.grip.proxied: response = sse_error_response('internal-error', 'Invalid internal request.') else: response = sse_error_response('bad-request', 'Invalid request: %s.' % str(e)) except EventRequest.Error as e: response = sse_error_response('bad-request', 'Invalid request: %s.' % str(e)) # for grip requests, prepare immediate response if not response and request.grip.proxied: try: event_response = await self.get_events(event_request) response = event_response.to_http_response(request) except EventPermissionError as e: response = sse_error_response('forbidden', str(e), {'channels': e.channels}) extra_headers = {} add_default_headers(extra_headers) # if this was a grip request or we encountered an error, respond now if response: response = gm.process_response(request, response) headers = [] for name, value in response.items(): if isinstance(name, six.text_type): name = name.encode('utf-8') if isinstance(value, six.text_type): value = value.encode('utf-8') headers.append((name, value)) for name, value in extra_headers.items(): if isinstance(name, six.text_type): name = name.encode('utf-8') if isinstance(value, six.text_type): value = value.encode('utf-8') headers.append((name, value)) await self.send_response(response.status_code, response.content, headers=headers) return # if we got here then the request was not a grip request, and there # were no errors, so we can begin a local stream response headers = [(six.b('Content-Type'), six.b('text/event-stream'))] for name, value in extra_headers.items(): if isinstance(name, six.text_type): name = name.encode('utf-8') if isinstance(value, six.text_type): value = value.encode('utf-8') headers.append((name, value)) await self.send_headers(headers=headers) self.listener = Listener() self.is_streaming = True asyncio.get_event_loop().create_task(self.stream(event_request))