forked from goodboy/tractor
				
			Add a super naive multi-host-capable web-req proxier for @jc211
							parent
							
								
									5c2e972315
								
							
						
					
					
						commit
						61cd95d883
					
				| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					import tractor
 | 
				
			||||||
 | 
					import trio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					log = tractor.log.get_console_log(
 | 
				
			||||||
 | 
					    _root_name='my_app',
 | 
				
			||||||
 | 
					    name='client',
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def client_main():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # enable console logging for our custom app's logger
 | 
				
			||||||
 | 
					    tractor.log.get_console_log(
 | 
				
			||||||
 | 
					        level='info',
 | 
				
			||||||
 | 
					        _root_name='my_app',
 | 
				
			||||||
 | 
					        name='client',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # presuming you can get a ref to the target server RPC-ctx func
 | 
				
			||||||
 | 
					    from server import proxy_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async with (
 | 
				
			||||||
 | 
					        tractor.open_root_actor(
 | 
				
			||||||
 | 
					            name='web_requester',
 | 
				
			||||||
 | 
					            registry_addrs=[('127.0.0.1', 1616)],
 | 
				
			||||||
 | 
					            enable_modules=[],  # since this isn't a service actor
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # use discovery api to find the server actor on your net
 | 
				
			||||||
 | 
					        # (NOTE, in which case the below registry addr would have to
 | 
				
			||||||
 | 
					        # be the public IP of that host!)
 | 
				
			||||||
 | 
					        tractor.find_actor(
 | 
				
			||||||
 | 
					            name='web_proxier',
 | 
				
			||||||
 | 
					            registry_addrs=[('127.0.0.1', 1616)],
 | 
				
			||||||
 | 
					        ) as portal,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # open an RPC context with the remote actor, thus spawning
 | 
				
			||||||
 | 
					        # a new task implemented as the function defined in the
 | 
				
			||||||
 | 
					        # server code.
 | 
				
			||||||
 | 
					        portal.open_context(
 | 
				
			||||||
 | 
					            proxy_request,
 | 
				
			||||||
 | 
					            address='https://github.com',
 | 
				
			||||||
 | 
					        ) as (ctx, first),
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					        resp: dict = await ctx.result()
 | 
				
			||||||
 | 
					        print(resp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trio.run(client_main)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,75 @@
 | 
				
			||||||
 | 
					import httpx
 | 
				
			||||||
 | 
					import tractor
 | 
				
			||||||
 | 
					import trio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					log = tractor.log.get_console_log(
 | 
				
			||||||
 | 
					    _root_name='my_app',
 | 
				
			||||||
 | 
					    name='server_thingy',
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@tractor.context
 | 
				
			||||||
 | 
					async def proxy_request(
 | 
				
			||||||
 | 
					    ctx: tractor.Context,
 | 
				
			||||||
 | 
					    address: str,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    log.info(
 | 
				
			||||||
 | 
					        'Rxed client request\n'
 | 
				
			||||||
 | 
					        f'{address}\n'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    async with httpx.AsyncClient() as client:
 | 
				
			||||||
 | 
					        await ctx.started()  # signal the remote task has started its client
 | 
				
			||||||
 | 
					        log.info(
 | 
				
			||||||
 | 
					            'Opened `httpx` client..'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        resp: httpx.Response = await client.get(address)  # do the proxied request, get response.
 | 
				
			||||||
 | 
					        log.info(
 | 
				
			||||||
 | 
					            'Got response..\n'
 | 
				
			||||||
 | 
					            f'{resp}\n'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # only breaking this up to clarify that you didn't have to only return a single result you could have opened
 | 
				
			||||||
 | 
					        # a long lived stream to avoid task spawning overhead in this service actor.. but more on that later..
 | 
				
			||||||
 | 
					        #
 | 
				
			||||||
 | 
					        # NOTEs, cast to `str` here since we can't serialize the
 | 
				
			||||||
 | 
					        # response type for the wire directly, at least no without
 | 
				
			||||||
 | 
					        # a custom `msgspec.Decoder`!!
 | 
				
			||||||
 | 
					        return str(resp)
 | 
				
			||||||
 | 
					        #
 | 
				
			||||||
 | 
					        # ^TODO, various typed msging options:
 | 
				
			||||||
 | 
					        # -[ ] try returning just the `resp` verbatim => should raise
 | 
				
			||||||
 | 
					        #    an MTE
 | 
				
			||||||
 | 
					        # -[ ] try defining a custom `Response` msg to proxy the orig
 | 
				
			||||||
 | 
					        #     types fields and/or a decoder to serialize it?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def main():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # enable console logging for our custom app's logger
 | 
				
			||||||
 | 
					    tractor.log.get_console_log(
 | 
				
			||||||
 | 
					        level='info',
 | 
				
			||||||
 | 
					        _root_name='my_app',
 | 
				
			||||||
 | 
					        name='server_thingy',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # since run as a script this will likely be `__main__`
 | 
				
			||||||
 | 
					    # so instead we want to just use our module name..
 | 
				
			||||||
 | 
					    this_mod: str = 'server'
 | 
				
			||||||
 | 
					    async with tractor.open_root_actor(
 | 
				
			||||||
 | 
					        name='web_proxier',
 | 
				
			||||||
 | 
					        registry_addrs=[('127.0.0.1', 1616)],
 | 
				
			||||||
 | 
					        enable_modules=[this_mod],
 | 
				
			||||||
 | 
					        loglevel='info',
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					        # just block waiting for a peer actor to connect and open an RPC context using the above
 | 
				
			||||||
 | 
					        # proxy endpoint.
 | 
				
			||||||
 | 
					        log.info(
 | 
				
			||||||
 | 
					            'proxy server up bby!\n'
 | 
				
			||||||
 | 
					            'waiting to serve some requests..\n'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        await trio.sleep_forever()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    trio.run(main)
 | 
				
			||||||
		Loading…
	
		Reference in New Issue