tower::Service
This library helps converting Twisted's IRequest
to an http::Request
, and then sending the http::Response
back.
```rust use std::convert::Infallible;
use bytes::Bytes; use http::{Request, Response}; use http_body::Full; use pyo3::prelude::*; use tower::util::BoxCloneService;
use pyo3twistedweb::handletwistedrequestthroughservice;
struct Handler {
service: BoxCloneService
impl Handler { #[new] fn new() -> Self { let service = tower::servicefn(|request: Request<_>| async move { let response = Response::new(String::from("hello")); Ok(response) });
Self {
service: BoxCloneService::new(service),
}
}
fn handle<'a>(&self, twisted_request: &'a PyAny) -> PyResult<&'a PyAny> {
let service = self.service.clone();
handle_twisted_request_through_service(service, twisted_request)
}
}
fn myhandler(py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::
And on the Python side:
```python from twisted.internet import asyncioreactor asyncioreactor.install()
import asyncio from twisted.internet.defer import ensureDeferred, Deferred from twisted.web.server import NOTDONEYET from twisted.web import server, resource from twisted.internet import endpoints, reactor
from my_handler import Handler
class MyResource(resource.Resource): isLeaf = True
def __init__(self) -> None:
super().__init__()
self.handler = Handler()
def render(self, request):
f = self.handler.handle(request)
ensureDeferred(Deferred.fromFuture(f))
return NOT_DONE_YET
endpoints.serverFromString(reactor, "tcp:8888").listen(server.Site(MyResource())) reactor.run() ```
Resource
out of a Service```rust use std::convert::Infallible;
use bytes::Bytes; use http::{Request, Response}; use pyo3::prelude::*;
use pyo3twistedweb::Resource;
// Via a (sub)class
struct MyResource;
impl MyResource { #[new] fn new() -> (Self, Resource) { let service = tower::servicefn(|request: Request<_>| async move { let response = Response::new(String::from("hello")); Ok(response) });
let super_ = Resource::from_service::<_, _, Infallible>(service);
(Self, super_)
}
}
// Via a function
fn getresource(py: Python) -> PyResult
Py::new(py, Resource::from_service::<_, _, Infallible>(service))
}
fn myhandler(py: Python, m: &PyModule) -> PyResult<()> {
m.addclass::
And on the Python side:
```python from twisted.internet import asyncioreactor asyncioreactor.install()
import asyncio from twisted.web.server import Site from twisted.internet import endpoints, reactor
from myhandler import MyResource, getresource
endpoints.serverFromString(reactor, "tcp:8888").listen(Site(MyResource()))
endpoints.serverFromString(reactor, "tcp:8888").listen(Site(get_resource()))
reactor.run() ```
The Twisted asyncioreactor
should be installed. Futures are executed by the tokio
runtime through pyo3-asyncio
, which requires an asyncio event loop to be running.
The Service must accept an http::Request<bytes::Bytes>
, and return an http::Request<impl bytes::Buf>
.
The Service::Error
must convert to pyo3::PyErr
(std::convert::Infallible
is a good candidate if you don't want your handler to throw a Python exception).