mirror of
				https://github.com/django/daphne.git
				synced 2025-11-04 01:27:33 +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.
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -51,3 +51,12 @@ class Channel(object):
 | 
			
		|||
        response channels.
 | 
			
		||||
        """
 | 
			
		||||
        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 random
 | 
			
		||||
from collections import deque
 | 
			
		||||
from .base import Channel as BaseChannel
 | 
			
		||||
from .base import BaseChannel
 | 
			
		||||
 | 
			
		||||
queues = {}
 | 
			
		||||
closed = set()
 | 
			
		||||
 | 
			
		||||
class Channel(BaseChannel):
 | 
			
		||||
class InMemoryChannel(BaseChannel):
 | 
			
		||||
    """
 | 
			
		||||
    In-memory channel implementation. Intended only for use with threading,
 | 
			
		||||
    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
 | 
			
		||||
from django.core.management.commands.runserver import Command as RunserverCommand
 | 
			
		||||
from django.core.handlers.wsgi import WSGIHandler
 | 
			
		||||
from channel import Channel, coreg
 | 
			
		||||
from channel.request import encode_request
 | 
			
		||||
from channel.response import decode_response
 | 
			
		||||
from channel.worker import Worker
 | 
			
		||||
from channel.utils import auto_import_consumers
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from channels import Channel, coreg
 | 
			
		||||
from channels.worker import Worker
 | 
			
		||||
from channels.utils import auto_import_consumers
 | 
			
		||||
from channels.adapters import UrlConsumer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command(RunserverCommand):
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,8 @@ class Command(RunserverCommand):
 | 
			
		|||
        # Check a handler is registered for http reqs
 | 
			
		||||
        auto_import_consumers()
 | 
			
		||||
        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
 | 
			
		||||
        worker = WorkerThread()
 | 
			
		||||
        worker.daemon = True
 | 
			
		||||
| 
						 | 
				
			
			@ -39,13 +40,10 @@ class WSGIInterfaceHandler(WSGIHandler):
 | 
			
		|||
    """
 | 
			
		||||
 | 
			
		||||
    def get_response(self, request):
 | 
			
		||||
        response_channel = Channel.new_name("django.wsgi.response")
 | 
			
		||||
        Channel("django.wsgi.request").send(
 | 
			
		||||
            request = encode_request(request),
 | 
			
		||||
            response_channel = response_channel,
 | 
			
		||||
        )
 | 
			
		||||
        channel, message = Channel.receive_many([response_channel])
 | 
			
		||||
        return decode_response(message)
 | 
			
		||||
        request.response_channel = Channel.new_name("django.wsgi.response")
 | 
			
		||||
        Channel("django.wsgi.request").send(**request.channel_encode())
 | 
			
		||||
        channel, message = Channel.receive_many([request.response_channel])
 | 
			
		||||
        return HttpResponse.channel_decode(message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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_info": request.path_info,
 | 
			
		||||
        "method": request.method,
 | 
			
		||||
        "response_channel": request.response_channel,
 | 
			
		||||
    }
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,4 +32,5 @@ def decode_request(value):
 | 
			
		|||
    request.path = value['path']
 | 
			
		||||
    request.method = value['method']
 | 
			
		||||
    request.path_info = value['path_info']
 | 
			
		||||
    request.response_channel = value['response_channel']
 | 
			
		||||
    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']}
 | 
			
		||||
    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:
 | 
			
		||||
            __import__(consumer_module_name)
 | 
			
		||||
        except ImportError as e:
 | 
			
		||||
            if "no module named" not in str(e).lower():
 | 
			
		||||
            if "no module named consumers" not in str(e).lower():
 | 
			
		||||
                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