
    `fl                     4   d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	Z	d dl
Z
d dlm Z d dlmZ d dlmZ ddlmZmZmZmZmZ  ej        e          Z G d d          Z G d	 d
e j                  Z G d d          Z G d de          ZdS )    N)asyncio)ChannelFull)BaseChannelLayer   )_close_redis_consistent_hash_wrap_closecreate_pooldecode_hostsc                   *    e Zd ZdZd Zd Zd Zd ZdS )ChannelLockz
    Helper class for per-channel locking.

    Once a lock is released and has no waiters, it will also be deleted,
    to mitigate multi-event loop problems.
    c                     t          j        t          j                  | _        t          j        t
                    | _        d S N)collectionsdefaultdictr   Locklocksintwait_countsselfs    E/var/www/html/env/lib/python3.11/site-packages/channels_redis/core.py__init__zChannelLock.__init__%   s/     ,W\::
&2377    c                 z   K   | j         |xx         dz  cc<   | j        |                                          d{V S )z9
        Acquire the lock for the given channel.
        r   N)r   r   acquirer   channels     r   r   zChannelLock.acquire)   sR       	!!!Q&!!!Z(00222222222r   c                 @    | j         |                                         S )zP
        Return ``True`` if the lock for the given channel is acquired.
        )r   lockedr   s     r   r    zChannelLock.locked0   s     z'"))+++r   c                     | j         |                                          | j        |xx         dz  cc<   | j        |         dk     r| j         |= | j        |= dS dS )z9
        Release the lock for the given channel.
        r   N)r   releaser   r   s     r   r"   zChannelLock.release6   sr     	
7##%%%!!!Q&!!!G$q((
7# ))) )(r   N)__name__
__module____qualname____doc__r   r   r    r"    r   r   r   r      sZ         8 8 83 3 3, , ,* * * * *r   r   c                        e Zd Z fdZ xZS )BoundedQueuec                     |                                  r|                                  t          t          |                               |          S r   )full
get_nowaitsuperr)   
put_nowait)r   item	__class__s     r   r.   zBoundedQueue.put_nowaitB   sC    99;; 	 OO\4((33D999r   )r#   r$   r%   r.   __classcell__)r0   s   @r   r)   r)   A   s8        	: 	: 	: 	: 	: 	: 	: 	: 	:r   r)   c                        e Zd Zd Zd Zd ZdS )RedisLoopLayerc                 R    t          j                    | _        || _        i | _        d S r   )r   r   _lockchannel_layer_connections)r   r6   s     r   r   zRedisLoopLayer.__init__O   s%    \^^
*r   c                     || j         vr7| j                            |          }t          j        |          | j         |<   | j         |         S )N)connection_pool)r7   r6   r
   aioredisRedis)r   indexpools      r   get_connectionzRedisLoopLayer.get_connectionT   sN    )))%11%88D'/~d'K'K'KDe$ ''r   c                    K   | j         4 d {V  t          | j                  D ]1}| j                            |          }t	          |           d {V  2	 d d d           d {V  d S # 1 d {V swxY w Y   d S r   )r5   listr7   popr   )r   r<   
connections      r   flushzRedisLoopLayer.flush[   s$     : 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/d/00 / /!.22599
":........../	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/s   AA++
A58A5N)r#   r$   r%   r   r>   rC   r'   r   r   r3   r3   N   sA          
( ( (/ / / / /r   r3   c                       e Zd ZdZdZ	 	 	 	 	 	 	 d#dZd	 Zd
 ZddgZd Z	d Z
d Zd Zd Zd Zd$dZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd  Zd! Zd" ZdS )%RedisChannelLayerz
    Redis channel layer.

    It routes all messages into remote Redis server. Support for
    sharding among different Redis installations and message
    encryption are provided.
       Nasgi<   Q d   c                 4   || _         || _        || _        |                     |pi           | _        || _        t          | j        t                    s
J d            t          |          | _	        t          | j	                  | _        i | _        t          j        t          t          | j	                                      | _        t          j        t          t          | j	                                      | _        t%          j                    j        | _        |                     |           d| _        d | _        d | _        t5          j        t9          j        t<          | j                            | _        g | _         tC                      | _"        d S )NzPrefix must be unicoder   )#expirygroup_expirycapacitycompile_capacitieschannel_capacityprefix
isinstancestrr   hostslen	ring_size_layers	itertoolscyclerange_receive_index_generator_send_index_generatoruuiduuid4hexclient_prefix_setup_encryptionreceive_countreceive_lockreceive_event_loopr   r   	functoolspartialr)   receive_bufferreceive_cleanersr   receive_clean_locks)r   rT   rQ   rL   rM   rN   rP   symmetric_encryption_keyss           r   r   zRedisChannelLayer.__init__m   sJ    (  $ 7 78H8NB O O$+s++EE-EEE+!%((
TZ(1c$*oo8N8N(O(O%%._U3tz??5K5K%L%L"!Z\\-8999 "&)5lDM::
 
 !# $/==   r   c                 6    t          | j        |                   S r   )r
   rT   )r   r<   s     r   r
   zRedisChannelLayer.create_pool   s    4:e,---r   c                      |rpt          |t          t          f          rt          d          	 ddlm} n# t          $ r t          d          w xY w fd|D             } ||           _        d S d  _        d S )Nz9symmetric_encryption_keys must be a list of possible keysr   )MultiFernetz<Cannot run with encryption without 'cryptography' installed.c                 :    g | ]}                     |          S r'   )make_fernet).0keyr   s     r   
<listcomp>z7RedisChannelLayer._setup_encryption.<locals>.<listcomp>   s'    VVVS4++C00VVVr   )rR   rS   bytes
ValueErrorcryptography.fernetrm   ImportErrorcrypter)r   rj   rm   sub_fernetss   `   r   ra   z#RedisChannelLayer._setup_encryption   s    $ 	 3c5\BB  O  ;;;;;;;    R   WVVV<UVVVK&;{33DLLLDLLLs	   7 AgroupsrC   c                   K   t          |t                    s
J d            |                     |          s
J d            d|vsJ |}d|v r;t          |                                          }||d<   |                     |          }| j        |z   }d|v r|                     |          }nt          | j                  }| 	                    |          }|
                    |dt          t          j                              t          | j                  z
             d{V  |                    |dd	           d{V |                     |          k    rt!                      |                    ||                     |          t          j                    i           d{V  |                    |t          | j                             d{V  dS )
zF
        Send a message onto a (general or specific) channel.
        zmessage is not a dictChannel name not valid__asgi_channel__!r   minmaxNz-infz+inf)rR   dictvalid_channel_nameitemsnon_local_namerQ   consistent_hashnextr\   rB   zremrangebyscorer   timerL   zcountget_capacityr   zadd	serializeexpire)r   r   messagechannel_non_local_namechannel_keyr<   rB   s          r   sendzRedisChannelLayer.send   s     
 '4((AA*AAA(&&w//II1III/!0000!('>>7==??++G*1G&'%)%8%8%A%A"k$:: '>>((11EE344E__U++
))QC	$4$4s4;7G7G$G * 
 
 	
 	
 	
 	
 	
 	
 	
 "";????????4CTCTD
 D
 
 
 -- ookDNN7,C,CTY[[+QRRRRRRRRRS-=-=>>>>>>>>>>>r   c                     |dz   S )zQ
        Construct the key used as a backup queue for the given channel.
        z	$inflightr'   r   s     r   _backup_channel_namez&RedisChannelLayer._backup_channel_name   s     $$r   c                 D  K   d}|                      |          }|                     |          }|                    |d||           d{V  |                    ||           d{V }|2|\  }}	}
|                    ||	t          |
          i           d{V  nd}	|	S )z
        Perform a Redis BRPOP and manage the backup processing queue.
        In case of cancellation, make sure the message is not lost.
        a  
            local backed_up = redis.call('ZRANGE', ARGV[2], 0, -1, 'WITHSCORES')
            for i = #backed_up, 1, -2 do
                redis.call('ZADD', ARGV[1], backed_up[i], backed_up[i - 1])
            end
            redis.call('DEL', ARGV[2])
        r   Ntimeout)r   rB   evalbzpopminr   float)r   r<   r   r   cleanup_scriptbackup_queuerB   result_member	timestamps              r   _brpop_with_cleanz#RedisChannelLayer._brpop_with_clean   s       0099__U++
 oona,GGGGGGGGG!**7G*DDDDDDDD#) Avy//,y9I9I0JKKKKKKKKKKFr   c                    K   |                      |          }|                    |                     |                     d{V  dS )z
        Pop the oldest message off the channel backup queue.
        The result isn't interesting as it was already processed.
        N)rB   zpopminr   )r   r<   r   rB   s       r   _clean_receive_backupz'RedisChannelLayer._clean_receive_backup   sS      
 __U++
  !:!:7!C!CDDDDDDDDDDDr   c                   K   |                      |          sJ d|v r|                     |          }|                    | j        dz             s
J d            t	          j                    }| xj        dz  c_        	 | j        dk    r t	          j                    | _        || _	        n| j	        |k    rt          d          d}| j        |                                         rq| j                                        | j        |                                         g}d |D             }	 t	          j        |t          j                   d{V \  }}|D ]}|                                 nz# t          j        $ rh | j        |= |D ][}|                                sE|                                sJ |                                du r| j                                         \ w xY wdx}x}	}
|D ]Q}	 |                                }n# t,          $ r}|}
Y d}~)d}~ww xY w|du r|}	8t/          |t0                    sJ |}R|s|
r |	r| j                                         |
r|
n|	sJ 	 |                     |           d{V \  }}t/          |t4                    r&|D ]"}| j        |                             |           #n | j        |                             |           d}n# t8          $ r
 | j        |=  w xY w	 | j                                         n# | j                                         w xY w| j        |                                         q|| j        |                                         }| j        |                                         r| j        |= || xj        dz  c_        | j        d	k    r*| j                                        rJ d| _        d| _	        S S # | xj        dz  c_        | j        d	k    r)| j                                        rJ d| _        d| _	        w xY w|                     |           d{V d         S )
z
        Receive the first message that arrives on the channel.
        If more than one coroutine waits on the same channel, the first waiter
        will be given the message when it arrives.
        r}   zWrong client prefixr   zETwo event loops are trying to receive() on one channel layer at once!Nc                 6    g | ]}t          j        |          S r'   )r   ensure_future)rp   tasks     r   rr   z-RedisChannelLayer.receive.<locals>.<listcomp>,  s#    KKKTW2488KKKr   )return_whenTr   )r   r   endswithr`   r   get_running_looprb   r   rc   rd   RuntimeErrorrg   emptyr   getwaitFIRST_COMPLETEDcancelCancelledErrordoner   r"   BaseExceptionrR   r   receive_singler@   r.   	Exceptionr,   r    )r   r   real_channelloopr   tasksr   pendingr   token	exceptionr   errormessage_channelchans                  r   receivezRedisChannelLayer.receive  s      &&w/////'>>..w77L(("S(  % %$% %  +--D!#f3%**(/D%.2D++ .$66*c  
 )'288:: G8)1133+G488::E LKUKKKE.5l!w/F/ / / ) ) ) ) ) )g %, * *D KKMMMM* #1 
 
 
 !/8$) @ @D#';;== @'+yy{{ 2 2{#';;==D#8#8$($5$=$=$?$?$?
 376G6ei $ - -%%)[[]]FF, % % % ).I$HHHH	% "T>>$*EE#-fd#;#;;;#;&,GG 8) 8  8 -55777$ ""+O!$u8 >B=P=P ,> > 8 8 8 8 8 84OW  */4@@ Y,; !R !RD$($7$=$H$H$Q$Q$Q$Q!R !% 3O D O OPW X X X&*GG( " " " $ 3G <!" $
 !-557777D-557777O )'288:: G8V ?"1':EEGGG&w/5577 5+G4 ""a'""%**#07799999(,D%.2D++ + ""a'""%**#07799999(,D%.2D+2222 --g66666666::s   ?B+O& +AE. -O& .A7G%%O& 3HO& 
HHO& HAO& +A;K' &L 'K;;L ?O& L44A,O& &AP,c                    K                         |d          s
J d            d|v r-|                    d          sJ                      |          }nt           j                  } j        |z   d} j                                       d{V  	 |%                     | j	                   d{V }|%t          j                             |                    } j                            |            fd}|                    |           n)# t           $ r  j                                        w xY w                     |          }d|v r|d         }|d= ||fS )	zN
        Receives a single message off of the channel and returns it.
        T)r   zChannel name invalidr}   Nr   c                 p    j                             |            j                                       d S r   )rh   removeri   r"   )cleanerr   r   s    r   _cleanup_donez7RedisChannelLayer.receive_single.<locals>._cleanup_done  s7    %,,W555(00=====r   r|   )r   r   r   r   r[   rQ   ri   r   r   brpop_timeoutr   r   r   rh   appendadd_done_callbackr   r"   deserialize)r   r   r<   contentr   r   r   r   s   `      @r   r   z RedisChannelLayer.receive_single  s     
 &&w&==UU?UUU='>>##C(((((((11EE677EkG+&..{;;;;;;;;;	/ !% 6 6;0B !7 ! !       / +**5+>> G !((111> > > > > > %%m4444 	 	 	$,,[999	
 ""7++ ((01G*+s   BD &Especificc                 P   K   | d| j          dt          j                    j         S )zx
        Returns a new channel name that can be used by something in our
        process as a specific channel.
        .r}   )r`   r]   r^   r_   )r   rQ   s     r   new_channelzRedisChannelLayer.new_channel  s0      
 BB4-BB
0@BBBr   c                   K   |                                   d{V  d}t          | j                  D ]<}|                     |          }|                    |d| j        dz              d{V  =|                                  d{V  dS )z@
        Deletes all messages and groups on all shards.
        Nz
            local keys = redis.call('keys', ARGV[1])
            for i=1,#keys,5000 do
                redis.call('del', unpack(keys, i, math.min(i+4999, #keys)))
            end
        r   *)wait_receivedrZ   rV   rB   r   rQ   close_pools)r   delete_prefixirB   s       r   rC   zRedisChannelLayer.flush  s         """"""""" t~&& 	G 	GA++J//-DK#4EFFFFFFFFFF           r   c                    K   |                                   d{V  | j                                        D ]}|                                 d{V  dS )z@
        Close all connections in the event loop pools.
        N)r   rW   valuesrC   )r   layers     r   r   zRedisChannelLayer.close_pools  sv         """""""""\((** 	  	 E++--	  	 r   c                 j   K   | j         r)t          j        | j         dd                    d{V  dS dS )zC
        Wait for all channel cleanup functions to finish.
        N)rh   r   r   r   s    r   r   zRedisChannelLayer.wait_received  sQ         	9,t4QQQ788888888888	9 	9r   c                   K   |                      |          s
J d            |                     |          s
J d            |                     |          }|                     |                     |                    }|                    ||t          j                    i           d{V  |                    || j                   d{V  dS )z3
        Adds the channel name to a group.
        Group name not validr{   N)	valid_group_namer   
_group_keyrB   r   r   r   r   rM   )r   groupr   	group_keyrB   s        r   	group_addzRedisChannelLayer.group_add  s      
 $$U++CC-CCC+&&w//II1III/OOE**	__T%9%9%%@%@AA
ooi'49;;)?@@@@@@@@@ 	4+<===========r   c                 8  K   |                      |          s
J d            |                     |          s
J d            |                     |          }|                     |                     |                    }|                    ||           d{V  dS )z
        Removes the channel from the named group if it is in the group;
        does nothing otherwise (does not error)
        r   r{   N)r   r   r   rB   r   zrem)r   r   r   rq   rB   s        r   group_discardzRedisChannelLayer.group_discard  s      
 $$U++CC-CCC+&&w//II1III/ooe$$__T%9%9%%@%@AA
ooc7+++++++++++r   c           
      R  K   |                      |          s
J d            |                     |          }|                     |                     |                    }|                    |dt          t          j                              | j        z
             d{V  d |                    |dd           d{V D             }| 	                    ||          \  }|
                                D ]+\  }}|                                }	|D ]M}|	                    |dt          t          j                              t          | j                  z
             N|	                                 d{V  d}
fd|D             }|fd	|D             z  }|t          j                    | j        gz  }|                     |          } |j        |
t          |          g||R   d{V }|dk    r*t                               d
|t          |          |           -dS )z6
        Sends a message to the entire group.
        r   r   r~   Nc                 8    g | ]}|                     d           S )utf8)decode)rp   xs     r   rr   z0RedisChannelLayer.group_send.<locals>.<listcomp>  s$    WWWa&))WWWr   aI  
                local over_capacity = 0
                local current_time = ARGV[#ARGV - 1]
                local expiry = ARGV[#ARGV]
                for i=1,#KEYS do
                    if redis.call('ZCOUNT', KEYS[i], '-inf', '+inf') < tonumber(ARGV[i + #KEYS]) then
                        redis.call('ZADD', KEYS[i], current_time, ARGV[i])
                        redis.call('EXPIRE', KEYS[i], expiry)
                    else
                        over_capacity = over_capacity + 1
                    end
                end
                return over_capacity
            c                      g | ]
}|         S r'   r'   )rp   r   channel_keys_to_messages     r   rr   z0RedisChannelLayer.group_send.<locals>.<listcomp>;  s.        (4  r   c                      g | ]
}|         S r'   r'   )rp   r   channel_keys_to_capacitys     r   rr   z0RedisChannelLayer.group_send.<locals>.<listcomp>A  s.        )5  r   z+%s of %s channels over capacity in group %s)r   r   rB   r   r   r   r   rM   zrange_map_channel_keys_to_connectionr   pipelinerL   executer   rU   loggerinfo)r   r   r   rq   rB   channel_namesconnection_to_channel_keysconnection_indexchannel_redis_keyspipegroup_send_luaargschannels_over_capacityr   r   s                @@r   
group_sendzRedisChannelLayer.group_send  s      $$U++CC-CCC+ooe$$__T%9%9%%@%@AA
))QC	,,t/@@ * 
 
 	
 	
 	
 	
 	
 	
 	
 XW9J9J3PQSU9V9V3V3V3V3V3V3VWWW 00HH		
&#$ 5O4T4T4V4V 6	 6	00&&((D)  %%QC	$4$4s4;7G7G$G &     ,,..       N    #5  D     #5   D
 TY[[$+..D )9::J+::?$6 7 7,:L,OS, , , & & & & & &" &))A*&&	  c6	 6	r   c                 b   t          j        t                    }t                      }t                      }|D ]}|}d|v r|                     |          }| j        |z   }||vrut          |                                          }|g|d<   |||<   |                     |          ||<   |                     |          }	||	         	                    |           ||         d         	                    |           |                                D ]\  }
}| 
                    |          ||
<   |||fS )a  
        For a list of channel names, GET

        1. list of their redis keys bucket each one to a dict keyed by the connection index

        2. for each unique channel redis key create a serialized message specific to that redis key, by adding
           the list of channels mapped to that redis key in __asgi_channel__ key to the message

        3. returns a mapping of redis channels keys to their capacity
        r}   r|   )r   r   r@   r   r   rQ   r   r   r   r   r   )r   r   r   r   channel_key_to_messagechannel_key_to_capacityr   r   r   idxrq   values               r   r   z1RedisChannelLayer._map_channel_keys_to_connectionU  sd    &1%<T%B%B"!%"&&& % 	X 	XG%,"g~~)-)<)<W)E)E&+(>>K"888w}}///6i*+6=&{37;7H7H7Q7Q'4**+ABB*3/66{CCCC '{34FGNNwWWWW 16688 	@ 	@JC*...*?*?"3'' '"#
 	
r   c                 @    | j          d|                     d          S )zH
        Common function to make the storage key for the group.
        z:group:r   )rQ   encode)r   r   s     r   r   zRedisChannelLayer._group_key  s(     +--e--44V<<<r   c                     t          j        |d          }| j        r| j                            |          }t	          j        d                              dd          }||z   S )z6
        Serializes message to a byte string.
        T)use_bin_type`      big)msgpackpackbrw   encryptrandomgetrandbitsto_bytes)r   r   r   random_prefixs       r   r   zRedisChannelLayer.serialize  sd     gD999< 	0L((//E *622;;BFFu$$r   c                     |dd         }| j         r#| j                             || j        dz             }t          j        |d          S )z2
        Deserializes from a byte string.
        r  N
   F)raw)rw   decryptrL   r  unpackb)r   r   s     r   r   zRedisChannelLayer.deserialize  sO    
 "##,< 	Fl**7DK"4DEEGwE2222r   c                 ,    t          || j                  S r   )r   rV   )r   r   s     r   r   z!RedisChannelLayer.consistent_hash  s    t~666r   c                     ddl m} t          |t                    r|                    d          }t          j        t          j        |          	                                          } ||          S )zT
        Given a single encryption key, returns a Fernet instance using it.
        r   )Fernetr   )
ru   r  rR   rS   r  base64urlsafe_b64encodehashlibsha256digest)r   rq   r  formatted_keys       r   ro   zRedisChannelLayer.make_fernet  so     	/.....c3 	%**V$$C01D1D1K1K1M1MNNvm$$$r   c                 0    | j         j         d| j         dS )Nz(hosts=))r0   r#   rT   r   s    r   __str__zRedisChannelLayer.__str__  s     .)??$*????r   c                 @   d|cxk    r| j         k     sn t          d| j          d| d          t          j                    }	 | j        |         }n9# t
          $ r, t          | |           t          |           x}| j        |<   Y nw xY w|                    |          S )zh
        Returns the correct connection for the index given.
        Lazily instantiates pools.
        r   zThere are only z hosts - you asked for r}   )	rV   rt   r   r   rW   KeyErrorr	   r3   r>   )r   r<   r   r   s       r   rB   zRedisChannelLayer.connection  s     E****DN****Q$.QQQQQ   '))	>L&EE 	> 	> 	>d###)7)=)==EDL&&&	> ##E***s   A 3BB)NrG   rH   rI   rJ   NN)r   )r#   r$   r%   r&   r   r   r
   ra   
extensionsr   r   r   r   r   r   r   rC   r   r   r   r   r   r   r   r   r   r   ro   r  rB   r'   r   r   rE   rE   b   s         M "&+1 +1 +1 +1Z. . .     ( G$J&? &? &?P% % %  >E E Ez; z; z;x5  5  5 nC C C C! ! !,     9 9 9> > > 	, 	, 	,K K KZ0
 0
 0
d= = =
% 
% 
%	3 	3 	37 7 7	% 	% 	%@ @ @
+ + + + +r   rE   )r   r  r   re   r  rX   loggingr
  r   r]   r  redisr:   channels.exceptionsr   channels.layersr   utilsr   r   r	   r
   r   	getLoggerr#   r   r   Queuer)   r3   rE   r'   r   r   <module>r)     s                       % % % % % % + + + + + + , , , , , ,              
	8	$	$!* !* !* !* !* !* !* !*H
: 
: 
: 
: 
:7= 
: 
: 
:/ / / / / / / /(k	+ k	+ k	+ k	+ k	+( k	+ k	+ k	+ k	+ k	+r   