示例#1
0
    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()
示例#2
0
    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
示例#3
0
	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']

		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 = {}
		extra_headers['Cache-Control'] = 'no-cache'
		extra_headers['X-Accel-Buffering'] = 'no'
		augment_cors_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))
示例#4
0
	async def handle(self, body):
		from .eventrequest import EventRequest
		from .eventstream import EventPermissionError
		from .utils import sse_encode_event, sse_error_response, make_id

		self.listener = None

		request = AsgiRequest(self.scope, body)

		# TODO use GripMiddleware
		request.grip_proxied = False
		for name, value in self.scope['headers']:
			if name == b'grip-sig':
				request.grip_proxied = True
				break

		if 'user' in self.scope:
			request.user = self.scope['user']

		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))

		user = None
		if hasattr(request, 'user') and request.user.is_authenticated:
			user = request.user

		# for grip requests, prepare immediate response
		if not response and request.grip_proxied:
			try:
				event_response = await self.get_events(event_request, user)
				response = event_response.to_http_response(request)
			except EventPermissionError as e:
				response = sse_error_response(
					'forbidden',
					str(e),
					{'channels': e.channels})

		extra_headers = {}
		extra_headers['Cache-Control'] = 'no-cache'

		cors_origin = ''
		if hasattr(settings, 'EVENTSTREAM_ALLOW_ORIGIN'):
			cors_origin = settings.EVENTSTREAM_ALLOW_ORIGIN

		if cors_origin:
			extra_headers['Access-Control-Allow-Origin'] = cors_origin

		# if this was a grip request or we encountered an error, respond now
		if response:
			headers = []
			for name, value in response.items():
				headers.append((name, value))

			for name, value in extra_headers.items():
				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 = [('Content-Type', 'text/event-stream')]
		for name, value in extra_headers.items():
			headers.append((name, value))

		await self.send_headers(headers=headers)

		body = b':' + (b' ' * 2048) + b'\n\n'
		body += b'event: stream-open\ndata:\n\n'
		await self.send_body(body, more_body=True)

		self.listener = Listener()
		self.is_streaming = True

		asyncio.get_event_loop().create_task(self.stream(event_request, user))