o
    b2                     @   s   d Z ddlZddlZddlZzddlZW n ey   dZY nw ddlmZ ddlm	Z	m
Z
 ddlmZmZ ddlmZmZ ddlmZmZmZmZmZmZmZmZmZmZmZ ddlmZ dd	lm Z  dd
l!m"Z" ee	j#$e
 dG dd de"Z%ee	j#$e
 dG dd de"Z&dS )z
Whitebox tests for TCP APIs.
    N)skipIf)
interfacesreactor)gatherResultsmaybeDeferred)ProtocolServerFactory)_ACCEPT_ERRORSEAGAINECONNABORTEDEINPROGRESSEMFILEENFILEENOBUFSENOMEMEPERMEWOULDBLOCKPort)log)platform)TestCasez?This test only applies to reactors that implement IReactorFDsetc                   @   sF   e Zd ZdZdZdd Zdd Zdd Zee	
 d	kd
dd ZdS )PlatformAssumptionsTestsz4
    Test assumptions about platform behaviors.
    i    c                 C   s`   g | _ td ur.ddlm} t| d }ttj| _ttj|| jd f |d | _	d S d S )Nr   )_listOpenFDs      d   )
openSocketsresourcetwisted.internet.processr   len	getrlimitRLIMIT_NOFILEoriginalFileLimit	setrlimitsocketLimit)selfr   newLimit r'   A/usr/lib/python3/dist-packages/twisted/test/test_tcp_internals.pysetUp4   s   zPlatformAssumptionsTests.setUpc                 C   s\   | j r| j    | j std ur,ttjd }t| jd |}ttj||f d S d S )Nr   r   )	r   popcloser   r    r!   minr"   r#   )r%   currentHardLimitnewSoftLimitr'   r'   r(   tearDownF   s   z!PlatformAssumptionsTests.tearDownc                 C   s   t   }| j| |S )z|
        Create and return a new socket object, also tracking it so it can be
        closed in the test tear down.
        )socketr   append)r%   sr'   r'   r(   r0   R   s   zPlatformAssumptionsTests.socketwin32zhWindows requires an unacceptably large amount of resources to provoke this behavior in the naive manner.c                 C   s   |   }|d | d }|d |   }|d t| jD ]%}z|    W q" tyG } z|jd t	t
fv rBW Y d}~ n d}~ww | d | |d|fdtf | t j|j}| |jd t	t
f dS )	z
        Test that the platform accept(2) call fails with either L{EMFILE} or
        L{ENOBUFS} when there are too many file descriptors open.
        )	127.0.0.1r   r      Fr   Nz7Could provoke neither EMFILE nor ENOBUFS from platform.r4   )r0   bindgetsocknamelistensetblockingranger$   OSErrorargsr   r   failassertIn
connect_exr   assertRaiseserroraccept)r%   portserverPortNumberclientieexcr'   r'   r(   test_acceptOutOfFiles[   s*   



z.PlatformAssumptionsTests.test_acceptOutOfFilesN)__name__
__module____qualname____doc__r$   r)   r/   r0   r   r   getTyperI   r'   r'   r'   r(   r   )   s    	
r   c                   @   s   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
ee dkddd Zee dkddd Zeejddkddd Zee dkddd Zdd  Zd!S )"SelectReactorTestsz7
    Tests for select-specific failure conditions.
    c                 C   s   g | _ g | _t| jj d S N)portsmessagesr   addObserverr1   r%   r'   r'   r(   r)      s   zSelectReactorTests.setUpc                 C   s"   t | jj tdd | jD S )Nc                 S   s   g | ]}t |jqS r'   )r   stopListening).0pr'   r'   r(   
<listcomp>   s    z/SelectReactorTests.tearDown.<locals>.<listcomp>)r   removeObserverrR   r1   r   rQ   rT   r'   r'   r(   r/      s   zSelectReactorTests.tearDownc                 C   s&   t |||d}|  | j| |S )z
        Create, start, and return a new L{Port}, also tracking it so it can
        be stopped in the test tear down.
        	interface)r   startListeningrQ   r1   )r%   
portNumberfactoryr[   rW   r'   r'   r(   rC      s   zSelectReactorTests.portc                    s   G fddd}t  }| jd|dd}| |d|  |  dtj   fdd	| jD }| t|dd
| jf  dS )a  
        Test behavior in the face of an exception from C{accept(2)}.

        On any exception which indicates the platform is unable or unwilling
        to allocate further resources to us, the existing port should remain
        listening, a message should be logged, and the exception should not
        propagate outward from doRead.

        @param socketErrorNumber: The errno to simulate from accept.
        c                          e Zd ZdZ fddZdS )z9SelectReactorTests._acceptFailureTest.<locals>.FakeSocketzI
            Pretend to be a socket in an overloaded system.
            c                    s   t  t rP   )r;   osstrerrorrT   socketErrorNumberr'   r(   rB      s   z@SelectReactorTests._acceptFailureTest.<locals>.FakeSocket.acceptNrJ   rK   rL   rM   rB   r'   rb   r'   r(   
FakeSocket   s    re   r   r4   rZ   r0   z/Could not accept new connection ({acceptError})c                    s(   g | ]}| d ko| d kqS )
log_formatacceptError)get)rV   msg)expectedErrorCodeexpectedFormatr'   r(   rX      s
    z9SelectReactorTests._acceptFailureTest.<locals>.<listcomp>z+Log event for failed accept not found in %rN)	r   rC   patchdoReaderrno	errorcoderR   assertGreaterr   )r%   rc   re   r^   rC   matchingMessagesr'   )rj   rk   rc   r(   _acceptFailureTest   s   

z%SelectReactorTests._acceptFailureTestc                 C   
   |  tS )a*  
        C{accept(2)} can fail with C{EMFILE} when there are too many open file
        descriptors in the process.  Test that this doesn't negatively impact
        any other existing connections.

        C{EMFILE} mainly occurs on Linux when the open file rlimit is
        encountered.
        )rr   r   rT   r'   r'   r(   test_tooManyFilesFromAccept      
	z.SelectReactorTests.test_tooManyFilesFromAcceptc                 C   rs   )z
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
        C{accept(2)} fails with C{ENOBUFS}.

        This mainly occurs on Windows and FreeBSD, but may be possible on
        Linux and other platforms as well.
        )rr   r   rT   r'   r'   r(   test_noBufferSpaceFromAccept      
z/SelectReactorTests.test_noBufferSpaceFromAcceptc                 C   rs   )z
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
        C{accept(2)} fails with C{ECONNABORTED}.

        It is not clear whether this is actually possible for TCP
        connections on modern versions of Linux.
        )rr   r   rT   r'   r'   r(    test_connectionAbortedFromAccept   rw   z3SelectReactorTests.test_connectionAbortedFromAcceptr3   z(Windows accept(2) cannot generate ENFILEc                 C   rs   )z
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
        C{accept(2)} fails with C{ENFILE}.

        This can occur on Linux when the system has exhausted (!) its supply
        of inodes.
        )rr   r   rT   r'   r'   r(   test_noFilesFromAccept   ru   z)SelectReactorTests.test_noFilesFromAcceptz(Windows accept(2) cannot generate ENOMEMc                 C   rs   )a  
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
        C{accept(2)} fails with C{ENOMEM}.

        On Linux at least, this can sensibly occur, even in a Python program
        (which eats memory like no ones business), when memory has become
        fragmented or low memory has been filled (d_alloc calls
        kmem_cache_alloc calls kmalloc - kmalloc only allocates out of low
        memory).
        )rr   r   rT   r'   r'   r(   test_noMemoryFromAccept   s   
z*SelectReactorTests.test_noMemoryFromAcceptINFRASTRUCTUREAZUREPIPELINESz(Hangs on Azure Pipelines due to firewallc                    s   t  }t|_| jd|dd| j g   fdd}| | fdd} |  d_  | 	jd  |    | 
jd   | 
jd d	S )
z
        L{tcp.Port.doRead} increases the number of consecutive
        C{accept} calls it performs if all of the previous C{accept}
        calls succeed; otherwise, it reduces the number to the amount
        of successful calls.
        r   r4   rZ   c                     s    D ]} |    qd S rP   )r+   rE   )clientsr'   r(   closeAll  s   
z7SelectReactorTests.test_acceptScaling.<locals>.closeAllc                     s(   t  t jt j} | d  jf | S )Nr4   )r0   AF_INETSOCK_STREAMconnectgetHostrC   r}   )rC   r'   r(   r     s   z6SelectReactorTests.test_acceptScaling.<locals>.connectr   N)r   r   protocolrC   
addCleanuprU   r1   numberAcceptsrm   rp   assertEqual)r%   r^   r   r   r'   )r~   rC   r(   test_acceptScaling  s"   
z%SelectReactorTests.test_acceptScalingz'Windows accept(2) cannot generate EPERMc                    s   ddg G  fddd}t D ]}tj| j qj| j dg t }jd|dd}d|_	|d|  |
  |jd d	S )
z
        C{accept(2)} returning C{EPERM} is treated as a transient
        failure and the call retried no more than the maximum number
        of consecutive C{accept(2)} calls.
        {   r   c                       s    e Zd ZdZ fddZdS )zLSelectReactorTests.test_permissionFailure.<locals>.FakeSocketWithAcceptLimit
            Pretend to be a socket in an overloaded system whose
            C{accept} method can only be called
            C{maximumNumberOfAccepts} times.
            c                    s6    d  d7  <  d kr d tttt)Nr   r   z(Maximum number of accept calls exceeded.)r=   r;   r   r`   ra   oselfacceptCallsmaximumNumberOfAcceptsr%   r'   r(   rB   C  s   
zSSelectReactorTests.test_permissionFailure.<locals>.FakeSocketWithAcceptLimit.acceptNrd   r'   r   r'   r(   FakeSocketWithAcceptLimit<  s    r   r4   rZ   r0   r   N)r:   r@   r0   rA   rB   failureExceptionr   rC   r   rl   rm   assertEquals)r%   r   _r^   rC   r'   r   r(   test_permissionFailure2  s   z)SelectReactorTests.test_permissionFailurec                    s   t t}|tttg tdd |D d  G  fddd}t }| jd|dd}| 	|d	|  |
  | tj}| dt| | |d jjd   d
S )z}
        A C{socket.error} raised by C{accept(2)} whose C{errno} is
        unknown to the recovery logic is logged.
        c                 s   s    | ]
}t |tr|V  qd S rP   )
isinstanceint)rV   rA   r'   r'   r(   	<genexpr>g  s    zBSelectReactorTests.test_unknownSocketErrorRaise.<locals>.<genexpr>r   c                       r_   )zYSelectReactorTests.test_unknownSocketErrorRaise.<locals>.FakeSocketWithUnknownAcceptErrorr   c                    s
   t  d)Nzunknown socket error message)r;   r   unknownAcceptErrorr'   r(   rB   q  s   
z`SelectReactorTests.test_unknownSocketErrorRaise.<locals>.FakeSocketWithUnknownAcceptError.acceptNrd   r'   r   r'   r(    FakeSocketWithUnknownAcceptErrorj  s    r   r   r4   rZ   r0   N)listr	   extendr
   r   r   maxr   rC   rl   rm   flushLoggedErrorsr0   rA   r   r   valuer<   )r%   knownErrorsr   r^   rC   failuresr'   r   r(   test_unknownSocketErrorRaise^  s   
z/SelectReactorTests.test_unknownSocketErrorRaiseN)rJ   rK   rL   rM   r)   r/   rC   rr   rt   rv   rx   r   r   rN   ry   rz   r`   environrh   r   r   r   r'   r'   r'   r(   rO      s*    
)





)
+rO   )'rM   rn   r`   r0   r   ImportErrorunittestr   twisted.internetr   r   twisted.internet.deferr   r   twisted.internet.protocolr   r   twisted.internet.tcpr	   r
   r   r   r   r   r   r   r   r   r   twisted.pythonr   twisted.python.runtimer   twisted.trial.unittestr   IReactorFDSet
providedByr   rO   r'   r'   r'   r(   <module>   s6   4[