mirror of
				https://github.com/django/daphne.git
				synced 2025-11-04 09:37:32 +03:00 
			
		
		
		
	Rename to plural "channels", start fleshing out req/resp cycle
This commit is contained in:
		
							parent
							
								
									821816f656
								
							
						
					
					
						commit
						6cd01e2bc1
					
				| 
						 | 
					@ -1,7 +0,0 @@
 | 
				
			||||||
from .consumer_registry import ConsumerRegistry
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Make a site-wide registry
 | 
					 | 
				
			||||||
coreg = ConsumerRegistry()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Load an implementation of Channel
 | 
					 | 
				
			||||||
from .channels.memory import Channel
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,18 +0,0 @@
 | 
				
			||||||
from django.core.handlers.base import BaseHandler
 | 
					 | 
				
			||||||
from channel import Channel
 | 
					 | 
				
			||||||
from .response import encode_response
 | 
					 | 
				
			||||||
from .request import decode_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class DjangoUrlAdapter(object):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Adapts the channel-style HTTP requests to the URL-router/handler style
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        self.handler = BaseHandler()
 | 
					 | 
				
			||||||
        self.handler.load_middleware()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __call__(self, request, response_channel):
 | 
					 | 
				
			||||||
        response = self.handler.get_response(decode_request(request))
 | 
					 | 
				
			||||||
        Channel(response_channel).send(**encode_response(response))
 | 
					 | 
				
			||||||
							
								
								
									
										11
									
								
								channels/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								channels/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					from .consumer_registry import ConsumerRegistry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Make a site-wide registry
 | 
				
			||||||
 | 
					coreg = ConsumerRegistry()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Load an implementation of Channel
 | 
				
			||||||
 | 
					from .backends import InMemoryChannel as Channel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ensure monkeypatching
 | 
				
			||||||
 | 
					from .hacks import monkeypatch_django
 | 
				
			||||||
 | 
					monkeypatch_django()
 | 
				
			||||||
							
								
								
									
										47
									
								
								channels/adapters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								channels/adapters.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,47 @@
 | 
				
			||||||
 | 
					from django.core.handlers.base import BaseHandler
 | 
				
			||||||
 | 
					from django.http import HttpRequest, HttpResponse
 | 
				
			||||||
 | 
					from channels import Channel, coreg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UrlConsumer(object):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Dispatches channel HTTP requests into django's URL system.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.handler = BaseHandler()
 | 
				
			||||||
 | 
					        self.handler.load_middleware()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, **kwargs):
 | 
				
			||||||
 | 
					        request = HttpRequest.channel_decode(kwargs)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            response = self.handler.get_response(request)
 | 
				
			||||||
 | 
					        except HttpResponse.ResponseLater:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        Channel(request.response_channel).send(**response.channel_encode())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def view_producer(channel_name):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Returns a new view function that actually writes the request to a channel
 | 
				
			||||||
 | 
					    and abandons the response (with an exception the Worker will catch)
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def producing_view(request):
 | 
				
			||||||
 | 
					        Channel(channel_name).send(**request.channel_encode())
 | 
				
			||||||
 | 
					        raise HttpResponse.ResponseLater()
 | 
				
			||||||
 | 
					    return producing_view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def view_consumer(channel_name):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Decorates a normal Django view to be a channel consumer.
 | 
				
			||||||
 | 
					    Does not run any middleware.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def inner(func): 
 | 
				
			||||||
 | 
					        def consumer(**kwargs):
 | 
				
			||||||
 | 
					            request = HttpRequest.channel_decode(kwargs)
 | 
				
			||||||
 | 
					            response = func(request)
 | 
				
			||||||
 | 
					            Channel(request.response_channel).send(**response.channel_encode())
 | 
				
			||||||
 | 
					        coreg.add_consumer(consumer, [channel_name])
 | 
				
			||||||
 | 
					        return func
 | 
				
			||||||
 | 
					    return inner
 | 
				
			||||||
							
								
								
									
										2
									
								
								channels/backends/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								channels/backends/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					from .base import BaseChannel
 | 
				
			||||||
 | 
					from .memory import InMemoryChannel
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
class Channel(object):
 | 
					class BaseChannel(object):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Base class for all channel layer implementations.
 | 
					    Base class for all channel layer implementations.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -51,3 +51,12 @@ class Channel(object):
 | 
				
			||||||
        response channels.
 | 
					        response channels.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        raise NotImplementedError()
 | 
					        raise NotImplementedError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def as_view(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns a view version of this channel - one that takes
 | 
				
			||||||
 | 
					        the request passed in and dispatches it to our channel,
 | 
				
			||||||
 | 
					        serialized.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        from channels.adapters import view_producer
 | 
				
			||||||
 | 
					        return view_producer(self.name)
 | 
				
			||||||
| 
						 | 
					@ -2,12 +2,12 @@ import time
 | 
				
			||||||
import string
 | 
					import string
 | 
				
			||||||
import random
 | 
					import random
 | 
				
			||||||
from collections import deque
 | 
					from collections import deque
 | 
				
			||||||
from .base import Channel as BaseChannel
 | 
					from .base import BaseChannel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
queues = {}
 | 
					queues = {}
 | 
				
			||||||
closed = set()
 | 
					closed = set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Channel(BaseChannel):
 | 
					class InMemoryChannel(BaseChannel):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    In-memory channel implementation. Intended only for use with threading,
 | 
					    In-memory channel implementation. Intended only for use with threading,
 | 
				
			||||||
    in low-throughput development environments.
 | 
					    in low-throughput development environments.
 | 
				
			||||||
							
								
								
									
										0
									
								
								channel/consumer_registry.py → channels/consumer_registry.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								channel/consumer_registry.py → channels/consumer_registry.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										23
									
								
								channels/docs/integration-changes.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								channels/docs/integration-changes.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					Message Standards
 | 
				
			||||||
 | 
					=================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Some standardised message formats are used for common message types - they
 | 
				
			||||||
 | 
					are detailed below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HTTP Request
 | 
				
			||||||
 | 
					------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Represents a full-fledged, single HTTP request coming in from a client.
 | 
				
			||||||
 | 
					Contains the following keys:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* request: An encoded Django HTTP request
 | 
				
			||||||
 | 
					* response_channel: The channel name to write responses to
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HTTP Response
 | 
				
			||||||
 | 
					-------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sends a whole response to a client.
 | 
				
			||||||
 | 
					Contains the following keys:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* response: An encoded Django HTTP response
 | 
				
			||||||
							
								
								
									
										23
									
								
								channels/docs/message-standards.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								channels/docs/message-standards.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					Message Standards
 | 
				
			||||||
 | 
					=================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Some standardised message formats are used for common message types - they
 | 
				
			||||||
 | 
					are detailed below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HTTP Request
 | 
				
			||||||
 | 
					------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Represents a full-fledged, single HTTP request coming in from a client.
 | 
				
			||||||
 | 
					Contains the following keys:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* request: An encoded Django HTTP request
 | 
				
			||||||
 | 
					* response_channel: The channel name to write responses to
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HTTP Response
 | 
				
			||||||
 | 
					-------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sends a whole response to a client.
 | 
				
			||||||
 | 
					Contains the following keys:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* response: An encoded Django HTTP response
 | 
				
			||||||
							
								
								
									
										27
									
								
								channels/hacks.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								channels/hacks.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					from django.http.request import HttpRequest
 | 
				
			||||||
 | 
					from django.http.response import HttpResponseBase
 | 
				
			||||||
 | 
					from django.core.handlers.base import BaseHandler
 | 
				
			||||||
 | 
					from .request import encode_request, decode_request
 | 
				
			||||||
 | 
					from .response import encode_response, decode_response, ResponseLater
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def monkeypatch_django():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Monkeypatches support for us into parts of Django.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # Request encode/decode
 | 
				
			||||||
 | 
					    HttpRequest.channel_encode = encode_request
 | 
				
			||||||
 | 
					    HttpRequest.channel_decode = staticmethod(decode_request)
 | 
				
			||||||
 | 
					    # Response encode/decode
 | 
				
			||||||
 | 
					    HttpResponseBase.channel_encode = encode_response
 | 
				
			||||||
 | 
					    HttpResponseBase.channel_decode = staticmethod(decode_response)
 | 
				
			||||||
 | 
					    HttpResponseBase.ResponseLater = ResponseLater
 | 
				
			||||||
 | 
					    # Allow ResponseLater to propagate above handler
 | 
				
			||||||
 | 
					    BaseHandler.old_handle_uncaught_exception = BaseHandler.handle_uncaught_exception
 | 
				
			||||||
 | 
					    BaseHandler.handle_uncaught_exception = new_handle_uncaught_exception
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def new_handle_uncaught_exception(self, request, resolver, exc_info):
 | 
				
			||||||
 | 
					    if exc_info[0] is ResponseLater:
 | 
				
			||||||
 | 
					        raise
 | 
				
			||||||
 | 
					    return BaseHandler.old_handle_uncaught_exception(self, request, resolver, exc_info)
 | 
				
			||||||
							
								
								
									
										24
									
								
								channel/management/commands/runinterfaceserver.py → channels/management/commands/runserver.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										24
									
								
								channel/management/commands/runinterfaceserver.py → channels/management/commands/runserver.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| 
						 | 
					@ -2,11 +2,11 @@ import django
 | 
				
			||||||
import threading
 | 
					import threading
 | 
				
			||||||
from django.core.management.commands.runserver import Command as RunserverCommand
 | 
					from django.core.management.commands.runserver import Command as RunserverCommand
 | 
				
			||||||
from django.core.handlers.wsgi import WSGIHandler
 | 
					from django.core.handlers.wsgi import WSGIHandler
 | 
				
			||||||
from channel import Channel, coreg
 | 
					from django.http import HttpResponse
 | 
				
			||||||
from channel.request import encode_request
 | 
					from channels import Channel, coreg
 | 
				
			||||||
from channel.response import decode_response
 | 
					from channels.worker import Worker
 | 
				
			||||||
from channel.worker import Worker
 | 
					from channels.utils import auto_import_consumers
 | 
				
			||||||
from channel.utils import auto_import_consumers
 | 
					from channels.adapters import UrlConsumer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(RunserverCommand):
 | 
					class Command(RunserverCommand):
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,8 @@ class Command(RunserverCommand):
 | 
				
			||||||
        # Check a handler is registered for http reqs
 | 
					        # Check a handler is registered for http reqs
 | 
				
			||||||
        auto_import_consumers()
 | 
					        auto_import_consumers()
 | 
				
			||||||
        if not coreg.consumer_for_channel("django.wsgi.request"):
 | 
					        if not coreg.consumer_for_channel("django.wsgi.request"):
 | 
				
			||||||
            raise RuntimeError("No consumer registered for WSGI requests")
 | 
					            # Register the default one
 | 
				
			||||||
 | 
					            coreg.add_consumer(UrlConsumer(), ["django.wsgi.request"])
 | 
				
			||||||
        # Launch a worker thread
 | 
					        # Launch a worker thread
 | 
				
			||||||
        worker = WorkerThread()
 | 
					        worker = WorkerThread()
 | 
				
			||||||
        worker.daemon = True
 | 
					        worker.daemon = True
 | 
				
			||||||
| 
						 | 
					@ -39,13 +40,10 @@ class WSGIInterfaceHandler(WSGIHandler):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_response(self, request):
 | 
					    def get_response(self, request):
 | 
				
			||||||
        response_channel = Channel.new_name("django.wsgi.response")
 | 
					        request.response_channel = Channel.new_name("django.wsgi.response")
 | 
				
			||||||
        Channel("django.wsgi.request").send(
 | 
					        Channel("django.wsgi.request").send(**request.channel_encode())
 | 
				
			||||||
            request = encode_request(request),
 | 
					        channel, message = Channel.receive_many([request.response_channel])
 | 
				
			||||||
            response_channel = response_channel,
 | 
					        return HttpResponse.channel_decode(message)
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        channel, message = Channel.receive_many([response_channel])
 | 
					 | 
				
			||||||
        return decode_response(message)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WorkerThread(threading.Thread):
 | 
					class WorkerThread(threading.Thread):
 | 
				
			||||||
							
								
								
									
										2
									
								
								channel/request.py → channels/request.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								channel/request.py → channels/request.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| 
						 | 
					@ -15,6 +15,7 @@ def encode_request(request):
 | 
				
			||||||
        "path": request.path,
 | 
					        "path": request.path,
 | 
				
			||||||
        "path_info": request.path_info,
 | 
					        "path_info": request.path_info,
 | 
				
			||||||
        "method": request.method,
 | 
					        "method": request.method,
 | 
				
			||||||
 | 
					        "response_channel": request.response_channel,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return value
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,4 +32,5 @@ def decode_request(value):
 | 
				
			||||||
    request.path = value['path']
 | 
					    request.path = value['path']
 | 
				
			||||||
    request.method = value['method']
 | 
					    request.method = value['method']
 | 
				
			||||||
    request.path_info = value['path_info']
 | 
					    request.path_info = value['path_info']
 | 
				
			||||||
 | 
					    request.response_channel = value['response_channel']
 | 
				
			||||||
    return request
 | 
					    return request
 | 
				
			||||||
							
								
								
									
										9
									
								
								channel/response.py → channels/response.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										9
									
								
								channel/response.py → channels/response.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| 
						 | 
					@ -27,3 +27,12 @@ def decode_response(value):
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    response._headers = {k.lower: (k, v) for k, v in value['headers']}
 | 
					    response._headers = {k.lower: (k, v) for k, v in value['headers']}
 | 
				
			||||||
    return response
 | 
					    return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ResponseLater(Exception):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Class that represents a response which will be sent doown the response
 | 
				
			||||||
 | 
					    channel later. Used to move a django view-based segment onto the next
 | 
				
			||||||
 | 
					    task, as otherwise we'd need to write some kind of fake response.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
							
								
								
									
										2
									
								
								channel/utils.py → channels/utils.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								channel/utils.py → channels/utils.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| 
						 | 
					@ -10,5 +10,5 @@ def auto_import_consumers():
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            __import__(consumer_module_name)
 | 
					            __import__(consumer_module_name)
 | 
				
			||||||
        except ImportError as e:
 | 
					        except ImportError as e:
 | 
				
			||||||
            if "no module named" not in str(e).lower():
 | 
					            if "no module named consumers" not in str(e).lower():
 | 
				
			||||||
                raise
 | 
					                raise
 | 
				
			||||||
							
								
								
									
										0
									
								
								channel/worker.py → channels/worker.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								channel/worker.py → channels/worker.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user