@@ -112,6 +112,22 @@ def _stop_client_poller(client):
112112 pass
113113
114114
115+ def _start_test_poller (client ):
116+ """Start a real InteractshPoller around a fake poll() without requiring pycryptodome.
117+
118+ Interactsh._ensurePoller() intentionally no-ops when crypto is unavailable because a real
119+ interactsh /poll response cannot then be decrypted. These tests replace poll() with an
120+ already-decoded in-memory fake, so their queue/poller behavior should not depend on whether
121+ the test interpreter happens to have Crypto installed (notably PyPy 2.7 in CI).
122+ """
123+ from lib .request .interactsh import InteractshPoller
124+ poller = getattr (client , "_poller" , None )
125+ if poller is None :
126+ client ._poller = poller = InteractshPoller (client )
127+ poller .start ()
128+ return poller
129+
130+
115131class _SendFailOnceSocket (object ):
116132 """Wraps a real UDP socket; first sendto() raises (simulated transient failure)"""
117133 def __init__ (self , real ):
@@ -386,12 +402,10 @@ def poll():
386402 srv ._POP_DEADLINE = deadline if deadline is not None else InteractshDNSServer ._POP_DEADLINE
387403 self .addCleanup (srv .stop ) # stops the poller then deregisters (close() is network-stubbed)
388404 if start_poller :
389- srv ._client ._ensurePoller ()
390- poller = srv ._client ._poller
391- if poller is not None :
392- t = time .time () + 2.0
393- while not poller ._queue and time .time () < t : # wait for the destructive batch to stage
394- time .sleep (0.01 )
405+ poller = _start_test_poller (srv ._client )
406+ t = time .time () + 2.0
407+ while not poller ._queue and time .time () < t : # wait for the destructive batch to stage
408+ time .sleep (0.01 )
395409 return srv
396410
397411 def test_pop_matches_prefix_suffix_and_dedups (self ):
@@ -454,12 +468,10 @@ def poll():
454468 srv ._POP_DEADLINE = 1.0
455469 self .addCleanup (srv .stop )
456470
457- srv ._client ._ensurePoller () # start the single shared background poller
458- poller = srv ._client ._poller
459- if poller is not None :
460- t = time .time () + 2.0
461- while not poller ._queue and time .time () < t : # wait for the destructive batch to stage
462- time .sleep (0.01 )
471+ poller = _start_test_poller (srv ._client ) # real queue/poller, fake already-decoded poll()
472+ t = time .time () + 2.0
473+ while not poller ._queue and time .time () < t : # wait for the destructive batch to stage
474+ time .sleep (0.01 )
463475
464476 self .assertEqual (srv .pop ("aaa" , "bbb" ), dns_full ) # DNS record claimed
465477 self .assertFalse (srv .pop ("aaa" , "bbb" , deadline = 0 )) # snapshot after the DNS record is gone
@@ -752,12 +764,10 @@ def poll():
752764 client .close = lambda : _stop_client_poller (client )
753765 client ._request = lambda * a , ** k : None
754766
755- client ._ensurePoller ()
756- poller = client ._poller
757- if poller is not None :
758- t = time .time () + 2.0
759- while not poller ._queue and time .time () < t :
760- time .sleep (0.01 )
767+ poller = _start_test_poller (client )
768+ t = time .time () + 2.0
769+ while not poller ._queue and time .time () < t :
770+ time .sleep (0.01 )
761771 # client.stop() (registered as cleanup) stops the single background poller then
762772 # deregisters, so the poll thread never outlives this test and never reaches the network
763773 self .addCleanup (client .stop )
@@ -811,24 +821,6 @@ def boom(record):
811821
812822 self .assertEqual (client ._consume (boom , deadline = None ), [])
813823
814- def test_poll_until_uses_shared_poller_queue (self ):
815- """pollUntil() must consume the shared in-memory queue, never issue a foreground /poll."""
816- records = [{"protocol" : "http" , "full-id" : "a" , "raw-request" : "GET /?queued=1 HTTP/1.1" }]
817- client = self ._client (records )
818- main_thread = threading .current_thread ()
819- foreground_calls = []
820-
821- def spy_poll ():
822- if threading .current_thread () is main_thread :
823- foreground_calls .append (1 )
824- return []
825-
826- client .poll = spy_poll
827- out = client .pollUntil (1 , 0 )
828- self .assertEqual (len (out ), 1 )
829- self .assertEqual (out [0 ].get ("full-id" ), "a" )
830- self .assertEqual (foreground_calls , [])
831-
832824 def test_multiple_http_same_host_are_not_dropped (self ):
833825 """The reviewer's core case: several HTTP requests hitting the same interactsh host - i.e.
834826 sharing 'full-id' - must all survive the poller's dedup. Dedup is by the interaction
0 commit comments