Handle Twisted requests through a tower::Service

This library helps converting Twisted's IRequest to an http::Request, and then sending the http::Response back.

Usage

Handle a Twisted request through a Service

```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;

[pyclass]

struct Handler { service: BoxCloneService>, Response, Infallible>, }

[pymethods]

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)
}

}

[pymodule]

fn myhandler(py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; Ok(()) } ```

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() ```

Define a Twisted 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

[pyclass(extends=Resource)]

struct MyResource;

[pymethods]

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

[pyfunction]

fn getresource(py: Python) -> PyResult> { let service = tower::servicefn(|_request: Request<_>| async move { let response = Response::new(String::from("hello")); Ok(response) });

Py::new(py, Resource::from_service::<_, _, Infallible>(service))

}

[pymodule]

fn myhandler(py: Python, m: &PyModule) -> PyResult<()> { m.addclass::()?; m.addfunction(wrappyfunction!(getresource, m)?)?; Ok(()) } ```

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()))

or

endpoints.serverFromString(reactor, "tcp:8888").listen(Site(get_resource()))

reactor.run() ```

Limitations

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).