diff --git a/.lock b/.lock new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000..e69de29bb2 diff --git a/actix_codec/all.html b/actix_codec/all.html new file mode 100644 index 0000000000..eea140d088 --- /dev/null +++ b/actix_codec/all.html @@ -0,0 +1 @@ +
Redirecting to ../../actix_codec/struct.BytesCodec.html...
+ + + \ No newline at end of file diff --git a/actix_codec/fn.poll_read_buf.html b/actix_codec/fn.poll_read_buf.html new file mode 100644 index 0000000000..d61930777b --- /dev/null +++ b/actix_codec/fn.poll_read_buf.html @@ -0,0 +1,35 @@ +pub fn poll_read_buf<T, B>(
+ io: Pin<&mut T>,
+ cx: &mut Context<'_>,
+ buf: &mut B
+) -> Poll<Result<usize, Error>>
Try to read data from an AsyncRead
into an implementer of the BufMut
trait.
use bytes::{Bytes, BytesMut};
+use tokio_stream as stream;
+use tokio::io::Result;
+use tokio_util::io::{StreamReader, poll_read_buf};
+use futures::future::poll_fn;
+use std::pin::Pin;
+
+// Create a reader from an iterator. This particular reader will always be
+// ready.
+let mut read = StreamReader::new(stream::iter(vec![Result::Ok(Bytes::from_static(&[0, 1, 2, 3]))]));
+
+let mut buf = BytesMut::new();
+let mut reads = 0;
+
+loop {
+ reads += 1;
+ let n = poll_fn(|cx| poll_read_buf(Pin::new(&mut read), cx, &mut buf)).await?;
+
+ if n == 0 {
+ break;
+ }
+}
+
+// one or more reads might be necessary.
+assert!(reads >= 1);
+assert_eq!(&buf[..], &[0, 1, 2, 3]);
Redirecting to ../../actix_codec/struct.Framed.html...
+ + + \ No newline at end of file diff --git a/actix_codec/framed/struct.FramedParts.html b/actix_codec/framed/struct.FramedParts.html new file mode 100644 index 0000000000..5514d76b93 --- /dev/null +++ b/actix_codec/framed/struct.FramedParts.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_codec/struct.FramedParts.html...
+ + + \ No newline at end of file diff --git a/actix_codec/index.html b/actix_codec/index.html new file mode 100644 index 0000000000..e85c2b9059 --- /dev/null +++ b/actix_codec/index.html @@ -0,0 +1,6 @@ +Codec utilities for working with framed protocols.
+Contains adapters to go from streams of bytes, AsyncRead
and AsyncWrite
, to framed
+streams implementing Sink
and Stream
. Framed streams are also known as transports
.
Stream
and Sink
interface to an underlying I/O object, using the Encoder
and
+Decoder
traits to encode and decode frames.FramedParts
contains an export of the data of a Framed transport.FramedWrite
.AsyncRead
into an implementer of the BufMut
trait.Redirecting to ../../actix_codec/struct.LinesCodec.html...
+ + + \ No newline at end of file diff --git a/actix_codec/sidebar-items.js b/actix_codec/sidebar-items.js new file mode 100644 index 0000000000..ada95f74ef --- /dev/null +++ b/actix_codec/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["poll_read_buf"],"struct":["BytesCodec","Framed","FramedParts","LinesCodec","ReadBuf"],"trait":["AsyncRead","AsyncWrite","Decoder","Encoder"]}; \ No newline at end of file diff --git a/actix_codec/struct.BytesCodec.html b/actix_codec/struct.BytesCodec.html new file mode 100644 index 0000000000..643c0c9e52 --- /dev/null +++ b/actix_codec/struct.BytesCodec.html @@ -0,0 +1,28 @@ +pub struct BytesCodec;
Bytes codec. Reads/writes chunks of bytes from a stream.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Framed<T, U> { /* private fields */ }
A unified Stream
and Sink
interface to an underlying I/O object, using the Encoder
and
+Decoder
traits to encode and decode frames.
Raw I/O objects work with byte sequences, but higher-level code usually wants to batch these
+into meaningful chunks, called “frames”. This method layers framing on top of an I/O object,
+by using the Encoder
/Decoder
traits to handle encoding and decoding of message frames.
+Note that the incoming and outgoing frame types may be distinct.
Returns a reference to the underlying I/O stream wrapped by Frame
.
Note that care should be taken to not tamper with the underlying stream of data coming in as +it may corrupt the stream of frames otherwise being worked with.
+Returns a mutable reference to the underlying I/O stream.
+Note that care should be taken to not tamper with the underlying stream of data coming in as +it may corrupt the stream of frames otherwise being worked with.
+Returns a Pin
of a mutable reference to the underlying I/O stream.
Check if read buffer is empty.
+Check if write buffer is empty.
+Check if write buffer is full.
+Check if framed is able to write more data.
+Framed
object considers ready if there is free space in write buffer.
Consume the Frame
, returning Frame
with different codec.
Consume the Frame
, returning Frame
with different io.
Consume the Frame
, returning Frame
with different codec.
Serialize item and write to the inner buffer
+Try to read underlying I/O stream and decode item.
+This function returns a single object that is both Stream
and Sink
; grouping this into
+a single object is often useful for layering things like gzip or TLS, which require both
+read and write access to the underlying object.
These objects take a stream, a read buffer and a write buffer. These fields can be obtained
+from an existing Framed
with the into_parts
method.
Consumes the Frame
, returning its underlying I/O stream, the buffer with unprocessed data,
+and the codec.
Note that care should be taken to not tamper with the underlying stream of data coming in as +it may corrupt the stream of frames otherwise being worked with.
+Sink
to receive a value. Read morepoll_ready
which returned Poll::Ready(Ok(()))
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct FramedParts<T, U> {
+ pub io: T,
+ pub codec: U,
+ pub read_buf: BytesMut,
+ pub write_buf: BytesMut,
+ /* private fields */
+}
FramedParts
contains an export of the data of a Framed transport.
It can be used to construct a new Framed
with a different codec. It contains all current
+buffers and the inner transport.
io: T
The inner transport used to read bytes to and write bytes to.
+codec: U
The codec object.
+read_buf: BytesMut
The buffer with read but unprocessed data.
+write_buf: BytesMut
A buffer with unprocessed data which are not written yet.
+Creates a new default FramedParts
.
Creates a new FramedParts
with read buffer.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read more#[non_exhaustive]pub struct LinesCodec;
Lines codec. Reads/writes line delimited strings.
+Will split input up by LF or CRLF delimiters. Carriage return characters at the end of lines are +not preserved.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ReadBuf<'a> { /* private fields */ }
A wrapper around a byte buffer that is incrementally filled and initialized.
+This type is a sort of “double cursor”. It tracks three regions in the +buffer: a region at the beginning of the buffer that has been logically +filled with data, a region that has been initialized at some point but not +yet logically filled, and a region at the end that may be uninitialized. +The filled region is guaranteed to be a subset of the initialized region.
+In summary, the contents of the buffer can be visualized as:
+[ capacity ]
+[ filled | unfilled ]
+[ initialized | uninitialized ]
+
It is undefined behavior to de-initialize any bytes from the uninitialized +region, since it is merely unknown whether this region is uninitialized or +not, and if part of it turns out to be initialized, it must stay initialized.
+Creates a new ReadBuf
from a fully initialized buffer.
Creates a new ReadBuf
from a fully uninitialized buffer.
Use assume_init
if part of the buffer is known to be already initialized.
Returns a mutable reference to the filled portion of the buffer.
+Returns a new ReadBuf
comprised of the unfilled section up to n
.
Returns a shared reference to the initialized portion of the buffer.
+This includes the filled portion.
+Returns a mutable reference to the initialized portion of the buffer.
+This includes the filled portion.
+Returns a mutable reference to the entire buffer, without ensuring that it has been fully +initialized.
+The elements between 0 and self.filled().len()
are filled, and those between 0 and
+self.initialized().len()
are initialized (and so can be converted to a &mut [u8]
).
The caller of this method must ensure that these invariants are upheld. For example, if the
+caller initializes some of the uninitialized section of the buffer, it must call
+assume_init
with the number of bytes initialized.
The caller must not de-initialize portions of the buffer that have already been initialized.
+This includes any bytes in the region marked as uninitialized by ReadBuf
.
Returns a mutable reference to the unfilled part of the buffer without ensuring that it has been fully +initialized.
+The caller must not de-initialize portions of the buffer that have already been initialized.
+This includes any bytes in the region marked as uninitialized by ReadBuf
.
Returns a mutable reference to the unfilled part of the buffer, ensuring it is fully initialized.
+Since ReadBuf
tracks the region of the buffer that has been initialized, this is effectively “free” after
+the first use.
Returns a mutable reference to the first n
bytes of the unfilled part of the buffer, ensuring it is
+fully initialized.
Panics if self.remaining()
is less than n
.
Returns the number of bytes at the end of the slice that have not yet been filled.
+Clears the buffer, resetting the filled region to empty.
+The number of initialized bytes is not changed, and the contents of the buffer are not modified.
+Advances the size of the filled region of the buffer.
+The number of initialized bytes is not changed.
+Panics if the filled region of the buffer would become larger than the initialized region.
+Sets the size of the filled region of the buffer.
+The number of initialized bytes is not changed.
+Note that this can be used to shrink the filled region of the buffer in addition to growing it (for
+example, by a AsyncRead
implementation that compresses data in-place).
Panics if the filled region of the buffer would become larger than the initialized region.
+Asserts that the first n
unfilled bytes of the buffer are initialized.
ReadBuf
assumes that bytes are never de-initialized, so this method does nothing when called with fewer
+bytes than are already known to be initialized.
The caller must ensure that n
unfilled bytes of the buffer have already been initialized.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait AsyncRead {
+ // Required method
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut ReadBuf<'_>
+ ) -> Poll<Result<(), Error>>;
+}
Reads bytes from a source.
+This trait is analogous to the std::io::Read
trait, but integrates with
+the asynchronous task system. In particular, the poll_read
method,
+unlike Read::read
, will automatically queue the current task for wakeup
+and return if data is not yet available, rather than blocking the calling
+thread.
Specifically, this means that the poll_read
function will return one of
+the following:
Poll::Ready(Ok(()))
means that data was immediately read and placed into
+the output buffer. The amount of data read can be determined by the
+increase in the length of the slice returned by ReadBuf::filled
. If the
+difference is 0, EOF has been reached.
Poll::Pending
means that no data was read into the buffer
+provided. The I/O object is not currently readable but may become readable
+in the future. Most importantly, the current future’s task is scheduled
+to get unparked when the object is readable. This means that like
+Future::poll
you’ll receive a notification when the I/O object is
+readable again.
Poll::Ready(Err(e))
for other errors are standard I/O errors coming from the
+underlying object.
This trait importantly means that the read
method only works in the
+context of a future’s task. The object may panic if used outside of a task.
Utilities for working with AsyncRead
values are provided by
+AsyncReadExt
.
Attempts to read from the AsyncRead
into buf
.
On success, returns Poll::Ready(Ok(()))
and places data in the
+unfilled portion of buf
. If no data was read (buf.filled().len()
is
+unchanged), it implies that EOF has been reached.
If no data is available for reading, the method returns Poll::Pending
+and arranges for the current task (via cx.waker()
) to receive a
+notification when the object becomes readable or is closed.
pub trait AsyncWrite {
+ // Required methods
+ fn poll_write(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8]
+ ) -> Poll<Result<usize, Error>>;
+ fn poll_flush(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>
+ ) -> Poll<Result<(), Error>>;
+ fn poll_shutdown(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>
+ ) -> Poll<Result<(), Error>>;
+
+ // Provided methods
+ fn poll_write_vectored(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ bufs: &[IoSlice<'_>]
+ ) -> Poll<Result<usize, Error>> { ... }
+ fn is_write_vectored(&self) -> bool { ... }
+}
Writes bytes asynchronously.
+The trait inherits from std::io::Write
and indicates that an I/O object is
+nonblocking. All non-blocking I/O objects must return an error when
+bytes cannot be written instead of blocking the current thread.
Specifically, this means that the poll_write
function will return one of
+the following:
Poll::Ready(Ok(n))
means that n
bytes of data was immediately
+written.
Poll::Pending
means that no data was written from the buffer
+provided. The I/O object is not currently writable but may become writable
+in the future. Most importantly, the current future’s task is scheduled
+to get unparked when the object is writable. This means that like
+Future::poll
you’ll receive a notification when the I/O object is
+writable again.
Poll::Ready(Err(e))
for other errors are standard I/O errors coming from the
+underlying object.
This trait importantly means that the write
method only works in
+the context of a future’s task. The object may panic if used outside of a task.
Note that this trait also represents that the Write::flush
method
+works very similarly to the write
method, notably that Ok(())
means that the
+writer has successfully been flushed, a “would block” error means that the
+current task is ready to receive a notification when flushing can make more
+progress, and otherwise normal errors can happen as well.
Utilities for working with AsyncWrite
values are provided by
+AsyncWriteExt
.
Attempt to write bytes from buf
into the object.
On success, returns Poll::Ready(Ok(num_bytes_written))
. If successful,
+then it must be guaranteed that n <= buf.len()
. A return value of 0
+typically means that the underlying object is no longer able to accept
+bytes and will likely not be able to in the future as well, or that the
+buffer provided is empty.
If the object is not ready for writing, the method returns
+Poll::Pending
and arranges for the current task (via
+cx.waker()
) to receive a notification when the object becomes
+writable or is closed.
Attempts to flush the object, ensuring that any buffered data reach +their destination.
+On success, returns Poll::Ready(Ok(()))
.
If flushing cannot immediately complete, this method returns
+Poll::Pending
and arranges for the current task (via
+cx.waker()
) to receive a notification when the object can make
+progress towards flushing.
Initiates or attempts to shut down this writer, returning success when +the I/O connection has completely shut down.
+This method is intended to be used for asynchronous shutdown of I/O
+connections. For example this is suitable for implementing shutdown of a
+TLS connection or calling TcpStream::shutdown
on a proxied connection.
+Protocols sometimes need to flush out final pieces of data or otherwise
+perform a graceful shutdown handshake, reading/writing more data as
+appropriate. This method is the hook for such protocols to implement the
+graceful shutdown logic.
This shutdown
method is required by implementers of the
+AsyncWrite
trait. Wrappers typically just want to proxy this call
+through to the wrapped type, and base types will typically implement
+shutdown logic here or just return Ok(().into())
. Note that if you’re
+wrapping an underlying AsyncWrite
a call to shutdown
implies that
+transitively the entire stream has been shut down. After your wrapper’s
+shutdown logic has been executed you should shut down the underlying
+stream.
Invocation of a shutdown
implies an invocation of flush
. Once this
+method returns Ready
it implies that a flush successfully happened
+before the shutdown happened. That is, callers don’t need to call
+flush
before calling shutdown
. They can rely that by calling
+shutdown
any pending buffered data will be written out.
This function returns a Poll<io::Result<()>>
classified as such:
Poll::Ready(Ok(()))
- indicates that the connection was
+successfully shut down and is now safe to deallocate/drop/close
+resources associated with it. This method means that the current task
+will no longer receive any notifications due to this method and the
+I/O object itself is likely no longer usable.
Poll::Pending
- indicates that shutdown is initiated but could
+not complete just yet. This may mean that more I/O needs to happen to
+continue this shutdown operation. The current task is scheduled to
+receive a notification when it’s otherwise ready to continue the
+shutdown operation. When woken up this method should be called again.
Poll::Ready(Err(e))
- indicates a fatal error has happened with shutdown,
+indicating that the shutdown operation did not complete successfully.
+This typically means that the I/O object is no longer usable.
This function can return normal I/O errors through Err
, described
+above. Additionally this method may also render the underlying
+Write::write
method no longer usable (e.g. will return errors in the
+future). It’s recommended that once shutdown
is called the
+write
method is no longer called.
This function will panic if not called within the context of a future’s +task.
+Like poll_write
, except that it writes from a slice of buffers.
Data is copied from each buffer in order, with the final buffer
+read from possibly being only partially consumed. This method must
+behave as a call to write
with the buffers concatenated would.
The default implementation calls poll_write
with either the first nonempty
+buffer provided, or an empty one if none exists.
On success, returns Poll::Ready(Ok(num_bytes_written))
.
If the object is not ready for writing, the method returns
+Poll::Pending
and arranges for the current task (via
+cx.waker()
) to receive a notification when the object becomes
+writable or is closed.
This should be implemented as a single “atomic” write action. If any +data has been partially written, it is wrong to return an error or +pending.
+Determines if this writer has an efficient poll_write_vectored
+implementation.
If a writer does not override the default poll_write_vectored
+implementation, code using it may want to avoid the method all together
+and coalesce writes into a single buffer for higher performance.
The default implementation returns false
.
pub trait Decoder {
+ type Item;
+ type Error: From<Error>;
+
+ // Required method
+ fn decode(
+ &mut self,
+ src: &mut BytesMut
+ ) -> Result<Option<Self::Item>, Self::Error>;
+
+ // Provided methods
+ fn decode_eof(
+ &mut self,
+ buf: &mut BytesMut
+ ) -> Result<Option<Self::Item>, Self::Error> { ... }
+ fn framed<T>(self, io: T) -> Framed<T, Self>
+ where T: AsyncRead + AsyncWrite,
+ Self: Sized { ... }
+}
Decoding of frames via buffers.
+This trait is used when constructing an instance of Framed
or
+FramedRead
. An implementation of Decoder
takes a byte stream that has
+already been buffered in src
and decodes the data into a stream of
+Self::Item
frames.
Implementations are able to track state on self
, which enables
+implementing stateful streaming parsers. In many cases, though, this type
+will simply be a unit struct (e.g. struct HttpDecoder
).
For some underlying data-sources, namely files and FIFOs, +it’s possible to temporarily read 0 bytes by reaching EOF.
+In these cases decode_eof
will be called until it signals
+fulfillment of all closing frames by returning Ok(None)
.
+After that, repeated attempts to read from the Framed
or FramedRead
+will not invoke decode
or decode_eof
again, until data can be read
+during a retry.
It is up to the Decoder to keep track of a restart after an EOF, +and to decide how to handle such an event by, for example, +allowing frames to cross EOF boundaries, re-emitting opening frames, or +resetting the entire internal state.
+The type of decoded frames.
+The type of unrecoverable frame decoding errors.
+If an individual message is ill-formed but can be ignored without
+interfering with the processing of future messages, it may be more
+useful to report the failure as an Item
.
From<io::Error>
is required in the interest of making Error
suitable
+for returning directly from a FramedRead
, and to enable the default
+implementation of decode_eof
to yield an io::Error
when the decoder
+fails to consume all available data.
Note that implementors of this trait can simply indicate type Error = io::Error
to use I/O errors as this type.
Attempts to decode a frame from the provided buffer of bytes.
+This method is called by FramedRead
whenever bytes are ready to be
+parsed. The provided buffer of bytes is what’s been read so far, and
+this instance of Decode
can determine whether an entire frame is in
+the buffer and is ready to be returned.
If an entire frame is available, then this instance will remove those +bytes from the buffer provided and return them as a decoded +frame. Note that removing bytes from the provided buffer doesn’t always +necessarily copy the bytes, so this should be an efficient operation in +most circumstances.
+If the bytes look valid, but a frame isn’t fully available yet, then
+Ok(None)
is returned. This indicates to the Framed
instance that
+it needs to read some more bytes before calling this method again.
Note that the bytes provided may be empty. If a previous call to
+decode
consumed all the bytes in the buffer then decode
will be
+called again until it returns Ok(None)
, indicating that more bytes need to
+be read.
Finally, if the bytes in the buffer are malformed then an error is
+returned indicating why. This informs Framed
that the stream is now
+corrupt and should be terminated.
Before returning from the function, implementations should ensure that
+the buffer has appropriate capacity in anticipation of future calls to
+decode
. Failing to do so leads to inefficiency.
For example, if frames have a fixed length, or if the length of the +current frame is known from a header, a possible buffer management +strategy is:
+ +impl Decoder for MyCodec {
+ // ...
+
+ fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+ // ...
+
+ // Reserve enough to complete decoding of the current frame.
+ let current_frame_len: usize = 1000; // Example.
+ // And to start decoding the next frame.
+ let next_frame_header_len: usize = 10; // Example.
+ src.reserve(current_frame_len + next_frame_header_len);
+
+ return Ok(None);
+ }
+}
An optimal buffer management strategy minimizes reallocations and +over-allocations.
+A default method available to be called when there are no more bytes +available to be read from the underlying I/O.
+This method defaults to calling decode
and returns an error if
+Ok(None)
is returned while there is unconsumed data in buf
.
+Typically this doesn’t need to be implemented unless the framing
+protocol differs near the end of the stream, or if you need to construct
+frames across eof boundaries on sources that can be resumed.
Note that the buf
argument may be empty. If a previous call to
+decode_eof
consumed all the bytes in the buffer, decode_eof
will be
+called again until it returns None
, indicating that there are no more
+frames to yield. This behavior enables returning finalization frames
+that may not be based on inbound data.
Once None
has been returned, decode_eof
won’t be called again until
+an attempt to resume the stream has been made, where the underlying stream
+actually returned more data.
Provides a Stream
and Sink
interface for reading and writing to this
+Io
object, using Decode
and Encode
to read and write the raw data.
Raw I/O objects work with byte sequences, but higher-level code usually
+wants to batch these into meaningful chunks, called “frames”. This
+method layers framing on top of an I/O object, by using the Codec
+traits to handle encoding and decoding of messages frames. Note that
+the incoming and outgoing frame types may be distinct.
This function returns a single object that is both Stream
and
+Sink
; grouping this into a single object is often useful for layering
+things like gzip or TLS, which require both read and write access to the
+underlying object.
If you want to work more directly with the streams and sink, consider
+calling split
on the Framed
returned by this method, which will
+break them into separate objects, allowing them to interact more easily.
pub trait Encoder<Item> {
+ type Error: From<Error>;
+
+ // Required method
+ fn encode(
+ &mut self,
+ item: Item,
+ dst: &mut BytesMut
+ ) -> Result<(), Self::Error>;
+}
Trait of helper objects to write out messages as bytes, for use with
+FramedWrite
.
The type of encoding errors.
+FramedWrite
requires Encoder
s errors to implement From<io::Error>
+in the interest letting it return Error
s directly.
Encodes a frame into the buffer provided.
+This method will encode item
into the byte buffer provided by dst
.
+The dst
provided is an internal buffer of the FramedWrite
instance and
+will be written out when possible.
#[main]
Marks async entry-point function to be executed by Actix system.
+#[actix_rt::main]
+async fn main() {
+ println!("Hello world");
+}
#[test]
Marks async test function to be executed in an Actix system.
+#[actix_rt::test]
+async fn my_test() {
+ assert!(true);
+}
Redirecting to ../../actix_rt/struct.Arbiter.html...
+ + + \ No newline at end of file diff --git a/actix_rt/arbiter/struct.ArbiterHandle.html b/actix_rt/arbiter/struct.ArbiterHandle.html new file mode 100644 index 0000000000..6d9f077553 --- /dev/null +++ b/actix_rt/arbiter/struct.ArbiterHandle.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_rt/struct.ArbiterHandle.html...
+ + + \ No newline at end of file diff --git a/actix_rt/attr.main.html b/actix_rt/attr.main.html new file mode 100644 index 0000000000..edcb1b817d --- /dev/null +++ b/actix_rt/attr.main.html @@ -0,0 +1,7 @@ +pub fn spawn<Fut>(f: Fut) -> JoinHandle<Fut::Output> ⓘ
Spawns a future on the current thread as a new task.
+If not immediately awaited, the task can be cancelled using JoinHandle::abort
.
The provided future is spawned as a new task; therefore, panics are caught.
+Panics if Actix system is not running.
+// task resolves successfully
+assert_eq!(actix_rt::spawn(async { 1 }).await.unwrap(), 1);
+
+// task panics
+assert!(actix_rt::spawn(async {
+ panic!("panic is caught at task boundary");
+})
+.await
+.unwrap_err()
+.is_panic());
+
+// task is cancelled before completion
+let handle = actix_rt::spawn(actix_rt::time::sleep(Duration::from_secs(100)));
+handle.abort();
+assert!(handle.await.unwrap_err().is_cancelled());
Tokio-based single-threaded async runtime for the Actix ecosystem.
+In most parts of the the Actix ecosystem, it has been chosen to use !Send futures. For this +reason, a single-threaded runtime is appropriate since it is guaranteed that futures will not +be moved between threads. This can result in small performance improvements over cases where +atomics would otherwise be needed.
+To achieve similar performance to multi-threaded, work-stealing runtimes, applications
+using actix-rt
will create multiple, mostly disconnected, single-threaded runtimes.
+This approach has good performance characteristics for workloads where the majority of tasks
+have similar runtime expense.
The disadvantage is that idle threads will not steal work from very busy, stuck or otherwise
+backlogged threads. Tasks that are disproportionately expensive should be offloaded to the
+blocking task thread-pool using task::spawn_blocking
.
use std::sync::mpsc;
+use actix_rt::{Arbiter, System};
+
+let _ = System::new();
+
+let (tx, rx) = mpsc::channel::<u32>();
+
+let arbiter = Arbiter::new();
+arbiter.spawn_fn(move || tx.send(42).unwrap());
+
+let num = rx.recv().unwrap();
+assert_eq!(num, 42);
+
+arbiter.stop();
+arbiter.join().unwrap();
io-uring
SupportThere is experimental support for using io-uring with this crate by enabling the
+io-uring
feature. For now, it is semver exempt.
Note that there are currently some unimplemented parts of using actix-rt
with io-uring
.
+In particular, when running a System
, only System::block_on
is supported.
Redirecting to macro.pin.html...
+ + + \ No newline at end of file diff --git a/actix_rt/macro.pin.html b/actix_rt/macro.pin.html new file mode 100644 index 0000000000..20bd368cd6 --- /dev/null +++ b/actix_rt/macro.pin.html @@ -0,0 +1,104 @@ +macro_rules! pin { + ($($x:ident),*) => { ... }; + ($( + let $x:ident = $init:expr; + )*) => { ... }; +}
Pins a value on the stack.
+Calls to async fn
return anonymous Future
values that are !Unpin
.
+These values must be pinned before they can be polled. Calling .await
will
+handle this, but consumes the future. If it is required to call .await
on
+a &mut _
reference, the caller is responsible for pinning the future.
Pinning may be done by allocating with Box::pin
or by using the stack
+with the pin!
macro.
The following will fail to compile:
+ +async fn my_async_fn() {
+ // async logic here
+}
+
+#[tokio::main]
+async fn main() {
+ let mut future = my_async_fn();
+ (&mut future).await;
+}
To make this work requires pinning:
+ +use tokio::pin;
+
+async fn my_async_fn() {
+ // async logic here
+}
+
+#[tokio::main]
+async fn main() {
+ let future = my_async_fn();
+ pin!(future);
+
+ (&mut future).await;
+}
Pinning is useful when using select!
and stream operators that require T: Stream + Unpin
.
The pin!
macro takes identifiers as arguments. It does not work
+with expressions.
The following does not compile as an expression is passed to pin!
.
async fn my_async_fn() {
+ // async logic here
+}
+
+#[tokio::main]
+async fn main() {
+ let mut future = pin!(my_async_fn());
+ (&mut future).await;
+}
Using with select:
+ +use tokio::{pin, select};
+use tokio_stream::{self as stream, StreamExt};
+
+async fn my_async_fn() {
+ // async logic here
+}
+
+#[tokio::main]
+async fn main() {
+ let mut stream = stream::iter(vec![1, 2, 3, 4]);
+
+ let future = my_async_fn();
+ pin!(future);
+
+ loop {
+ select! {
+ _ = &mut future => {
+ // Stop looping `future` will be polled after completion
+ break;
+ }
+ Some(val) = stream.next() => {
+ println!("got value = {}", val);
+ }
+ }
+ }
+}
Because assigning to a variable followed by pinning is common, there is also +a variant of the macro that supports doing both in one go.
+ +use tokio::{pin, select};
+
+async fn my_async_fn() {
+ // async logic here
+}
+
+#[tokio::main]
+async fn main() {
+ pin! {
+ let future1 = my_async_fn();
+ let future2 = my_async_fn();
+ }
+
+ select! {
+ _ = &mut future1 => {}
+ _ = &mut future2 => {}
+ }
+}
TCP/UDP/Unix bindings (mostly Tokio re-exports).
+TcpStream
or
+TcpListener
.pub struct Ready(/* private fields */);
Describes the readiness state of an I/O resources.
+Ready
tracks which operation an I/O resource is ready to perform.
Returns a Ready
representing read closed readiness.
Returns a Ready
representing write closed readiness.
Returns true if Ready
is the empty set.
use tokio::io::Ready;
+
+assert!(Ready::EMPTY.is_empty());
+assert!(!Ready::READABLE.is_empty());
Returns true
if the value includes readable
.
use tokio::io::Ready;
+
+assert!(!Ready::EMPTY.is_readable());
+assert!(Ready::READABLE.is_readable());
+assert!(Ready::READ_CLOSED.is_readable());
+assert!(!Ready::WRITABLE.is_readable());
Returns true
if the value includes writable readiness
.
use tokio::io::Ready;
+
+assert!(!Ready::EMPTY.is_writable());
+assert!(!Ready::READABLE.is_writable());
+assert!(Ready::WRITABLE.is_writable());
+assert!(Ready::WRITE_CLOSED.is_writable());
Returns true
if the value includes read-closed readiness
.
use tokio::io::Ready;
+
+assert!(!Ready::EMPTY.is_read_closed());
+assert!(!Ready::READABLE.is_read_closed());
+assert!(Ready::READ_CLOSED.is_read_closed());
Returns true
if the value includes write-closed readiness
.
use tokio::io::Ready;
+
+assert!(!Ready::EMPTY.is_write_closed());
+assert!(!Ready::WRITABLE.is_write_closed());
+assert!(Ready::WRITE_CLOSED.is_write_closed());
Returns true
if the value includes priority readiness
.
use tokio::io::Ready;
+
+assert!(!Ready::EMPTY.is_priority());
+assert!(!Ready::WRITABLE.is_priority());
+assert!(Ready::PRIORITY.is_priority());
|=
operation. Read moreself
and other
) and is used by the <=
+operator. Read morepub struct TcpListener { /* private fields */ }
A TCP socket server, listening for connections.
+You can accept a new connection by using the accept
+method.
A TcpListener
can be turned into a Stream
with TcpListenerStream
.
Note that accepting a connection can lead to various errors and not all +of them are necessarily fatal ‒ for example having too many open file +descriptors or the other side closing the connection while it waits in +an accept queue. These would terminate the stream if not handled in any +way.
+Using accept
:
use tokio::net::TcpListener;
+
+use std::io;
+
+async fn process_socket<T>(socket: T) {
+ // do work with socket here
+}
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let listener = TcpListener::bind("127.0.0.1:8080").await?;
+
+ loop {
+ let (socket, _) = listener.accept().await?;
+ process_socket(socket).await;
+ }
+}
Creates a new TcpListener
, which will be bound to the specified address.
The returned listener is ready for accepting connections.
+Binding with a port number of 0 will request that the OS assigns a port
+to this listener. The port allocated can be queried via the local_addr
+method.
The address type can be any implementor of the ToSocketAddrs
trait.
+If addr
yields multiple addresses, bind will be attempted with each of
+the addresses until one succeeds and returns the listener. If none of
+the addresses succeed in creating a listener, the error returned from
+the last attempt (the last address) is returned.
This function sets the SO_REUSEADDR
option on the socket.
To configure the socket before binding, you can use the TcpSocket
+type.
use tokio::net::TcpListener;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let listener = TcpListener::bind("127.0.0.1:2345").await?;
+
+ // use the listener
+
+ Ok(())
+}
Accepts a new incoming connection from this listener.
+This function will yield once a new TCP connection is established. When
+established, the corresponding TcpStream
and the remote peer’s
+address will be returned.
This method is cancel safe. If the method is used as the event in a
+tokio::select!
statement and some other branch
+completes first, then it is guaranteed that no new connections were
+accepted by this method.
use tokio::net::TcpListener;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let listener = TcpListener::bind("127.0.0.1:8080").await?;
+
+ match listener.accept().await {
+ Ok((_socket, addr)) => println!("new client: {:?}", addr),
+ Err(e) => println!("couldn't get client: {:?}", e),
+ }
+
+ Ok(())
+}
Polls to accept a new incoming connection to this listener.
+If there is no connection to accept, Poll::Pending
is returned and the
+current task will be notified by a waker. Note that on multiple calls
+to poll_accept
, only the Waker
from the Context
passed to the most
+recent call is scheduled to receive a wakeup.
Creates new TcpListener
from a std::net::TcpListener
.
This function is intended to be used to wrap a TCP listener from the +standard library in the Tokio equivalent.
+This API is typically paired with the socket2
crate and the Socket
+type to build up and customize a listener before it’s shipped off to the
+backing event loop. This allows configuration of options like
+SO_REUSEPORT
, binding to multiple addresses, etc.
The caller is responsible for ensuring that the listener is in
+non-blocking mode. Otherwise all I/O operations on the listener
+will block the thread, which will cause unexpected behavior.
+Non-blocking mode can be set using set_nonblocking
.
use std::error::Error;
+use tokio::net::TcpListener;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let std_listener = std::net::TcpListener::bind("127.0.0.1:0")?;
+ std_listener.set_nonblocking(true)?;
+ let listener = TcpListener::from_std(std_listener)?;
+ Ok(())
+}
This function panics if it is not called from within a runtime with +IO enabled.
+The runtime is usually set implicitly when this function is called
+from a future driven by a tokio runtime, otherwise runtime can be set
+explicitly with Runtime::enter
function.
Turns a tokio::net::TcpListener
into a std::net::TcpListener
.
The returned std::net::TcpListener
will have nonblocking mode set as
+true
. Use set_nonblocking
to change the blocking mode if needed.
use std::error::Error;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let tokio_listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?;
+ let std_listener = tokio_listener.into_std()?;
+ std_listener.set_nonblocking(false)?;
+ Ok(())
+}
Returns the local address that this listener is bound to.
+This can be useful, for example, when binding to port 0 to figure out +which port was actually bound.
+use tokio::net::TcpListener;
+
+use std::io;
+use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let listener = TcpListener::bind("127.0.0.1:8080").await?;
+
+ assert_eq!(listener.local_addr()?,
+ SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080)));
+
+ Ok(())
+}
Gets the value of the IP_TTL
option for this socket.
For more information about this option, see set_ttl
.
use tokio::net::TcpListener;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let listener = TcpListener::bind("127.0.0.1:0").await?;
+
+ listener.set_ttl(100).expect("could not set TTL");
+ assert_eq!(listener.ttl()?, 100);
+
+ Ok(())
+}
Sets the value for the IP_TTL
option on this socket.
This value sets the time-to-live field that is used in every packet sent +from this socket.
+use tokio::net::TcpListener;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let listener = TcpListener::bind("127.0.0.1:0").await?;
+
+ listener.set_ttl(100).expect("could not set TTL");
+
+ Ok(())
+}
Consumes stream, returning the tokio I/O object.
+This is equivalent to
+TcpListener::from_std(stream)
.
pub struct TcpSocket { /* private fields */ }
A TCP socket that has not yet been converted to a TcpStream
or
+TcpListener
.
TcpSocket
wraps an operating system socket and enables the caller to
+configure the socket before establishing a TCP connection or accepting
+inbound connections. The caller is able to set socket option and explicitly
+bind the socket with a socket address.
The underlying socket is closed when the TcpSocket
value is dropped.
TcpSocket
should only be used directly if the default configuration used
+by TcpStream::connect
and TcpListener::bind
does not meet the required
+use case.
Calling TcpStream::connect("127.0.0.1:8080")
is equivalent to:
use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ let stream = socket.connect(addr).await?;
+
+ Ok(())
+}
Calling TcpListener::bind("127.0.0.1:8080")
is equivalent to:
use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ // On platforms with Berkeley-derived sockets, this allows to quickly
+ // rebind a socket, without needing to wait for the OS to clean up the
+ // previous one.
+ //
+ // On Windows, this allows rebinding sockets which are actively in use,
+ // which allows “socket hijacking”, so we explicitly don't set it here.
+ // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
+ socket.set_reuseaddr(true)?;
+ socket.bind(addr)?;
+
+ let listener = socket.listen(1024)?;
+
+ Ok(())
+}
Setting socket options not explicitly provided by TcpSocket
may be done by
+accessing the RawFd
/RawSocket
using AsRawFd
/AsRawSocket
and
+setting the option with a crate like socket2
.
Creates a new socket configured for IPv4.
+Calls socket(2)
with AF_INET
and SOCK_STREAM
.
On success, the newly created TcpSocket
is returned. If an error is
+encountered, it is returned instead.
Create a new IPv4 socket and start listening.
+ +use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+ let socket = TcpSocket::new_v4()?;
+ socket.bind(addr)?;
+
+ let listener = socket.listen(128)?;
+ Ok(())
+}
Creates a new socket configured for IPv6.
+Calls socket(2)
with AF_INET6
and SOCK_STREAM
.
On success, the newly created TcpSocket
is returned. If an error is
+encountered, it is returned instead.
Create a new IPv6 socket and start listening.
+ +use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "[::1]:8080".parse().unwrap();
+ let socket = TcpSocket::new_v6()?;
+ socket.bind(addr)?;
+
+ let listener = socket.listen(128)?;
+ Ok(())
+}
Sets value for the SO_KEEPALIVE
option on this socket.
Gets the value of the SO_KEEPALIVE
option on this socket.
Allows the socket to bind to an in-use address.
+Behavior is platform specific. Refer to the target platform’s +documentation for more details.
+use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ socket.set_reuseaddr(true)?;
+ socket.bind(addr)?;
+
+ let listener = socket.listen(1024)?;
+
+ Ok(())
+}
Retrieves the value set for SO_REUSEADDR
on this socket.
use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ socket.set_reuseaddr(true)?;
+ assert!(socket.reuseaddr().unwrap());
+ socket.bind(addr)?;
+
+ let listener = socket.listen(1024)?;
+ Ok(())
+}
Allows the socket to bind to an in-use port. Only available for unix systems +(excluding Solaris & Illumos).
+Behavior is platform specific. Refer to the target platform’s +documentation for more details.
+use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ socket.set_reuseport(true)?;
+ socket.bind(addr)?;
+
+ let listener = socket.listen(1024)?;
+ Ok(())
+}
Allows the socket to bind to an in-use port. Only available for unix systems +(excluding Solaris & Illumos).
+Behavior is platform specific. Refer to the target platform’s +documentation for more details.
+use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ socket.set_reuseport(true)?;
+ assert!(socket.reuseport().unwrap());
+ socket.bind(addr)?;
+
+ let listener = socket.listen(1024)?;
+ Ok(())
+}
Sets the size of the TCP send buffer on this socket.
+On most operating systems, this sets the SO_SNDBUF
socket option.
Returns the size of the TCP send buffer for this socket.
+On most operating systems, this is the value of the SO_SNDBUF
socket
+option.
Note that if set_send_buffer_size
has been called on this socket
+previously, the value returned by this function may not be the same as
+the argument provided to set_send_buffer_size
. This is for the
+following reasons:
getsockopt(2)
. As per man 7 socket
:
+++Sets or gets the maximum socket send buffer in bytes. The +kernel doubles this value (to allow space for bookkeeping +overhead) when it is set using
+setsockopt(2)
, and this doubled +value is returned bygetsockopt(2)
.
Sets the size of the TCP receive buffer on this socket.
+On most operating systems, this sets the SO_RCVBUF
socket option.
Returns the size of the TCP receive buffer for this socket.
+On most operating systems, this is the value of the SO_RCVBUF
socket
+option.
Note that if set_recv_buffer_size
has been called on this socket
+previously, the value returned by this function may not be the same as
+the argument provided to set_send_buffer_size
. This is for the
+following reasons:
getsockopt(2)
. As per man 7 socket
:
+++Sets or gets the maximum socket send buffer in bytes. The +kernel doubles this value (to allow space for bookkeeping +overhead) when it is set using
+setsockopt(2)
, and this doubled +value is returned bygetsockopt(2)
.
Sets the linger duration of this socket by setting the SO_LINGER
option.
This option controls the action taken when a stream has unsent messages and the stream is
+closed. If SO_LINGER
is set, the system shall block the process until it can transmit the
+data or until the time expires.
If SO_LINGER
is not specified, and the socket is closed, the system handles the call in a
+way that allows the process to continue as quickly as possible.
Reads the linger duration for this socket by getting the SO_LINGER
+option.
For more information about this option, see set_linger
.
Sets the value of the TCP_NODELAY
option on this socket.
If set, this option disables the Nagle algorithm. This means that segments are always +sent as soon as possible, even if there is only a small amount of data. When not set, +data is buffered until there is a sufficient amount to send out, thereby avoiding +the frequent sending of small packets.
+use tokio::net::TcpSocket;
+
+let socket = TcpSocket::new_v4()?;
+
+println!("{:?}", socket.nodelay()?);
Gets the value of the TCP_NODELAY
option on this socket.
For more information about this option, see set_nodelay
.
use tokio::net::TcpSocket;
+
+let stream = TcpSocket::new_v4()?;
+
+stream.set_nodelay(true)?;
Gets the value of the IP_TOS
option for this socket.
For more information about this option, see set_tos
.
NOTE: On Windows, IP_TOS
is only supported on Windows 8+ or
+Windows Server 2012+.
Sets the value for the IP_TOS
option on this socket.
This value sets the type-of-service field that is used in every packet +sent from this socket.
+NOTE: On Windows, IP_TOS
is only supported on Windows 8+ or
+Windows Server 2012+.
Gets the value for the SO_BINDTODEVICE
option on this socket
This value gets the socket binded device’s interface name.
+Sets the value for the SO_BINDTODEVICE
option on this socket
If a socket is bound to an interface, only packets received from that
+particular interface are processed by the socket. Note that this only
+works for some socket types, particularly AF_INET
sockets.
If interface
is None
or an empty string it removes the binding.
Gets the local address of this socket.
+Will fail on windows if called before bind
.
use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ socket.bind(addr)?;
+ assert_eq!(socket.local_addr().unwrap().to_string(), "127.0.0.1:8080");
+ let listener = socket.listen(1024)?;
+ Ok(())
+}
Returns the value of the SO_ERROR
option.
Binds the socket to the given address.
+This calls the bind(2)
operating-system function. Behavior is
+platform specific. Refer to the target platform’s documentation for more
+details.
Bind a socket before listening.
+ +use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ socket.bind(addr)?;
+
+ let listener = socket.listen(1024)?;
+
+ Ok(())
+}
Establishes a TCP connection with a peer at the specified socket address.
+The TcpSocket
is consumed. Once the connection is established, a
+connected TcpStream
is returned. If the connection fails, the
+encountered error is returned.
This calls the connect(2)
operating-system function. Behavior is
+platform specific. Refer to the target platform’s documentation for more
+details.
Connecting to a peer.
+ +use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ let stream = socket.connect(addr).await?;
+
+ Ok(())
+}
Converts the socket into a TcpListener
.
backlog
defines the maximum number of pending connections are queued
+by the operating system at any given time. Connection are removed from
+the queue with TcpListener::accept
. When the queue is full, the
+operating-system will start rejecting connections.
This calls the listen(2)
operating-system function, marking the socket
+as a passive socket. Behavior is platform specific. Refer to the target
+platform’s documentation for more details.
Create a TcpListener
.
use tokio::net::TcpSocket;
+
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let addr = "127.0.0.1:8080".parse().unwrap();
+
+ let socket = TcpSocket::new_v4()?;
+ socket.bind(addr)?;
+
+ let listener = socket.listen(1024)?;
+
+ Ok(())
+}
Converts a std::net::TcpStream
into a TcpSocket
. The provided
+socket must not have been connected prior to calling this function. This
+function is typically used together with crates such as socket2
to
+configure socket options that are not available on TcpSocket
.
The caller is responsible for ensuring that the socket is in
+non-blocking mode. Otherwise all I/O operations on the socket
+will block the thread, which will cause unexpected behavior.
+Non-blocking mode can be set using set_nonblocking
.
use tokio::net::TcpSocket;
+use socket2::{Domain, Socket, Type};
+
+#[tokio::main]
+async fn main() -> std::io::Result<()> {
+ let socket2_socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
+ socket2_socket.set_nonblocking(true)?;
+
+ let socket = TcpSocket::from_std_stream(socket2_socket.into());
+
+ Ok(())
+}
pub struct TcpStream { /* private fields */ }
A TCP stream between a local and a remote socket.
+A TCP stream can either be created by connecting to an endpoint, via the
+connect
method, or by accepting a connection from a listener. A
+TCP stream can also be created via the TcpSocket
type.
Reading and writing to a TcpStream
is usually done using the
+convenience methods found on the AsyncReadExt
and AsyncWriteExt
+traits.
use tokio::net::TcpStream;
+use tokio::io::AsyncWriteExt;
+use std::error::Error;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ // Write some data.
+ stream.write_all(b"hello world!").await?;
+
+ Ok(())
+}
The write_all
method is defined on the AsyncWriteExt
trait.
To shut down the stream in the write direction, you can call the
+shutdown()
method. This will cause the other peer to receive a read of
+length 0, indicating that no more data will be sent. This only closes
+the stream in one direction.
Opens a TCP connection to a remote host.
+addr
is an address of the remote host. Anything which implements the
+ToSocketAddrs
trait can be supplied as the address. If addr
+yields multiple addresses, connect will be attempted with each of the
+addresses until a connection is successful. If none of the addresses
+result in a successful connection, the error returned from the last
+connection attempt (the last address) is returned.
To configure the socket before connecting, you can use the TcpSocket
+type.
use tokio::net::TcpStream;
+use tokio::io::AsyncWriteExt;
+use std::error::Error;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ // Write some data.
+ stream.write_all(b"hello world!").await?;
+
+ Ok(())
+}
The write_all
method is defined on the AsyncWriteExt
trait.
Creates new TcpStream
from a std::net::TcpStream
.
This function is intended to be used to wrap a TCP stream from the +standard library in the Tokio equivalent.
+The caller is responsible for ensuring that the stream is in
+non-blocking mode. Otherwise all I/O operations on the stream
+will block the thread, which will cause unexpected behavior.
+Non-blocking mode can be set using set_nonblocking
.
use std::error::Error;
+use tokio::net::TcpStream;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let std_stream = std::net::TcpStream::connect("127.0.0.1:34254")?;
+ std_stream.set_nonblocking(true)?;
+ let stream = TcpStream::from_std(std_stream)?;
+ Ok(())
+}
This function panics if it is not called from within a runtime with +IO enabled.
+The runtime is usually set implicitly when this function is called
+from a future driven by a tokio runtime, otherwise runtime can be set
+explicitly with Runtime::enter
function.
Turns a tokio::net::TcpStream
into a std::net::TcpStream
.
The returned std::net::TcpStream
will have nonblocking mode set as true
.
+Use set_nonblocking
to change the blocking mode if needed.
use std::error::Error;
+use std::io::Read;
+use tokio::net::TcpListener;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let mut data = [0u8; 12];
+ let listener = TcpListener::bind("127.0.0.1:34254").await?;
+ let (tokio_tcp_stream, _) = listener.accept().await?;
+ let mut std_tcp_stream = tokio_tcp_stream.into_std()?;
+ std_tcp_stream.set_nonblocking(false)?;
+ std_tcp_stream.read_exact(&mut data)?;
+ Ok(())
+}
Returns the local address that this stream is bound to.
+use tokio::net::TcpStream;
+
+let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+println!("{:?}", stream.local_addr()?);
Returns the value of the SO_ERROR
option.
Returns the remote address that this stream is connected to.
+use tokio::net::TcpStream;
+
+let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+println!("{:?}", stream.peer_addr()?);
Attempts to receive data on the socket, without removing that data from +the queue, registering the current task for wakeup if data is not yet +available.
+Note that on multiple calls to poll_peek
, poll_read
or
+poll_read_ready
, only the Waker
from the Context
passed to the
+most recent call is scheduled to receive a wakeup. (However,
+poll_write
retains a second, independent waker.)
The function returns:
+Poll::Pending
if data is not yet available.Poll::Ready(Ok(n))
if data is available. n
is the number of bytes peeked.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
use tokio::io::{self, ReadBuf};
+use tokio::net::TcpStream;
+
+use futures::future::poll_fn;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let stream = TcpStream::connect("127.0.0.1:8000").await?;
+ let mut buf = [0; 10];
+ let mut buf = ReadBuf::new(&mut buf);
+
+ poll_fn(|cx| {
+ stream.poll_peek(cx, &mut buf)
+ }).await?;
+
+ Ok(())
+}
Waits for any of the requested ready states.
+This function is usually paired with try_read()
or try_write()
. It
+can be used to concurrently read / write to the same socket on a single
+task without splitting the socket.
The function may complete without the socket being ready. This is a
+false-positive and attempting an operation will return with
+io::ErrorKind::WouldBlock
. The function can also return with an empty
+Ready
set, so you should always check the returned value and possibly
+wait again if the requested states are not set.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read or write that fails with WouldBlock
or
+Poll::Pending
.
Concurrently read and write to the stream on the same task without +splitting.
+ +use tokio::io::Interest;
+use tokio::net::TcpStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ loop {
+ let ready = stream.ready(Interest::READABLE | Interest::WRITABLE).await?;
+
+ if ready.is_readable() {
+ let mut data = vec![0; 1024];
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read(&mut data) {
+ Ok(n) => {
+ println!("read {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+
+ }
+
+ if ready.is_writable() {
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write(b"hello world") {
+ Ok(n) => {
+ println!("write {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+ }
+}
Waits for the socket to become readable.
+This function is equivalent to ready(Interest::READABLE)
and is usually
+paired with try_read()
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::TcpStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ let mut msg = vec![0; 1024];
+
+ loop {
+ // Wait for the socket to be readable
+ stream.readable().await?;
+
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read(&mut msg) {
+ Ok(n) => {
+ msg.truncate(n);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ println!("GOT = {:?}", msg);
+ Ok(())
+}
Polls for read readiness.
+If the tcp stream is not currently ready for reading, this method will
+store a clone of the Waker
from the provided Context
. When the tcp
+stream becomes ready for reading, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_read_ready
, poll_read
or
+poll_peek
, only the Waker
from the Context
passed to the most
+recent call is scheduled to receive a wakeup. (However,
+poll_write_ready
retains a second, independent waker.)
This function is intended for cases where creating and pinning a future
+via readable
is not feasible. Where possible, using readable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the tcp stream is not ready for reading.Poll::Ready(Ok(()))
if the tcp stream is ready for reading.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Tries to read data from the stream into the provided buffer, returning how +many bytes were read.
+Receives any pending data from the socket but does not wait for new data
+to arrive. On success, returns the number of bytes read. Because
+try_read()
is non-blocking, the buffer does not have to be stored by
+the async task and can exist entirely on the stack.
Usually, readable()
or ready()
is used with this function.
If data is successfully read, Ok(n)
is returned, where n
is the
+number of bytes read. If n
is 0
, then it can indicate one of two scenarios:
If the stream is not ready to read data,
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::TcpStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ loop {
+ // Wait for the socket to be readable
+ stream.readable().await?;
+
+ // Creating the buffer **after** the `await` prevents it from
+ // being stored in the async task.
+ let mut buf = [0; 4096];
+
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read(&mut buf) {
+ Ok(0) => break,
+ Ok(n) => {
+ println!("read {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to read data from the stream into the provided buffers, returning +how many bytes were read.
+Data is copied to fill each buffer in order, with the final buffer
+written to possibly being only partially filled. This method behaves
+equivalently to a single call to try_read()
with concatenated
+buffers.
Receives any pending data from the socket but does not wait for new data
+to arrive. On success, returns the number of bytes read. Because
+try_read_vectored()
is non-blocking, the buffer does not have to be
+stored by the async task and can exist entirely on the stack.
Usually, readable()
or ready()
is used with this function.
If data is successfully read, Ok(n)
is returned, where n
is the
+number of bytes read. Ok(0)
indicates the stream’s read half is closed
+and will no longer yield data. If the stream is not ready to read data
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::TcpStream;
+use std::error::Error;
+use std::io::{self, IoSliceMut};
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ loop {
+ // Wait for the socket to be readable
+ stream.readable().await?;
+
+ // Creating the buffer **after** the `await` prevents it from
+ // being stored in the async task.
+ let mut buf_a = [0; 512];
+ let mut buf_b = [0; 1024];
+ let mut bufs = [
+ IoSliceMut::new(&mut buf_a),
+ IoSliceMut::new(&mut buf_b),
+ ];
+
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read_vectored(&mut bufs) {
+ Ok(0) => break,
+ Ok(n) => {
+ println!("read {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Waits for the socket to become writable.
+This function is equivalent to ready(Interest::WRITABLE)
and is usually
+paired with try_write()
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to write that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::TcpStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ loop {
+ // Wait for the socket to be writable
+ stream.writable().await?;
+
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Polls for write readiness.
+If the tcp stream is not currently ready for writing, this method will
+store a clone of the Waker
from the provided Context
. When the tcp
+stream becomes ready for writing, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_write_ready
or poll_write
, only
+the Waker
from the Context
passed to the most recent call is
+scheduled to receive a wakeup. (However, poll_read_ready
retains a
+second, independent waker.)
This function is intended for cases where creating and pinning a future
+via writable
is not feasible. Where possible, using writable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the tcp stream is not ready for writing.Poll::Ready(Ok(()))
if the tcp stream is ready for writing.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Try to write a buffer to the stream, returning how many bytes were +written.
+The function will attempt to write the entire contents of buf
, but
+only part of the buffer may be written.
This function is usually paired with writable()
.
If data is successfully written, Ok(n)
is returned, where n
is the
+number of bytes written. If the stream is not ready to write data,
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::TcpStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ loop {
+ // Wait for the socket to be writable
+ stream.writable().await?;
+
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to write several buffers to the stream, returning how many bytes +were written.
+Data is written from each buffer in order, with the final buffer read
+from possible being only partially consumed. This method behaves
+equivalently to a single call to try_write()
with concatenated
+buffers.
This function is usually paired with writable()
.
If data is successfully written, Ok(n)
is returned, where n
is the
+number of bytes written. If the stream is not ready to write data,
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::TcpStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ let bufs = [io::IoSlice::new(b"hello "), io::IoSlice::new(b"world")];
+
+ loop {
+ // Wait for the socket to be writable
+ stream.writable().await?;
+
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write_vectored(&bufs) {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to read or write from the socket using a user-provided IO operation.
+If the socket is ready, the provided closure is called. The closure
+should attempt to perform IO operation on the socket by manually
+calling the appropriate syscall. If the operation fails because the
+socket is not actually ready, then the closure should return a
+WouldBlock
error and the readiness flag is cleared. The return value
+of the closure is then returned by try_io
.
If the socket is not ready, then the closure is not called
+and a WouldBlock
error is returned.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio TcpStream
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Usually, readable()
, writable()
or ready()
is used with this function.
Reads or writes from the socket using a user-provided IO operation.
+The readiness of the socket is awaited and when the socket is ready,
+the provided closure is called. The closure should attempt to perform
+IO operation on the socket by manually calling the appropriate syscall.
+If the operation fails because the socket is not actually ready,
+then the closure should return a WouldBlock
error. In such case the
+readiness flag is cleared and the socket readiness is awaited again.
+This loop is repeated until the closure returns an Ok
or an error
+other than WouldBlock
.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio TcpStream
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Receives data on the socket from the remote address to which it is +connected, without removing that data from the queue. On success, +returns the number of bytes peeked.
+Successive calls return the same data. This is accomplished by passing
+MSG_PEEK
as a flag to the underlying recv
system call.
use tokio::net::TcpStream;
+use tokio::io::AsyncReadExt;
+use std::error::Error;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+ let mut b1 = [0; 10];
+ let mut b2 = [0; 10];
+
+ // Peek at the data
+ let n = stream.peek(&mut b1).await?;
+
+ // Read the data
+ assert_eq!(n, stream.read(&mut b2[..n]).await?);
+ assert_eq!(&b1[..n], &b2[..n]);
+
+ Ok(())
+}
The read
method is defined on the AsyncReadExt
trait.
Gets the value of the TCP_NODELAY
option on this socket.
For more information about this option, see set_nodelay
.
use tokio::net::TcpStream;
+
+let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+println!("{:?}", stream.nodelay()?);
Sets the value of the TCP_NODELAY
option on this socket.
If set, this option disables the Nagle algorithm. This means that +segments are always sent as soon as possible, even if there is only a +small amount of data. When not set, data is buffered until there is a +sufficient amount to send out, thereby avoiding the frequent sending of +small packets.
+use tokio::net::TcpStream;
+
+let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+stream.set_nodelay(true)?;
Reads the linger duration for this socket by getting the SO_LINGER
+option.
For more information about this option, see set_linger
.
use tokio::net::TcpStream;
+
+let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+println!("{:?}", stream.linger()?);
Sets the linger duration of this socket by setting the SO_LINGER
option.
This option controls the action taken when a stream has unsent messages and the stream is
+closed. If SO_LINGER
is set, the system shall block the process until it can transmit the
+data or until the time expires.
If SO_LINGER
is not specified, and the stream is closed, the system handles the call in a
+way that allows the process to continue as quickly as possible.
use tokio::net::TcpStream;
+
+let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+stream.set_linger(None)?;
Sets the value for the IP_TTL
option on this socket.
This value sets the time-to-live field that is used in every packet sent +from this socket.
+use tokio::net::TcpStream;
+
+let stream = TcpStream::connect("127.0.0.1:8080").await?;
+
+stream.set_ttl(123)?;
Splits a TcpStream
into a read half and a write half, which can be used
+to read and write the stream concurrently.
This method is more efficient than into_split
, but the halves cannot be
+moved into independently spawned tasks.
Splits a TcpStream
into a read half and a write half, which can be used
+to read and write the stream concurrently.
Unlike split
, the owned halves can be moved to separate tasks, however
+this comes at the cost of a heap allocation.
Note: Dropping the write half will shut down the write half of the TCP
+stream. This is equivalent to calling shutdown()
on the TcpStream
.
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read morepub struct UdpSocket { /* private fields */ }
A UDP socket.
+UDP is “connectionless”, unlike TCP. Meaning, regardless of what address you’ve bound to, a UdpSocket
+is free to communicate with many different remotes. In tokio there are basically two main ways to use UdpSocket
:
bind
and use send_to
+and recv_from
to communicate with many different addressesconnect
and associate with a single address, using send
+and recv
to communicate only with that remote addressThis type does not provide a split
method, because this functionality
+can be achieved by instead wrapping the socket in an Arc
. Note that
+you do not need a Mutex
to share the UdpSocket
— an Arc<UdpSocket>
+is enough. This is because all of the methods take &self
instead of
+&mut self
. Once you have wrapped it in an Arc
, you can call
+.clone()
on the Arc<UdpSocket>
to get multiple shared handles to the
+same socket. An example of such usage can be found further down.
If you need to listen over UDP and produce a Stream
, you can look
+at UdpFramed
.
Using bind
we can create a simple echo server that sends and recv’s with many different clients:
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let sock = UdpSocket::bind("0.0.0.0:8080").await?;
+ let mut buf = [0; 1024];
+ loop {
+ let (len, addr) = sock.recv_from(&mut buf).await?;
+ println!("{:?} bytes received from {:?}", len, addr);
+
+ let len = sock.send_to(&buf[..len], addr).await?;
+ println!("{:?} bytes sent", len);
+ }
+}
Or using connect
we can echo with a single remote address using send
and recv
:
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let sock = UdpSocket::bind("0.0.0.0:8080").await?;
+
+ let remote_addr = "127.0.0.1:59611";
+ sock.connect(remote_addr).await?;
+ let mut buf = [0; 1024];
+ loop {
+ let len = sock.recv(&mut buf).await?;
+ println!("{:?} bytes received from {:?}", len, remote_addr);
+
+ let len = sock.send(&buf[..len]).await?;
+ println!("{:?} bytes sent", len);
+ }
+}
Arc
Because send_to
and recv_from
take &self
. It’s perfectly alright
+to use an Arc<UdpSocket>
and share the references to multiple tasks.
+Here is a similar “echo” example that supports concurrent
+sending/receiving:
use tokio::{net::UdpSocket, sync::mpsc};
+use std::{io, net::SocketAddr, sync::Arc};
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let sock = UdpSocket::bind("0.0.0.0:8080".parse::<SocketAddr>().unwrap()).await?;
+ let r = Arc::new(sock);
+ let s = r.clone();
+ let (tx, mut rx) = mpsc::channel::<(Vec<u8>, SocketAddr)>(1_000);
+
+ tokio::spawn(async move {
+ while let Some((bytes, addr)) = rx.recv().await {
+ let len = s.send_to(&bytes, &addr).await.unwrap();
+ println!("{:?} bytes sent", len);
+ }
+ });
+
+ let mut buf = [0; 1024];
+ loop {
+ let (len, addr) = r.recv_from(&mut buf).await?;
+ println!("{:?} bytes received from {:?}", len, addr);
+ tx.send((buf[..len].to_vec(), addr)).await.unwrap();
+ }
+}
This function will create a new UDP socket and attempt to bind it to
+the addr
provided.
Binding with a port number of 0 will request that the OS assigns a port
+to this listener. The port allocated can be queried via the local_addr
+method.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let sock = UdpSocket::bind("0.0.0.0:8080").await?;
+ // use `sock`
+ Ok(())
+}
Creates new UdpSocket
from a previously bound std::net::UdpSocket
.
This function is intended to be used to wrap a UDP socket from the +standard library in the Tokio equivalent.
+This can be used in conjunction with socket2
’s Socket
interface to
+configure a socket before it’s handed off, such as setting options like
+reuse_address
or binding to multiple addresses.
The caller is responsible for ensuring that the socket is in
+non-blocking mode. Otherwise all I/O operations on the socket
+will block the thread, which will cause unexpected behavior.
+Non-blocking mode can be set using set_nonblocking
.
This function panics if thread-local runtime is not set.
+The runtime is usually set implicitly when this function is called
+from a future driven by a tokio runtime, otherwise runtime can be set
+explicitly with Runtime::enter
function.
use tokio::net::UdpSocket;
+
+let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
+let std_sock = std::net::UdpSocket::bind(addr)?;
+std_sock.set_nonblocking(true)?;
+let sock = UdpSocket::from_std(std_sock)?;
+// use `sock`
Turns a tokio::net::UdpSocket
into a std::net::UdpSocket
.
The returned std::net::UdpSocket
will have nonblocking mode set as
+true
. Use set_nonblocking
to change the blocking mode if needed.
use std::error::Error;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let tokio_socket = tokio::net::UdpSocket::bind("127.0.0.1:0").await?;
+ let std_socket = tokio_socket.into_std()?;
+ std_socket.set_nonblocking(false)?;
+ Ok(())
+}
Returns the local address that this socket is bound to.
+use tokio::net::UdpSocket;
+
+let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
+let sock = UdpSocket::bind(addr).await?;
+// the address the socket is bound to
+let local_addr = sock.local_addr()?;
Returns the socket address of the remote peer this socket was connected to.
+use tokio::net::UdpSocket;
+
+let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
+let peer = "127.0.0.1:11100".parse::<SocketAddr>().unwrap();
+let sock = UdpSocket::bind(addr).await?;
+sock.connect(peer).await?;
+assert_eq!(peer, sock.peer_addr()?);
Connects the UDP socket setting the default destination for send() and
+limiting packets that are read via recv
from the address specified in
+addr
.
use tokio::net::UdpSocket;
+
+let sock = UdpSocket::bind("0.0.0.0:8080".parse::<SocketAddr>().unwrap()).await?;
+
+let remote_addr = "127.0.0.1:59600".parse::<SocketAddr>().unwrap();
+sock.connect(remote_addr).await?;
+let mut buf = [0u8; 32];
+// recv from remote_addr
+let len = sock.recv(&mut buf).await?;
+// send to remote_addr
+let _len = sock.send(&buf[..len]).await?;
Waits for any of the requested ready states.
+This function is usually paired with try_recv()
or try_send()
. It
+can be used to concurrently recv
/ send
to the same socket on a single
+task without splitting the socket.
The function may complete without the socket being ready. This is a
+false-positive and attempting an operation will return with
+io::ErrorKind::WouldBlock
. The function can also return with an empty
+Ready
set, so you should always check the returned value and possibly
+wait again if the requested states are not set.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read or write that fails with WouldBlock
or
+Poll::Pending
.
Concurrently receive from and send to the socket on the same task +without splitting.
+ +use tokio::io::{self, Interest};
+use tokio::net::UdpSocket;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+ socket.connect("127.0.0.1:8081").await?;
+
+ loop {
+ let ready = socket.ready(Interest::READABLE | Interest::WRITABLE).await?;
+
+ if ready.is_readable() {
+ // The buffer is **not** included in the async task and will only exist
+ // on the stack.
+ let mut data = [0; 1024];
+ match socket.try_recv(&mut data[..]) {
+ Ok(n) => {
+ println!("received {:?}", &data[..n]);
+ }
+ // False-positive, continue
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ if ready.is_writable() {
+ // Write some data
+ match socket.try_send(b"hello world") {
+ Ok(n) => {
+ println!("sent {} bytes", n);
+ }
+ // False-positive, continue
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+ }
+}
Waits for the socket to become writable.
+This function is equivalent to ready(Interest::WRITABLE)
and is
+usually paired with try_send()
or try_send_to()
.
The function may complete without the socket being writable. This is a
+false-positive and attempting a try_send()
will return with
+io::ErrorKind::WouldBlock
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to write that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Bind socket
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+ socket.connect("127.0.0.1:8081").await?;
+
+ loop {
+ // Wait for the socket to be writable
+ socket.writable().await?;
+
+ // Try to send data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_send(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Polls for write/send readiness.
+If the udp stream is not currently ready for sending, this method will
+store a clone of the Waker
from the provided Context
. When the udp
+stream becomes ready for sending, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_send_ready
or poll_send
, only
+the Waker
from the Context
passed to the most recent call is
+scheduled to receive a wakeup. (However, poll_recv_ready
retains a
+second, independent waker.)
This function is intended for cases where creating and pinning a future
+via writable
is not feasible. Where possible, using writable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the udp stream is not ready for writing.Poll::Ready(Ok(()))
if the udp stream is ready for writing.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Sends data on the socket to the remote address that the socket is +connected to.
+The connect
method will connect this socket to a remote address.
+This method will fail if the socket is not connected.
On success, the number of bytes sent is returned, otherwise, the +encountered error is returned.
+This method is cancel safe. If send
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, then it is guaranteed that the message was not sent.
use tokio::io;
+use tokio::net::UdpSocket;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Bind socket
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+ socket.connect("127.0.0.1:8081").await?;
+
+ // Send a message
+ socket.send(b"hello world").await?;
+
+ Ok(())
+}
Attempts to send data on the socket to the remote address to which it
+was previously connect
ed.
The connect
method will connect this socket to a remote address.
+This method will fail if the socket is not connected.
Note that on multiple calls to a poll_*
method in the send direction,
+only the Waker
from the Context
passed to the most recent call will
+be scheduled to receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not available to writePoll::Ready(Ok(n))
n
is the number of bytes sentPoll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Tries to send data on the socket to the remote address to which it is +connected.
+When the socket buffer is full, Err(io::ErrorKind::WouldBlock)
is
+returned. This function is usually paired with writable()
.
If successful, Ok(n)
is returned, where n
is the number of bytes
+sent. If the socket is not ready to send data,
+Err(ErrorKind::WouldBlock)
is returned.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Bind a UDP socket
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+
+ // Connect to a peer
+ socket.connect("127.0.0.1:8081").await?;
+
+ loop {
+ // Wait for the socket to be writable
+ socket.writable().await?;
+
+ // Try to send data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_send(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Waits for the socket to become readable.
+This function is equivalent to ready(Interest::READABLE)
and is usually
+paired with try_recv()
.
The function may complete without the socket being readable. This is a
+false-positive and attempting a try_recv()
will return with
+io::ErrorKind::WouldBlock
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Connect to a peer
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+ socket.connect("127.0.0.1:8081").await?;
+
+ loop {
+ // Wait for the socket to be readable
+ socket.readable().await?;
+
+ // The buffer is **not** included in the async task and will
+ // only exist on the stack.
+ let mut buf = [0; 1024];
+
+ // Try to recv data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_recv(&mut buf) {
+ Ok(n) => {
+ println!("GOT {:?}", &buf[..n]);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Polls for read/receive readiness.
+If the udp stream is not currently ready for receiving, this method will
+store a clone of the Waker
from the provided Context
. When the udp
+socket becomes ready for reading, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_recv_ready
, poll_recv
or
+poll_peek
, only the Waker
from the Context
passed to the most
+recent call is scheduled to receive a wakeup. (However,
+poll_send_ready
retains a second, independent waker.)
This function is intended for cases where creating and pinning a future
+via readable
is not feasible. Where possible, using readable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the udp stream is not ready for reading.Poll::Ready(Ok(()))
if the udp stream is ready for reading.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Receives a single datagram message on the socket from the remote address +to which it is connected. On success, returns the number of bytes read.
+The function must be called with valid byte array buf
of sufficient
+size to hold the message bytes. If a message is too long to fit in the
+supplied buffer, excess bytes may be discarded.
The connect
method will connect this socket to a remote address.
+This method will fail if the socket is not connected.
This method is cancel safe. If recv
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, it is guaranteed that no messages were received on this
+socket.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Bind socket
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+ socket.connect("127.0.0.1:8081").await?;
+
+ let mut buf = vec![0; 10];
+ let n = socket.recv(&mut buf).await?;
+
+ println!("received {} bytes {:?}", n, &buf[..n]);
+
+ Ok(())
+}
Attempts to receive a single datagram message on the socket from the remote
+address to which it is connect
ed.
The connect
method will connect this socket to a remote address. This method
+resolves to an error if the socket is not connected.
Note that on multiple calls to a poll_*
method in the recv
direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not ready to readPoll::Ready(Ok(()))
reads data ReadBuf
if the socket is readyPoll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Tries to receive a single datagram message on the socket from the remote +address to which it is connected. On success, returns the number of +bytes read.
+This method must be called with valid byte array buf
of sufficient size
+to hold the message bytes. If a message is too long to fit in the
+supplied buffer, excess bytes may be discarded.
When there is no pending data, Err(io::ErrorKind::WouldBlock)
is
+returned. This function is usually paired with readable()
.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Connect to a peer
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+ socket.connect("127.0.0.1:8081").await?;
+
+ loop {
+ // Wait for the socket to be readable
+ socket.readable().await?;
+
+ // The buffer is **not** included in the async task and will
+ // only exist on the stack.
+ let mut buf = [0; 1024];
+
+ // Try to recv data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_recv(&mut buf) {
+ Ok(n) => {
+ println!("GOT {:?}", &buf[..n]);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Sends data on the socket to the given address. On success, returns the +number of bytes written.
+Address type can be any implementor of ToSocketAddrs
trait. See its
+documentation for concrete examples.
It is possible for addr
to yield multiple addresses, but send_to
+will only send data to the first address yielded by addr
.
This will return an error when the IP version of the local socket does
+not match that returned from ToSocketAddrs
.
This method is cancel safe. If send_to
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, then it is guaranteed that the message was not sent.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+ let len = socket.send_to(b"hello world", "127.0.0.1:8081").await?;
+
+ println!("Sent {} bytes", len);
+
+ Ok(())
+}
Attempts to send data on the socket to a given address.
+Note that on multiple calls to a poll_*
method in the send direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not ready to writePoll::Ready(Ok(n))
n
is the number of bytes sent.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Tries to send data on the socket to the given address, but if the send is +blocked this will return right away.
+This function is usually paired with writable()
.
If successful, returns the number of bytes sent
+Users should ensure that when the remote cannot receive, the
+ErrorKind::WouldBlock
is properly handled. An error can also occur
+if the IP version of the socket does not match that of target
.
use tokio::net::UdpSocket;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+
+ let dst = "127.0.0.1:8081".parse()?;
+
+ loop {
+ socket.writable().await?;
+
+ match socket.try_send_to(&b"hello world"[..], dst) {
+ Ok(sent) => {
+ println!("sent {} bytes", sent);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ // Writable false positive.
+ continue;
+ }
+ Err(e) => return Err(e.into()),
+ }
+ }
+
+ Ok(())
+}
Receives a single datagram message on the socket. On success, returns +the number of bytes read and the origin.
+The function must be called with valid byte array buf
of sufficient
+size to hold the message bytes. If a message is too long to fit in the
+supplied buffer, excess bytes may be discarded.
This method is cancel safe. If recv_from
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, it is guaranteed that no messages were received on this
+socket.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+
+ let mut buf = vec![0u8; 32];
+ let (len, addr) = socket.recv_from(&mut buf).await?;
+
+ println!("received {:?} bytes from {:?}", len, addr);
+
+ Ok(())
+}
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+Attempts to receive a single datagram on the socket.
+Note that on multiple calls to a poll_*
method in the recv
direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not ready to readPoll::Ready(Ok(addr))
reads data from addr
into ReadBuf
if the socket is readyPoll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+Tries to receive a single datagram message on the socket. On success, +returns the number of bytes read and the origin.
+This method must be called with valid byte array buf
of sufficient size
+to hold the message bytes. If a message is too long to fit in the
+supplied buffer, excess bytes may be discarded.
When there is no pending data, Err(io::ErrorKind::WouldBlock)
is
+returned. This function is usually paired with readable()
.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Connect to a peer
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+
+ loop {
+ // Wait for the socket to be readable
+ socket.readable().await?;
+
+ // The buffer is **not** included in the async task and will
+ // only exist on the stack.
+ let mut buf = [0; 1024];
+
+ // Try to recv data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_recv_from(&mut buf) {
+ Ok((n, _addr)) => {
+ println!("GOT {:?}", &buf[..n]);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to read or write from the socket using a user-provided IO operation.
+If the socket is ready, the provided closure is called. The closure
+should attempt to perform IO operation on the socket by manually
+calling the appropriate syscall. If the operation fails because the
+socket is not actually ready, then the closure should return a
+WouldBlock
error and the readiness flag is cleared. The return value
+of the closure is then returned by try_io
.
If the socket is not ready, then the closure is not called
+and a WouldBlock
error is returned.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio UdpSocket
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Usually, readable()
, writable()
or ready()
is used with this function.
Reads or writes from the socket using a user-provided IO operation.
+The readiness of the socket is awaited and when the socket is ready,
+the provided closure is called. The closure should attempt to perform
+IO operation on the socket by manually calling the appropriate syscall.
+If the operation fails because the socket is not actually ready,
+then the closure should return a WouldBlock
error. In such case the
+readiness flag is cleared and the socket readiness is awaited again.
+This loop is repeated until the closure returns an Ok
or an error
+other than WouldBlock
.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio UdpSocket
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Receives data from the socket, without removing it from the input queue. +On success, returns the number of bytes read and the address from whence +the data came.
+On Windows, if the data is larger than the buffer specified, the buffer
+is filled with the first part of the data, and peek_from
returns the error
+WSAEMSGSIZE(10040)
. The excess data is lost.
+Make sure to always use a sufficiently large buffer to hold the
+maximum UDP packet size, which can be up to 65536 bytes in size.
MacOS will return an error if you pass a zero-sized buffer.
+If you’re merely interested in learning the sender of the data at the head of the queue,
+try peek_sender
.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+
+ let mut buf = vec![0u8; 32];
+ let (len, addr) = socket.peek_from(&mut buf).await?;
+
+ println!("peeked {:?} bytes from {:?}", len, addr);
+
+ Ok(())
+}
Receives data from the socket, without removing it from the input queue. +On success, returns the sending address of the datagram.
+Note that on multiple calls to a poll_*
method in the recv
direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup
On Windows, if the data is larger than the buffer specified, the buffer
+is filled with the first part of the data, and peek returns the error
+WSAEMSGSIZE(10040)
. The excess data is lost.
+Make sure to always use a sufficiently large buffer to hold the
+maximum UDP packet size, which can be up to 65536 bytes in size.
MacOS will return an error if you pass a zero-sized buffer.
+If you’re merely interested in learning the sender of the data at the head of the queue,
+try poll_peek_sender
.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+The function returns:
+Poll::Pending
if the socket is not ready to readPoll::Ready(Ok(addr))
reads data from addr
into ReadBuf
if the socket is readyPoll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Tries to receive data on the socket without removing it from the input queue. +On success, returns the number of bytes read and the sending address of the +datagram.
+When there is no pending data, Err(io::ErrorKind::WouldBlock)
is
+returned. This function is usually paired with readable()
.
On Windows, if the data is larger than the buffer specified, the buffer
+is filled with the first part of the data, and peek returns the error
+WSAEMSGSIZE(10040)
. The excess data is lost.
+Make sure to always use a sufficiently large buffer to hold the
+maximum UDP packet size, which can be up to 65536 bytes in size.
MacOS will return an error if you pass a zero-sized buffer.
+If you’re merely interested in learning the sender of the data at the head of the queue,
+try try_peek_sender
.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+Retrieve the sender of the data at the head of the input queue, waiting if empty.
+This is equivalent to calling peek_from
with a zero-sized buffer,
+but suppresses the WSAEMSGSIZE
error on Windows and the “invalid argument” error on macOS.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+Retrieve the sender of the data at the head of the input queue, +scheduling a wakeup if empty.
+This is equivalent to calling poll_peek_from
with a zero-sized buffer,
+but suppresses the WSAEMSGSIZE
error on Windows and the “invalid argument” error on macOS.
Note that on multiple calls to a poll_*
method in the recv
direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+Try to retrieve the sender of the data at the head of the input queue.
+When there is no pending data, Err(io::ErrorKind::WouldBlock)
is
+returned. This function is usually paired with readable()
.
Note that the socket address cannot be implicitly trusted, because it is relatively +trivial to send a UDP datagram with a spoofed origin in a packet injection attack. +Because UDP is stateless and does not validate the origin of a packet, +the attacker does not need to be able to intercept traffic in order to interfere. +It is important to be aware of this when designing your application-level protocol.
+Gets the value of the SO_BROADCAST
option for this socket.
For more information about this option, see set_broadcast
.
Sets the value of the SO_BROADCAST
option for this socket.
When enabled, this socket is allowed to send packets to a broadcast +address.
+Gets the value of the IP_MULTICAST_LOOP
option for this socket.
For more information about this option, see set_multicast_loop_v4
.
Sets the value of the IP_MULTICAST_LOOP
option for this socket.
If enabled, multicast packets will be looped back to the local socket.
+This may not have any affect on IPv6 sockets.
+Gets the value of the IP_MULTICAST_TTL
option for this socket.
For more information about this option, see set_multicast_ttl_v4
.
Sets the value of the IP_MULTICAST_TTL
option for this socket.
Indicates the time-to-live value of outgoing multicast packets for +this socket. The default value is 1 which means that multicast packets +don’t leave the local network unless explicitly requested.
+This may not have any affect on IPv6 sockets.
+Gets the value of the IPV6_MULTICAST_LOOP
option for this socket.
For more information about this option, see set_multicast_loop_v6
.
Sets the value of the IPV6_MULTICAST_LOOP
option for this socket.
Controls whether this socket sees the multicast packets it sends itself.
+This may not have any affect on IPv4 sockets.
+Sets the value for the IP_TTL
option on this socket.
This value sets the time-to-live field that is used in every packet sent +from this socket.
+use tokio::net::UdpSocket;
+
+let sock = UdpSocket::bind("127.0.0.1:8080").await?;
+sock.set_ttl(60)?;
+
Gets the value of the IP_TOS
option for this socket.
For more information about this option, see set_tos
.
NOTE: On Windows, IP_TOS
is only supported on Windows 8+ or
+Windows Server 2012+.
Sets the value for the IP_TOS
option on this socket.
This value sets the type-of-service field that is used in every packet +sent from this socket.
+NOTE: On Windows, IP_TOS
is only supported on Windows 8+ or
+Windows Server 2012+.
Gets the value for the SO_BINDTODEVICE
option on this socket
This value gets the socket-bound device’s interface name.
+Sets the value for the SO_BINDTODEVICE
option on this socket
If a socket is bound to an interface, only packets received from that
+particular interface are processed by the socket. Note that this only
+works for some socket types, particularly AF_INET
sockets.
If interface
is None
or an empty string it removes the binding.
Executes an operation of the IP_ADD_MEMBERSHIP
type.
This function specifies a new multicast group for this socket to join.
+The address must be a valid multicast address, and interface
is the
+address of the local interface with which the system should join the
+multicast group. If it’s equal to INADDR_ANY
then an appropriate
+interface is chosen by the system.
Executes an operation of the IPV6_ADD_MEMBERSHIP
type.
This function specifies a new multicast group for this socket to join.
+The address must be a valid multicast address, and interface
is the
+index of the interface to join/leave (or 0 to indicate any interface).
Executes an operation of the IP_DROP_MEMBERSHIP
type.
For more information about this option, see join_multicast_v4
.
Executes an operation of the IPV6_DROP_MEMBERSHIP
type.
For more information about this option, see join_multicast_v6
.
Returns the value of the SO_ERROR
option.
use tokio::net::UdpSocket;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Create a socket
+ let socket = UdpSocket::bind("0.0.0.0:8080").await?;
+
+ if let Ok(Some(err)) = socket.take_error() {
+ println!("Got error: {:?}", err);
+ }
+
+ Ok(())
+}
pub struct UnixDatagram { /* private fields */ }
An I/O object representing a Unix datagram socket.
+A socket can be either named (associated with a filesystem path) or +unnamed.
+This type does not provide a split
method, because this functionality
+can be achieved by wrapping the socket in an Arc
. Note that you do
+not need a Mutex
to share the UnixDatagram
— an Arc<UnixDatagram>
+is enough. This is because all of the methods take &self
instead of
+&mut self
.
Note: named sockets are persisted even after the object is dropped +and the program has exited, and cannot be reconnected. It is advised +that you either check for and unlink the existing socket if it exists, +or use a temporary file that is guaranteed to not already exist.
+Using named sockets, associated with a filesystem path:
+ +use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// We use a temporary directory so that the socket
+// files left by the bound sockets will get cleaned up.
+let tmp = tempdir()?;
+
+// Bind each socket to a filesystem path
+let tx_path = tmp.path().join("tx");
+let tx = UnixDatagram::bind(&tx_path)?;
+let rx_path = tmp.path().join("rx");
+let rx = UnixDatagram::bind(&rx_path)?;
+
+let bytes = b"hello world";
+tx.send_to(bytes, &rx_path).await?;
+
+let mut buf = vec![0u8; 24];
+let (size, addr) = rx.recv_from(&mut buf).await?;
+
+let dgram = &buf[..size];
+assert_eq!(dgram, bytes);
+assert_eq!(addr.as_pathname().unwrap(), &tx_path);
+
Using unnamed sockets, created as a pair
+ +use tokio::net::UnixDatagram;
+
+// Create the pair of sockets
+let (sock1, sock2) = UnixDatagram::pair()?;
+
+// Since the sockets are paired, the paired send/recv
+// functions can be used
+let bytes = b"hello world";
+sock1.send(bytes).await?;
+
+let mut buff = vec![0u8; 24];
+let size = sock2.recv(&mut buff).await?;
+
+let dgram = &buff[..size];
+assert_eq!(dgram, bytes);
+
Waits for any of the requested ready states.
+This function is usually paired with try_recv()
or try_send()
. It
+can be used to concurrently recv
/ send
to the same socket on a single
+task without splitting the socket.
The function may complete without the socket being ready. This is a
+false-positive and attempting an operation will return with
+io::ErrorKind::WouldBlock
. The function can also return with an empty
+Ready
set, so you should always check the returned value and possibly
+wait again if the requested states are not set.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read or write that fails with WouldBlock
or
+Poll::Pending
.
Concurrently receive from and send to the socket on the same task +without splitting.
+ +use tokio::io::Interest;
+use tokio::net::UnixDatagram;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let dir = tempfile::tempdir().unwrap();
+ let client_path = dir.path().join("client.sock");
+ let server_path = dir.path().join("server.sock");
+ let socket = UnixDatagram::bind(&client_path)?;
+ socket.connect(&server_path)?;
+
+ loop {
+ let ready = socket.ready(Interest::READABLE | Interest::WRITABLE).await?;
+
+ if ready.is_readable() {
+ let mut data = [0; 1024];
+ match socket.try_recv(&mut data[..]) {
+ Ok(n) => {
+ println!("received {:?}", &data[..n]);
+ }
+ // False-positive, continue
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ if ready.is_writable() {
+ // Write some data
+ match socket.try_send(b"hello world") {
+ Ok(n) => {
+ println!("sent {} bytes", n);
+ }
+ // False-positive, continue
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+ }
+}
Waits for the socket to become writable.
+This function is equivalent to ready(Interest::WRITABLE)
and is
+usually paired with try_send()
or try_send_to()
.
The function may complete without the socket being writable. This is a
+false-positive and attempting a try_send()
will return with
+io::ErrorKind::WouldBlock
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to write that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::UnixDatagram;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let dir = tempfile::tempdir().unwrap();
+ let client_path = dir.path().join("client.sock");
+ let server_path = dir.path().join("server.sock");
+ let socket = UnixDatagram::bind(&client_path)?;
+ socket.connect(&server_path)?;
+
+ loop {
+ // Wait for the socket to be writable
+ socket.writable().await?;
+
+ // Try to send data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_send(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Polls for write/send readiness.
+If the socket is not currently ready for sending, this method will
+store a clone of the Waker
from the provided Context
. When the socket
+becomes ready for sending, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_send_ready
or poll_send
, only
+the Waker
from the Context
passed to the most recent call is
+scheduled to receive a wakeup. (However, poll_recv_ready
retains a
+second, independent waker.)
This function is intended for cases where creating and pinning a future
+via writable
is not feasible. Where possible, using writable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the socket is not ready for writing.Poll::Ready(Ok(()))
if the socket is ready for writing.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Waits for the socket to become readable.
+This function is equivalent to ready(Interest::READABLE)
and is usually
+paired with try_recv()
.
The function may complete without the socket being readable. This is a
+false-positive and attempting a try_recv()
will return with
+io::ErrorKind::WouldBlock
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::UnixDatagram;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let client_path = dir.path().join("client.sock");
+ let server_path = dir.path().join("server.sock");
+ let socket = UnixDatagram::bind(&client_path)?;
+ socket.connect(&server_path)?;
+
+ loop {
+ // Wait for the socket to be readable
+ socket.readable().await?;
+
+ // The buffer is **not** included in the async task and will
+ // only exist on the stack.
+ let mut buf = [0; 1024];
+
+ // Try to recv data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_recv(&mut buf) {
+ Ok(n) => {
+ println!("GOT {:?}", &buf[..n]);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Polls for read/receive readiness.
+If the socket is not currently ready for receiving, this method will
+store a clone of the Waker
from the provided Context
. When the
+socket becomes ready for reading, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_recv_ready
, poll_recv
or
+poll_peek
, only the Waker
from the Context
passed to the most
+recent call is scheduled to receive a wakeup. (However,
+poll_send_ready
retains a second, independent waker.)
This function is intended for cases where creating and pinning a future
+via readable
is not feasible. Where possible, using readable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the socket is not ready for reading.Poll::Ready(Ok(()))
if the socket is ready for reading.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Creates a new UnixDatagram
bound to the specified path.
use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// We use a temporary directory so that the socket
+// files left by the bound sockets will get cleaned up.
+let tmp = tempdir()?;
+
+// Bind the socket to a filesystem path
+let socket_path = tmp.path().join("socket");
+let socket = UnixDatagram::bind(&socket_path)?;
+
Creates an unnamed pair of connected sockets.
+This function will create a pair of interconnected Unix sockets for +communicating back and forth between one another.
+use tokio::net::UnixDatagram;
+
+// Create the pair of sockets
+let (sock1, sock2) = UnixDatagram::pair()?;
+
+// Since the sockets are paired, the paired send/recv
+// functions can be used
+let bytes = b"hail eris";
+sock1.send(bytes).await?;
+
+let mut buff = vec![0u8; 24];
+let size = sock2.recv(&mut buff).await?;
+
+let dgram = &buff[..size];
+assert_eq!(dgram, bytes);
+
Creates new UnixDatagram
from a std::os::unix::net::UnixDatagram
.
This function is intended to be used to wrap a UnixDatagram
from the
+standard library in the Tokio equivalent.
The caller is responsible for ensuring that the socket is in
+non-blocking mode. Otherwise all I/O operations on the socket
+will block the thread, which will cause unexpected behavior.
+Non-blocking mode can be set using set_nonblocking
.
This function panics if it is not called from within a runtime with +IO enabled.
+The runtime is usually set implicitly when this function is called
+from a future driven by a Tokio runtime, otherwise runtime can be set
+explicitly with Runtime::enter
function.
use tokio::net::UnixDatagram;
+use std::os::unix::net::UnixDatagram as StdUDS;
+use tempfile::tempdir;
+
+// We use a temporary directory so that the socket
+// files left by the bound sockets will get cleaned up.
+let tmp = tempdir()?;
+
+// Bind the socket to a filesystem path
+let socket_path = tmp.path().join("socket");
+let std_socket = StdUDS::bind(&socket_path)?;
+std_socket.set_nonblocking(true)?;
+let tokio_socket = UnixDatagram::from_std(std_socket)?;
+
Turns a tokio::net::UnixDatagram
into a std::os::unix::net::UnixDatagram
.
The returned std::os::unix::net::UnixDatagram
will have nonblocking
+mode set as true
. Use set_nonblocking
to change the blocking mode
+if needed.
let tokio_socket = tokio::net::UnixDatagram::bind("/path/to/the/socket")?;
+let std_socket = tokio_socket.into_std()?;
+std_socket.set_nonblocking(false)?;
Creates a new UnixDatagram
which is not bound to any address.
use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// Create an unbound socket
+let tx = UnixDatagram::unbound()?;
+
+// Create another, bound socket
+let tmp = tempdir()?;
+let rx_path = tmp.path().join("rx");
+let rx = UnixDatagram::bind(&rx_path)?;
+
+// Send to the bound socket
+let bytes = b"hello world";
+tx.send_to(bytes, &rx_path).await?;
+
+let mut buf = vec![0u8; 24];
+let (size, addr) = rx.recv_from(&mut buf).await?;
+
+let dgram = &buf[..size];
+assert_eq!(dgram, bytes);
+
Connects the socket to the specified address.
+The send
method may be used to send data to the specified address.
+recv
and recv_from
will only receive data from that address.
use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// Create an unbound socket
+let tx = UnixDatagram::unbound()?;
+
+// Create another, bound socket
+let tmp = tempdir()?;
+let rx_path = tmp.path().join("rx");
+let rx = UnixDatagram::bind(&rx_path)?;
+
+// Connect to the bound socket
+tx.connect(&rx_path)?;
+
+// Send to the bound socket
+let bytes = b"hello world";
+tx.send(bytes).await?;
+
+let mut buf = vec![0u8; 24];
+let (size, addr) = rx.recv_from(&mut buf).await?;
+
+let dgram = &buf[..size];
+assert_eq!(dgram, bytes);
+
Sends data on the socket to the socket’s peer.
+This method is cancel safe. If send
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, then it is guaranteed that the message was not sent.
use tokio::net::UnixDatagram;
+
+// Create the pair of sockets
+let (sock1, sock2) = UnixDatagram::pair()?;
+
+// Since the sockets are paired, the paired send/recv
+// functions can be used
+let bytes = b"hello world";
+sock1.send(bytes).await?;
+
+let mut buff = vec![0u8; 24];
+let size = sock2.recv(&mut buff).await?;
+
+let dgram = &buff[..size];
+assert_eq!(dgram, bytes);
+
Tries to send a datagram to the peer without waiting.
+use tokio::net::UnixDatagram;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let dir = tempfile::tempdir().unwrap();
+ let client_path = dir.path().join("client.sock");
+ let server_path = dir.path().join("server.sock");
+ let socket = UnixDatagram::bind(&client_path)?;
+ socket.connect(&server_path)?;
+
+ loop {
+ // Wait for the socket to be writable
+ socket.writable().await?;
+
+ // Try to send data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_send(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to send a datagram to the peer without waiting.
+use tokio::net::UnixDatagram;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let dir = tempfile::tempdir().unwrap();
+ let client_path = dir.path().join("client.sock");
+ let server_path = dir.path().join("server.sock");
+ let socket = UnixDatagram::bind(&client_path)?;
+
+ loop {
+ // Wait for the socket to be writable
+ socket.writable().await?;
+
+ // Try to send data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_send_to(b"hello world", &server_path) {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Receives data from the socket.
+This method is cancel safe. If recv
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, it is guaranteed that no messages were received on this
+socket.
use tokio::net::UnixDatagram;
+
+// Create the pair of sockets
+let (sock1, sock2) = UnixDatagram::pair()?;
+
+// Since the sockets are paired, the paired send/recv
+// functions can be used
+let bytes = b"hello world";
+sock1.send(bytes).await?;
+
+let mut buff = vec![0u8; 24];
+let size = sock2.recv(&mut buff).await?;
+
+let dgram = &buff[..size];
+assert_eq!(dgram, bytes);
+
Tries to receive a datagram from the peer without waiting.
+use tokio::net::UnixDatagram;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let client_path = dir.path().join("client.sock");
+ let server_path = dir.path().join("server.sock");
+ let socket = UnixDatagram::bind(&client_path)?;
+ socket.connect(&server_path)?;
+
+ loop {
+ // Wait for the socket to be readable
+ socket.readable().await?;
+
+ // The buffer is **not** included in the async task and will
+ // only exist on the stack.
+ let mut buf = [0; 1024];
+
+ // Try to recv data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_recv(&mut buf) {
+ Ok(n) => {
+ println!("GOT {:?}", &buf[..n]);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Sends data on the socket to the specified address.
+This method is cancel safe. If send_to
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, then it is guaranteed that the message was not sent.
use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// We use a temporary directory so that the socket
+// files left by the bound sockets will get cleaned up.
+let tmp = tempdir()?;
+
+// Bind each socket to a filesystem path
+let tx_path = tmp.path().join("tx");
+let tx = UnixDatagram::bind(&tx_path)?;
+let rx_path = tmp.path().join("rx");
+let rx = UnixDatagram::bind(&rx_path)?;
+
+let bytes = b"hello world";
+tx.send_to(bytes, &rx_path).await?;
+
+let mut buf = vec![0u8; 24];
+let (size, addr) = rx.recv_from(&mut buf).await?;
+
+let dgram = &buf[..size];
+assert_eq!(dgram, bytes);
+assert_eq!(addr.as_pathname().unwrap(), &tx_path);
+
Receives data from the socket.
+This method is cancel safe. If recv_from
is used as the event in a
+tokio::select!
statement and some other branch
+completes first, it is guaranteed that no messages were received on this
+socket.
use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// We use a temporary directory so that the socket
+// files left by the bound sockets will get cleaned up.
+let tmp = tempdir()?;
+
+// Bind each socket to a filesystem path
+let tx_path = tmp.path().join("tx");
+let tx = UnixDatagram::bind(&tx_path)?;
+let rx_path = tmp.path().join("rx");
+let rx = UnixDatagram::bind(&rx_path)?;
+
+let bytes = b"hello world";
+tx.send_to(bytes, &rx_path).await?;
+
+let mut buf = vec![0u8; 24];
+let (size, addr) = rx.recv_from(&mut buf).await?;
+
+let dgram = &buf[..size];
+assert_eq!(dgram, bytes);
+assert_eq!(addr.as_pathname().unwrap(), &tx_path);
+
Attempts to receive a single datagram on the specified address.
+Note that on multiple calls to a poll_*
method in the recv
direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not ready to readPoll::Ready(Ok(addr))
reads data from addr
into ReadBuf
if the socket is readyPoll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Attempts to send data to the specified address.
+Note that on multiple calls to a poll_*
method in the send direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not ready to writePoll::Ready(Ok(n))
n
is the number of bytes sent.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Attempts to send data on the socket to the remote address to which it
+was previously connect
ed.
The connect
method will connect this socket to a remote address.
+This method will fail if the socket is not connected.
Note that on multiple calls to a poll_*
method in the send direction,
+only the Waker
from the Context
passed to the most recent call will
+be scheduled to receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not available to writePoll::Ready(Ok(n))
n
is the number of bytes sentPoll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Attempts to receive a single datagram message on the socket from the remote
+address to which it is connect
ed.
The connect
method will connect this socket to a remote address. This method
+resolves to an error if the socket is not connected.
Note that on multiple calls to a poll_*
method in the recv
direction, only the
+Waker
from the Context
passed to the most recent call will be scheduled to
+receive a wakeup.
The function returns:
+Poll::Pending
if the socket is not ready to readPoll::Ready(Ok(()))
reads data ReadBuf
if the socket is readyPoll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Tries to receive data from the socket without waiting.
+use tokio::net::UnixDatagram;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let client_path = dir.path().join("client.sock");
+ let server_path = dir.path().join("server.sock");
+ let socket = UnixDatagram::bind(&client_path)?;
+
+ loop {
+ // Wait for the socket to be readable
+ socket.readable().await?;
+
+ // The buffer is **not** included in the async task and will
+ // only exist on the stack.
+ let mut buf = [0; 1024];
+
+ // Try to recv data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match socket.try_recv_from(&mut buf) {
+ Ok((n, _addr)) => {
+ println!("GOT {:?}", &buf[..n]);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to read or write from the socket using a user-provided IO operation.
+If the socket is ready, the provided closure is called. The closure
+should attempt to perform IO operation on the socket by manually
+calling the appropriate syscall. If the operation fails because the
+socket is not actually ready, then the closure should return a
+WouldBlock
error and the readiness flag is cleared. The return value
+of the closure is then returned by try_io
.
If the socket is not ready, then the closure is not called
+and a WouldBlock
error is returned.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio UnixDatagram
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Usually, readable()
, writable()
or ready()
is used with this function.
Reads or writes from the socket using a user-provided IO operation.
+The readiness of the socket is awaited and when the socket is ready,
+the provided closure is called. The closure should attempt to perform
+IO operation on the socket by manually calling the appropriate syscall.
+If the operation fails because the socket is not actually ready,
+then the closure should return a WouldBlock
error. In such case the
+readiness flag is cleared and the socket readiness is awaited again.
+This loop is repeated until the closure returns an Ok
or an error
+other than WouldBlock
.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio UnixDatagram
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Returns the local address that this socket is bound to.
+For a socket bound to a local path
+ +use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// We use a temporary directory so that the socket
+// files left by the bound sockets will get cleaned up.
+let tmp = tempdir()?;
+
+// Bind socket to a filesystem path
+let socket_path = tmp.path().join("socket");
+let socket = UnixDatagram::bind(&socket_path)?;
+
+assert_eq!(socket.local_addr()?.as_pathname().unwrap(), &socket_path);
+
For an unbound socket
+ +use tokio::net::UnixDatagram;
+
+// Create an unbound socket
+let socket = UnixDatagram::unbound()?;
+
+assert!(socket.local_addr()?.is_unnamed());
+
Returns the address of this socket’s peer.
+The connect
method will connect the socket to a peer.
For a peer with a local path
+ +use tokio::net::UnixDatagram;
+use tempfile::tempdir;
+
+// Create an unbound socket
+let tx = UnixDatagram::unbound()?;
+
+// Create another, bound socket
+let tmp = tempdir()?;
+let rx_path = tmp.path().join("rx");
+let rx = UnixDatagram::bind(&rx_path)?;
+
+// Connect to the bound socket
+tx.connect(&rx_path)?;
+
+assert_eq!(tx.peer_addr()?.as_pathname().unwrap(), &rx_path);
+
For an unbound peer
+ +use tokio::net::UnixDatagram;
+
+// Create the pair of sockets
+let (sock1, sock2) = UnixDatagram::pair()?;
+
+assert!(sock1.peer_addr()?.is_unnamed());
+
Returns the value of the SO_ERROR
option.
use tokio::net::UnixDatagram;
+
+// Create an unbound socket
+let socket = UnixDatagram::unbound()?;
+
+if let Ok(Some(err)) = socket.take_error() {
+ println!("Got error: {:?}", err);
+}
+
Shuts down the read, write, or both halves of this connection.
+This function will cause all pending and future I/O calls on the
+specified portions to immediately return with an appropriate value
+(see the documentation of Shutdown
).
use tokio::net::UnixDatagram;
+use std::net::Shutdown;
+
+// Create an unbound socket
+let (socket, other) = UnixDatagram::pair()?;
+
+socket.shutdown(Shutdown::Both)?;
+
+// NOTE: the following commented out code does NOT work as expected.
+// Due to an underlying issue, the recv call will block indefinitely.
+// See: https://github.com/tokio-rs/tokio/issues/1679
+//let mut buff = vec![0u8; 24];
+//let size = socket.recv(&mut buff).await?;
+//assert_eq!(size, 0);
+
+let send_result = socket.send(b"hello world").await;
+assert!(send_result.is_err());
+
Consumes stream, returning the Tokio I/O object.
+This is equivalent to
+UnixDatagram::from_std(stream)
.
pub struct UnixListener { /* private fields */ }
A Unix socket which can accept connections from other Unix sockets.
+You can accept a new connection by using the accept
method.
A UnixListener
can be turned into a Stream
with UnixListenerStream
.
Note that accepting a connection can lead to various errors and not all +of them are necessarily fatal ‒ for example having too many open file +descriptors or the other side closing the connection while it waits in +an accept queue. These would terminate the stream if not handled in any +way.
+use tokio::net::UnixListener;
+
+#[tokio::main]
+async fn main() {
+ let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ loop {
+ match listener.accept().await {
+ Ok((stream, _addr)) => {
+ println!("new client!");
+ }
+ Err(e) => { /* connection failed */ }
+ }
+ }
+}
Creates a new UnixListener
bound to the specified path.
This function panics if it is not called from within a runtime with +IO enabled.
+The runtime is usually set implicitly when this function is called
+from a future driven by a tokio runtime, otherwise runtime can be set
+explicitly with Runtime::enter
function.
Creates new UnixListener
from a std::os::unix::net::UnixListener
.
This function is intended to be used to wrap a UnixListener
from the
+standard library in the Tokio equivalent.
The caller is responsible for ensuring that the listener is in
+non-blocking mode. Otherwise all I/O operations on the listener
+will block the thread, which will cause unexpected behavior.
+Non-blocking mode can be set using set_nonblocking
.
use tokio::net::UnixListener;
+use std::os::unix::net::UnixListener as StdUnixListener;
+
+let std_listener = StdUnixListener::bind("/path/to/the/socket")?;
+std_listener.set_nonblocking(true)?;
+let listener = UnixListener::from_std(std_listener)?;
This function panics if it is not called from within a runtime with +IO enabled.
+The runtime is usually set implicitly when this function is called
+from a future driven by a tokio runtime, otherwise runtime can be set
+explicitly with Runtime::enter
function.
Turns a tokio::net::UnixListener
into a std::os::unix::net::UnixListener
.
The returned std::os::unix::net::UnixListener
will have nonblocking mode
+set as true
. Use set_nonblocking
to change the blocking mode if needed.
let tokio_listener = tokio::net::UnixListener::bind("/path/to/the/socket")?;
+let std_listener = tokio_listener.into_std()?;
+std_listener.set_nonblocking(false)?;
Returns the local socket address of this listener.
+Returns the value of the SO_ERROR
option.
Accepts a new incoming connection to this listener.
+This method is cancel safe. If the method is used as the event in a
+tokio::select!
statement and some other branch
+completes first, then it is guaranteed that no new connections were
+accepted by this method.
Polls to accept a new incoming connection to this listener.
+If there is no connection to accept, Poll::Pending
is returned and the
+current task will be notified by a waker. Note that on multiple calls
+to poll_accept
, only the Waker
from the Context
passed to the most
+recent call is scheduled to receive a wakeup.
Consumes stream, returning the tokio I/O object.
+This is equivalent to
+UnixListener::from_std(stream)
.
pub struct UnixStream { /* private fields */ }
A structure representing a connected Unix socket.
+This socket can be connected directly with UnixStream::connect
or accepted
+from a listener with UnixListener::accept
. Additionally, a pair of
+anonymous Unix sockets can be created with UnixStream::pair
.
To shut down the stream in the write direction, you can call the
+shutdown()
method. This will cause the other peer to receive a read of
+length 0, indicating that no more data will be sent. This only closes
+the stream in one direction.
Connects to the socket named by path
.
This function will create a new Unix socket and connect to the path +specified, associating the returned stream with the default event loop’s +handle.
+Waits for any of the requested ready states.
+This function is usually paired with try_read()
or try_write()
. It
+can be used to concurrently read / write to the same socket on a single
+task without splitting the socket.
The function may complete without the socket being ready. This is a
+false-positive and attempting an operation will return with
+io::ErrorKind::WouldBlock
. The function can also return with an empty
+Ready
set, so you should always check the returned value and possibly
+wait again if the requested states are not set.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read or write that fails with WouldBlock
or
+Poll::Pending
.
Concurrently read and write to the stream on the same task without +splitting.
+ +use tokio::io::Interest;
+use tokio::net::UnixStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+ let stream = UnixStream::connect(bind_path).await?;
+
+ loop {
+ let ready = stream.ready(Interest::READABLE | Interest::WRITABLE).await?;
+
+ if ready.is_readable() {
+ let mut data = vec![0; 1024];
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read(&mut data) {
+ Ok(n) => {
+ println!("read {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+
+ }
+
+ if ready.is_writable() {
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write(b"hello world") {
+ Ok(n) => {
+ println!("write {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+ }
+}
Waits for the socket to become readable.
+This function is equivalent to ready(Interest::READABLE)
and is usually
+paired with try_read()
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to read that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::UnixStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+ let stream = UnixStream::connect(bind_path).await?;
+
+ let mut msg = vec![0; 1024];
+
+ loop {
+ // Wait for the socket to be readable
+ stream.readable().await?;
+
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read(&mut msg) {
+ Ok(n) => {
+ msg.truncate(n);
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ println!("GOT = {:?}", msg);
+ Ok(())
+}
Polls for read readiness.
+If the unix stream is not currently ready for reading, this method will
+store a clone of the Waker
from the provided Context
. When the unix
+stream becomes ready for reading, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_read_ready
or poll_read
, only
+the Waker
from the Context
passed to the most recent call is
+scheduled to receive a wakeup. (However, poll_write_ready
retains a
+second, independent waker.)
This function is intended for cases where creating and pinning a future
+via readable
is not feasible. Where possible, using readable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the unix stream is not ready for reading.Poll::Ready(Ok(()))
if the unix stream is ready for reading.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Try to read data from the stream into the provided buffer, returning how +many bytes were read.
+Receives any pending data from the socket but does not wait for new data
+to arrive. On success, returns the number of bytes read. Because
+try_read()
is non-blocking, the buffer does not have to be stored by
+the async task and can exist entirely on the stack.
Usually, readable()
or ready()
is used with this function.
If data is successfully read, Ok(n)
is returned, where n
is the
+number of bytes read. If n
is 0
, then it can indicate one of two scenarios:
If the stream is not ready to read data,
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::UnixStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+ let stream = UnixStream::connect(bind_path).await?;
+
+ loop {
+ // Wait for the socket to be readable
+ stream.readable().await?;
+
+ // Creating the buffer **after** the `await` prevents it from
+ // being stored in the async task.
+ let mut buf = [0; 4096];
+
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read(&mut buf) {
+ Ok(0) => break,
+ Ok(n) => {
+ println!("read {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to read data from the stream into the provided buffers, returning +how many bytes were read.
+Data is copied to fill each buffer in order, with the final buffer
+written to possibly being only partially filled. This method behaves
+equivalently to a single call to try_read()
with concatenated
+buffers.
Receives any pending data from the socket but does not wait for new data
+to arrive. On success, returns the number of bytes read. Because
+try_read_vectored()
is non-blocking, the buffer does not have to be
+stored by the async task and can exist entirely on the stack.
Usually, readable()
or ready()
is used with this function.
If data is successfully read, Ok(n)
is returned, where n
is the
+number of bytes read. Ok(0)
indicates the stream’s read half is closed
+and will no longer yield data. If the stream is not ready to read data
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::UnixStream;
+use std::error::Error;
+use std::io::{self, IoSliceMut};
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+ let stream = UnixStream::connect(bind_path).await?;
+
+ loop {
+ // Wait for the socket to be readable
+ stream.readable().await?;
+
+ // Creating the buffer **after** the `await` prevents it from
+ // being stored in the async task.
+ let mut buf_a = [0; 512];
+ let mut buf_b = [0; 1024];
+ let mut bufs = [
+ IoSliceMut::new(&mut buf_a),
+ IoSliceMut::new(&mut buf_b),
+ ];
+
+ // Try to read data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_read_vectored(&mut bufs) {
+ Ok(0) => break,
+ Ok(n) => {
+ println!("read {} bytes", n);
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Waits for the socket to become writable.
+This function is equivalent to ready(Interest::WRITABLE)
and is usually
+paired with try_write()
.
This method is cancel safe. Once a readiness event occurs, the method
+will continue to return immediately until the readiness event is
+consumed by an attempt to write that fails with WouldBlock
or
+Poll::Pending
.
use tokio::net::UnixStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+ let stream = UnixStream::connect(bind_path).await?;
+
+ loop {
+ // Wait for the socket to be writable
+ stream.writable().await?;
+
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Polls for write readiness.
+If the unix stream is not currently ready for writing, this method will
+store a clone of the Waker
from the provided Context
. When the unix
+stream becomes ready for writing, Waker::wake
will be called on the
+waker.
Note that on multiple calls to poll_write_ready
or poll_write
, only
+the Waker
from the Context
passed to the most recent call is
+scheduled to receive a wakeup. (However, poll_read_ready
retains a
+second, independent waker.)
This function is intended for cases where creating and pinning a future
+via writable
is not feasible. Where possible, using writable
is
+preferred, as this supports polling from multiple tasks at once.
The function returns:
+Poll::Pending
if the unix stream is not ready for writing.Poll::Ready(Ok(()))
if the unix stream is ready for writing.Poll::Ready(Err(e))
if an error is encountered.This function may encounter any standard I/O error except WouldBlock
.
Tries to write a buffer to the stream, returning how many bytes were +written.
+The function will attempt to write the entire contents of buf
, but
+only part of the buffer may be written.
This function is usually paired with writable()
.
If data is successfully written, Ok(n)
is returned, where n
is the
+number of bytes written. If the stream is not ready to write data,
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::UnixStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+ let stream = UnixStream::connect(bind_path).await?;
+
+ loop {
+ // Wait for the socket to be writable
+ stream.writable().await?;
+
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write(b"hello world") {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to write several buffers to the stream, returning how many bytes +were written.
+Data is written from each buffer in order, with the final buffer read
+from possible being only partially consumed. This method behaves
+equivalently to a single call to try_write()
with concatenated
+buffers.
This function is usually paired with writable()
.
If data is successfully written, Ok(n)
is returned, where n
is the
+number of bytes written. If the stream is not ready to write data,
+Err(io::ErrorKind::WouldBlock)
is returned.
use tokio::net::UnixStream;
+use std::error::Error;
+use std::io;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ // Connect to a peer
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+ let stream = UnixStream::connect(bind_path).await?;
+
+ let bufs = [io::IoSlice::new(b"hello "), io::IoSlice::new(b"world")];
+
+ loop {
+ // Wait for the socket to be writable
+ stream.writable().await?;
+
+ // Try to write data, this may still fail with `WouldBlock`
+ // if the readiness event is a false positive.
+ match stream.try_write_vectored(&bufs) {
+ Ok(n) => {
+ break;
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
+ Ok(())
+}
Tries to read or write from the socket using a user-provided IO operation.
+If the socket is ready, the provided closure is called. The closure
+should attempt to perform IO operation on the socket by manually
+calling the appropriate syscall. If the operation fails because the
+socket is not actually ready, then the closure should return a
+WouldBlock
error and the readiness flag is cleared. The return value
+of the closure is then returned by try_io
.
If the socket is not ready, then the closure is not called
+and a WouldBlock
error is returned.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio UnixStream
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Usually, readable()
, writable()
or ready()
is used with this function.
Reads or writes from the socket using a user-provided IO operation.
+The readiness of the socket is awaited and when the socket is ready,
+the provided closure is called. The closure should attempt to perform
+IO operation on the socket by manually calling the appropriate syscall.
+If the operation fails because the socket is not actually ready,
+then the closure should return a WouldBlock
error. In such case the
+readiness flag is cleared and the socket readiness is awaited again.
+This loop is repeated until the closure returns an Ok
or an error
+other than WouldBlock
.
The closure should only return a WouldBlock
error if it has performed
+an IO operation on the socket that failed due to the socket not being
+ready. Returning a WouldBlock
error in any other situation will
+incorrectly clear the readiness flag, which can cause the socket to
+behave incorrectly.
The closure should not perform the IO operation using any of the methods
+defined on the Tokio UnixStream
type, as this will mess with the
+readiness flag and can cause the socket to behave incorrectly.
This method is not intended to be used with combined interests. +The closure should perform only one type of IO operation, so it should not +require more than one ready state. This method may panic or sleep forever +if it is called with a combined interest.
+Creates new UnixStream
from a std::os::unix::net::UnixStream
.
This function is intended to be used to wrap a UnixStream
from the
+standard library in the Tokio equivalent.
The caller is responsible for ensuring that the stream is in
+non-blocking mode. Otherwise all I/O operations on the stream
+will block the thread, which will cause unexpected behavior.
+Non-blocking mode can be set using set_nonblocking
.
use tokio::net::UnixStream;
+use std::os::unix::net::UnixStream as StdUnixStream;
+
+let std_stream = StdUnixStream::connect("/path/to/the/socket")?;
+std_stream.set_nonblocking(true)?;
+let stream = UnixStream::from_std(std_stream)?;
This function panics if it is not called from within a runtime with +IO enabled.
+The runtime is usually set implicitly when this function is called
+from a future driven by a tokio runtime, otherwise runtime can be set
+explicitly with Runtime::enter
function.
Turns a tokio::net::UnixStream
into a std::os::unix::net::UnixStream
.
The returned std::os::unix::net::UnixStream
will have nonblocking
+mode set as true
. Use set_nonblocking
to change the blocking
+mode if needed.
use std::error::Error;
+use std::io::Read;
+use tokio::net::UnixListener;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let dir = tempfile::tempdir().unwrap();
+ let bind_path = dir.path().join("bind_path");
+
+ let mut data = [0u8; 12];
+ let listener = UnixListener::bind(&bind_path)?;
+ let (tokio_unix_stream, _) = listener.accept().await?;
+ let mut std_unix_stream = tokio_unix_stream.into_std()?;
+ std_unix_stream.set_nonblocking(false)?;
+ std_unix_stream.read_exact(&mut data)?;
+ Ok(())
+}
Creates an unnamed pair of connected sockets.
+This function will create a pair of interconnected Unix sockets for +communicating back and forth between one another. Each socket will +be associated with the default event loop’s handle.
+Returns the socket address of the local half of this connection.
+use tokio::net::UnixStream;
+
+let dir = tempfile::tempdir().unwrap();
+let bind_path = dir.path().join("bind_path");
+let stream = UnixStream::connect(bind_path).await?;
+
+println!("{:?}", stream.local_addr()?);
Returns the socket address of the remote half of this connection.
+use tokio::net::UnixStream;
+
+let dir = tempfile::tempdir().unwrap();
+let bind_path = dir.path().join("bind_path");
+let stream = UnixStream::connect(bind_path).await?;
+
+println!("{:?}", stream.peer_addr()?);
Returns effective credentials of the process which called connect
or pair
.
Returns the value of the SO_ERROR
option.
Splits a UnixStream
into a read half and a write half, which can be used
+to read and write the stream concurrently.
This method is more efficient than into_split
, but the halves cannot be
+moved into independently spawned tasks.
Splits a UnixStream
into a read half and a write half, which can be used
+to read and write the stream concurrently.
Unlike split
, the owned halves can be moved to separate tasks, however
+this comes at the cost of a heap allocation.
Note: Dropping the write half will shut down the write half of the
+stream. This is equivalent to calling shutdown()
on the UnixStream
.
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreConsumes stream, returning the tokio I/O object.
+This is equivalent to
+UnixStream::from_std(stream)
.
Redirecting to ../../actix_rt/struct.Runtime.html...
+ + + \ No newline at end of file diff --git a/actix_rt/sidebar-items.js b/actix_rt/sidebar-items.js new file mode 100644 index 0000000000..5909e6a6fa --- /dev/null +++ b/actix_rt/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"attr":["main","test"],"fn":["spawn"],"macro":["pin"],"mod":["net","signal","task","time"],"struct":["Arbiter","ArbiterHandle","Runtime","System","SystemRunner"]}; \ No newline at end of file diff --git a/actix_rt/signal/fn.ctrl_c.html b/actix_rt/signal/fn.ctrl_c.html new file mode 100644 index 0000000000..47dffc45f5 --- /dev/null +++ b/actix_rt/signal/fn.ctrl_c.html @@ -0,0 +1,39 @@ +pub async fn ctrl_c() -> Result<(), Error>
Completes when a “ctrl-c” notification is sent to the process.
+While signals are handled very differently between Unix and Windows, both +platforms support receiving a signal on “ctrl-c”. This function provides a +portable API for receiving this notification.
+Once the returned future is polled, a listener is registered. The future
+will complete on the first received ctrl-c
after the initial call to
+either Future::poll
or .await
.
On Unix platforms, the first time that a Signal
instance is registered for a
+particular signal kind, an OS signal-handler is installed which replaces the
+default platform behavior when that signal is received, for the duration of
+the entire process.
For example, Unix systems will terminate a process by default when it
+receives a signal generated by "CTRL+C"
on the terminal. But, when a
+ctrl_c
stream is created to listen for this signal, the time it arrives,
+it will be translated to a stream event, and the process will continue to
+execute. Even if this Signal
instance is dropped, subsequent SIGINT
+deliveries will end up captured by Tokio, and the default platform behavior
+will NOT be reset.
Thus, applications should take care to ensure the expected signal behavior +occurs as expected after listening for specific signals.
+use tokio::signal;
+
+#[tokio::main]
+async fn main() {
+ println!("waiting for ctrl-c");
+
+ signal::ctrl_c().await.expect("failed to listen for event");
+
+ println!("received ctrl-c event");
+}
Listen in the background:
+ +tokio::spawn(async move {
+ tokio::signal::ctrl_c().await.unwrap();
+ // Your handler here
+});
pub fn signal(kind: SignalKind) -> Result<Signal, Error>
Creates a new listener which will receive notifications when the current
+process receives the specified signal kind
.
This function will create a new stream which binds to the default reactor.
+The Signal
stream is an infinite stream which will receive
+notifications whenever a signal is received. More documentation can be
+found on Signal
itself, but to reiterate:
A Signal
stream can be created for a particular signal number
+multiple times. When a signal is received then all the associated
+channels will receive the signal notification.
signal_hook::FORBIDDEN
This function panics if there is no current reactor set, or if the rt
+feature flag is not enabled.
Unix specific signals (Tokio re-exports).
+kind
.pub struct Signal { /* private fields */ }
An listener for receiving a particular type of OS signal.
+The listener can be turned into a Stream
using SignalStream
.
In general signal handling on Unix is a pretty tricky topic, and this
+structure is no exception! There are some important limitations to keep in
+mind when using Signal
streams:
Signals handling in Unix already necessitates coalescing signals
+together sometimes. This Signal
stream is also no exception here in
+that it will also coalesce signals. That is, even if the signal handler
+for this process runs multiple times, the Signal
stream may only return
+one signal notification. Specifically, before poll
is called, all
+signal notifications are coalesced into one item returned from poll
.
+Once poll
has been called, however, a further signal is guaranteed to
+be yielded as an item.
Put another way, any element pulled off the returned listener corresponds to +at least one signal, but possibly more.
+Signal handling in general is relatively inefficient. Although some +improvements are possible in this crate, it’s recommended to not plan on +having millions of signal channels open.
+If you’ve got any questions about this feel free to open an issue on the +repo! New approaches to alleviate some of these limitations are always +appreciated!
+The first time that a Signal
instance is registered for a particular
+signal kind, an OS signal-handler is installed which replaces the default
+platform behavior when that signal is received, for the duration of the
+entire process.
For example, Unix systems will terminate a process by default when it
+receives SIGINT
. But, when a Signal
instance is created to listen for
+this signal, the next SIGINT
that arrives will be translated to a stream
+event, and the process will continue to execute. Even if this Signal
+instance is dropped, subsequent SIGINT
deliveries will end up captured by
+Tokio, and the default platform behavior will NOT be reset.
Thus, applications should take care to ensure the expected signal behavior +occurs as expected after listening for specific signals.
+Wait for SIGHUP
use tokio::signal::unix::{signal, SignalKind};
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ // An infinite stream of hangup signals.
+ let mut sig = signal(SignalKind::hangup())?;
+
+ // Print whenever a HUP signal is received
+ loop {
+ sig.recv().await;
+ println!("got signal HUP");
+ }
+}
Receives the next signal notification event.
+None
is returned if no more events can be received by this stream.
This method is cancel safe. If you use it as the event in a
+tokio::select!
statement and some other branch
+completes first, then it is guaranteed that no signal is lost.
Wait for SIGHUP
use tokio::signal::unix::{signal, SignalKind};
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ // An infinite stream of hangup signals.
+ let mut stream = signal(SignalKind::hangup())?;
+
+ // Print whenever a HUP signal is received
+ loop {
+ stream.recv().await;
+ println!("got signal HUP");
+ }
+}
Polls to receive the next signal notification event, outside of an
+async
context.
This method returns:
+Poll::Pending
if no signals are available but the channel is not
+closed.Poll::Ready(Some(()))
if a signal is available.Poll::Ready(None)
if the channel has been closed and all signals
+sent before it was closed have been received.Polling from a manually implemented future
+ +use std::pin::Pin;
+use std::future::Future;
+use std::task::{Context, Poll};
+use tokio::signal::unix::Signal;
+
+struct MyFuture {
+ signal: Signal,
+}
+
+impl Future for MyFuture {
+ type Output = Option<()>;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ println!("polling MyFuture");
+ self.signal.poll_recv(cx)
+ }
+}
pub struct SignalKind(/* private fields */);
Represents the specific kind of signal to listen for.
+Allows for listening to any valid OS signal.
+For example, this can be used for listening for platform-specific +signals.
+ +// let signum = libc::OS_SPECIFIC_SIGNAL;
+let kind = SignalKind::from_raw(signum);
Get the signal’s numeric value.
+ +let kind = SignalKind::interrupt();
+assert_eq!(kind.as_raw_value(), libc::SIGINT);
Represents the SIGALRM
signal.
On Unix systems this signal is sent when a real-time timer has expired. +By default, the process is terminated by this signal.
+Represents the SIGCHLD
signal.
On Unix systems this signal is sent when the status of a child process +has changed. By default, this signal is ignored.
+Represents the SIGHUP
signal.
On Unix systems this signal is sent when the terminal is disconnected. +By default, the process is terminated by this signal.
+Represents the SIGINT
signal.
On Unix systems this signal is sent to interrupt a program. +By default, the process is terminated by this signal.
+Represents the SIGIO
signal.
On Unix systems this signal is sent when I/O operations are possible +on some file descriptor. By default, this signal is ignored.
+Represents the SIGPIPE
signal.
On Unix systems this signal is sent when the process attempts to write +to a pipe which has no reader. By default, the process is terminated by +this signal.
+Represents the SIGQUIT
signal.
On Unix systems this signal is sent to issue a shutdown of the +process, after which the OS will dump the process core. +By default, the process is terminated by this signal.
+Represents the SIGTERM
signal.
On Unix systems this signal is sent to issue a shutdown of the +process. By default, the process is terminated by this signal.
+Represents the SIGUSR1
signal.
On Unix systems this is a user defined signal. +By default, the process is terminated by this signal.
+Represents the SIGUSR2
signal.
On Unix systems this is a user defined signal. +By default, the process is terminated by this signal.
+Represents the SIGWINCH
signal.
On Unix systems this signal is sent when the terminal window is resized. +By default, this signal is ignored.
+source
. Read moreself
and other
values to be equal, and is used
+by ==
.pub struct Arbiter { /* private fields */ }
An Arbiter represents a thread that provides an asynchronous execution environment for futures +and functions.
+When an arbiter is created, it spawns a new OS thread, and hosts an event loop.
+Return a handle to the this Arbiter’s message sender.
+Return a handle to the current thread’s Arbiter’s message sender.
+Panics if no Arbiter is running on the current thread.
+Try to get current running arbiter handle.
+Returns None
if no Arbiter has been started.
Unlike current
, this never panics.
Stop Arbiter from continuing it’s event loop.
+Returns true if stop message was sent successfully and false if the Arbiter has been dropped.
+Send a future to the Arbiter’s thread and spawn it.
+If you require a result, include a response channel in the future.
+Returns true if future was sent successfully and false if the Arbiter has died.
+Send a function to the Arbiter’s thread and execute it.
+Any result from the function is discarded. If you require a result, include a response +channel in the function.
+Returns true if function was sent successfully and false if the Arbiter has died.
+Wait for Arbiter’s event loop to complete.
+Joins the underlying OS thread handle. See JoinHandle::join
.
pub struct ArbiterHandle { /* private fields */ }
A handle for sending spawn and stop messages to an Arbiter.
+source
. Read morepub struct Runtime { /* private fields */ }
A Tokio-based runtime proxy.
+All spawned futures will be executed on the current thread. Therefore, there is no Send
bound
+on submitted futures.
Returns a new runtime initialized with default configuration values.
+Offload a future onto the single-threaded runtime.
+The returned join handle can be used to await the future’s result.
+See crate root documentation for more details.
+let rt = actix_rt::Runtime::new().unwrap();
+
+// Spawn a future onto the runtime
+let handle = rt.spawn(async {
+ println!("running on the runtime");
+ 42
+});
+
+assert_eq!(rt.block_on(handle).unwrap(), 42);
This function panics if the spawn fails. Failure occurs if the executor is currently at +capacity and is unable to spawn a new future.
+Retrieves a reference to the underlying Tokio runtime associated with this instance.
+The Tokio runtime is responsible for executing asynchronous tasks and managing +the event loop for an asynchronous Rust program. This method allows accessing +the runtime to interact with its features directly.
+In a typical use case, you might need to share the same runtime between different
+modules of your project. For example, a module might require a tokio::runtime::Handle
+to spawn tasks on the same runtime, or the runtime itself to configure more complex
+behaviours.
use actix_rt::Runtime;
+
+mod module_a {
+ pub fn do_something(handle: tokio::runtime::Handle) {
+ handle.spawn(async {
+ // Some asynchronous task here
+ });
+ }
+}
+
+mod module_b {
+ pub fn do_something_else(rt: &tokio::runtime::Runtime) {
+ rt.spawn(async {
+ // Another asynchronous task here
+ });
+ }
+}
+
+let actix_runtime = actix_rt::Runtime::new().unwrap();
+let tokio_runtime = actix_runtime.tokio_runtime();
+
+let handle = tokio_runtime.handle().clone();
+
+module_a::do_something(handle);
+module_b::do_something_else(tokio_runtime);
An immutable reference to the tokio::runtime::Runtime
instance associated with this
+Runtime
instance.
While this method provides an immutable reference to the Tokio runtime, which is safe to share across threads, +be aware that spawning blocking tasks on the Tokio runtime could potentially impact the execution +of the Actix runtime. This is because Tokio is responsible for driving the Actix system, +and blocking tasks could delay or deadlock other tasks in run loop.
+Runs the provided future, blocking the current thread until the future completes.
+This function can be used to synchronously block the current thread until the provided
+future
has resolved either successfully or with an error. The result of the future is
+then returned from this function call.
Note that this function will also execute any spawned futures on the current thread, but
+will not block until these other spawned futures have completed. Once the function returns,
+any uncompleted futures remain pending in the Runtime
instance. These futures will not run
+until block_on
or run
is called again.
The caller is responsible for ensuring that other spawned futures complete execution by
+calling block_on
or run
.
pub struct System { /* private fields */ }
A manager for a per-thread distributed async runtime.
+Try to get current running system.
+Returns None
if no System has been started.
Unlike current
, this never panics.
Get handle to a the System’s initial Arbiter.
+Check if there is a System registered on the current thread.
+Stop the system with a given exit code.
+pub struct SystemRunner;
Runner that keeps a System’s event loop alive until stop message is received.
+Runs the event loop until stopped, returning the exit code.
+Redirecting to ../../actix_rt/struct.System.html...
+ + + \ No newline at end of file diff --git a/actix_rt/system/struct.SystemRunner.html b/actix_rt/system/struct.SystemRunner.html new file mode 100644 index 0000000000..3324e8300a --- /dev/null +++ b/actix_rt/system/struct.SystemRunner.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_rt/struct.SystemRunner.html...
+ + + \ No newline at end of file diff --git a/actix_rt/task/fn.spawn_blocking.html b/actix_rt/task/fn.spawn_blocking.html new file mode 100644 index 0000000000..39d48b2bc1 --- /dev/null +++ b/actix_rt/task/fn.spawn_blocking.html @@ -0,0 +1,86 @@ +pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R> ⓘ
Runs the provided closure on a thread where blocking is acceptable.
+In general, issuing a blocking call or performing a lot of compute in a +future without yielding is problematic, as it may prevent the executor from +driving other futures forward. This function runs the provided closure on a +thread dedicated to blocking operations. See the CPU-bound tasks and +blocking code section for more information.
+Tokio will spawn more blocking threads when they are requested through this
+function until the upper limit configured on the Builder
is reached.
+After reaching the upper limit, the tasks are put in a queue.
+The thread limit is very large by default, because spawn_blocking
is often
+used for various kinds of IO operations that cannot be performed
+asynchronously. When you run CPU-bound code using spawn_blocking
, you
+should keep this large upper limit in mind. When running many CPU-bound
+computations, a semaphore or some other synchronization primitive should be
+used to limit the number of computation executed in parallel. Specialized
+CPU-bound executors, such as rayon, may also be a good fit.
This function is intended for non-async operations that eventually finish on
+their own. If you want to spawn an ordinary thread, you should use
+thread::spawn
instead.
Be aware that tasks spawned using spawn_blocking
cannot be aborted
+because they are not async. If you call abort
on a spawn_blocking
+task, then this will not have any effect, and the task will continue
+running normally. The exception is if the task has not started running
+yet; in that case, calling abort
may prevent the task from starting.
When you shut down the executor, it will wait indefinitely for all blocking operations to
+finish. You can use shutdown_timeout
to stop waiting for them after a
+certain timeout. Be aware that this will still not cancel the tasks — they
+are simply allowed to keep running after the method returns. It is possible
+for a blocking task to be cancelled if it has not yet started running, but this
+is not guaranteed.
Note that if you are using the single threaded runtime, this function will +still spawn additional threads for blocking operations. The current-thread +scheduler’s single thread is only used for asynchronous code.
+In simple cases, it is sufficient to have the closure accept input +parameters at creation time and return a single value (or struct/tuple, etc.).
+For more complex situations in which it is desirable to stream data to or from
+the synchronous context, the mpsc channel
has blocking_send
and
+blocking_recv
methods for use in non-async code such as the thread created
+by spawn_blocking
.
Another option is SyncIoBridge
for cases where the synchronous context
+is operating on byte streams. For example, you might use an asynchronous
+HTTP client such as hyper to fetch data, but perform complex parsing
+of the payload body using a library written for synchronous I/O.
Finally, see also Bridging with sync code for discussions +around the opposite case of using Tokio as part of a larger synchronous +codebase.
+Pass an input value and receive result of computation:
+ +use tokio::task;
+
+// Initial input
+let mut v = "Hello, ".to_string();
+let res = task::spawn_blocking(move || {
+ // Stand-in for compute-heavy work or using synchronous APIs
+ v.push_str("world");
+ // Pass ownership of the value back to the asynchronous context
+ v
+}).await?;
+
+// `res` is the value returned from the thread
+assert_eq!(res.as_str(), "Hello, world");
Use a channel:
+ +use tokio::task;
+use tokio::sync::mpsc;
+
+let (tx, mut rx) = mpsc::channel(2);
+let start = 5;
+let worker = task::spawn_blocking(move || {
+ for x in 0..10 {
+ // Stand in for complex computation
+ tx.blocking_send(start + x).unwrap();
+ }
+});
+
+let mut acc = 0;
+while let Some(v) = rx.recv().await {
+ acc += v;
+}
+assert_eq!(acc, 95);
+worker.await.unwrap();
pub async fn yield_now()
Yields execution back to the Tokio runtime.
+A task yields by awaiting on yield_now()
, and may resume when that future
+completes (with no output.) The current task will be re-added as a pending
+task at the back of the pending queue. Any other pending tasks will be
+scheduled. No other waking is required for the task to continue.
See also the usage example in the task module.
+This function may not yield all the way up to the executor if there are any
+special combinators above it in the call stack. For example, if a
+tokio::select!
has another branch complete during the same poll as the
+yield_now()
, then the yield is not propagated all the way up to the
+runtime.
It is generally not guaranteed that the runtime behaves like you expect it
+to when deciding which task to schedule next after a call to yield_now()
.
+In particular, the runtime may choose to poll the task that just ran
+yield_now()
again immediately without polling any other tasks first. For
+example, the runtime will not drive the IO driver between every poll of a
+task, and this could result in the runtime polling the current task again
+immediately even if there is another task that could make progress if that
+other task is waiting for a notification from the IO driver.
In general, changes to the order in which the runtime polls tasks is not +considered a breaking change, and your program should be correct no matter +which order the runtime polls your tasks in.
+pub struct JoinError { /* private fields */ }
Task failed to execute to completion.
+Returns true if the error was caused by the task being cancelled.
+See the module level docs for more information on cancellation.
+Returns true if the error was caused by the task panicking.
+use std::panic;
+
+#[tokio::main]
+async fn main() {
+ let err = tokio::spawn(async {
+ panic!("boom");
+ }).await.unwrap_err();
+
+ assert!(err.is_panic());
+}
Consumes the join error, returning the object with which the task panicked.
+into_panic()
panics if the Error
does not represent the underlying
+task terminating with a panic. Use is_panic
to check the error reason
+or try_into_panic
for a variant that does not panic.
use std::panic;
+
+#[tokio::main]
+async fn main() {
+ let err = tokio::spawn(async {
+ panic!("boom");
+ }).await.unwrap_err();
+
+ if err.is_panic() {
+ // Resume the panic on the main task
+ panic::resume_unwind(err.into_panic());
+ }
+}
Consumes the join error, returning the object with which the task
+panicked if the task terminated due to a panic. Otherwise, self
is
+returned.
use std::panic;
+
+#[tokio::main]
+async fn main() {
+ let err = tokio::spawn(async {
+ panic!("boom");
+ }).await.unwrap_err();
+
+ if let Ok(reason) = err.try_into_panic() {
+ // Resume the panic on the main task
+ panic::resume_unwind(reason);
+ }
+}
pub struct JoinHandle<T> { /* private fields */ }
An owned permission to join on a task (await its termination).
+This can be thought of as the equivalent of std::thread::JoinHandle
+for a Tokio task rather than a thread. Note that the background task
+associated with this JoinHandle
started running immediately when you
+called spawn, even if you have not yet awaited the JoinHandle
.
A JoinHandle
detaches the associated task when it is dropped, which
+means that there is no longer any handle to the task, and no way to join
+on it.
This struct
is created by the task::spawn
and task::spawn_blocking
+functions.
The &mut JoinHandle<T>
type is cancel safe. If it is used as the event
+in a tokio::select!
statement and some other branch completes first,
+then it is guaranteed that the output of the task is not lost.
If a JoinHandle
is dropped, then the task continues running in the
+background and its return value is lost.
Creation from task::spawn
:
use tokio::task;
+
+let join_handle: task::JoinHandle<_> = task::spawn(async {
+ // some work here
+});
Creation from task::spawn_blocking
:
use tokio::task;
+
+let join_handle: task::JoinHandle<_> = task::spawn_blocking(|| {
+ // some blocking work here
+});
The generic parameter T
in JoinHandle<T>
is the return type of the spawned task.
+If the return value is an i32
, the join handle has type JoinHandle<i32>
:
use tokio::task;
+
+let join_handle: task::JoinHandle<i32> = task::spawn(async {
+ 5 + 3
+});
+
If the task does not have a return value, the join handle has type JoinHandle<()>
:
use tokio::task;
+
+let join_handle: task::JoinHandle<()> = task::spawn(async {
+ println!("I return nothing.");
+});
Note that handle.await
doesn’t give you the return type directly. It is wrapped in a
+Result
because panics in the spawned task are caught by Tokio. The ?
operator has
+to be double chained to extract the returned value:
use tokio::task;
+use std::io;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let join_handle: task::JoinHandle<Result<i32, io::Error>> = tokio::spawn(async {
+ Ok(5 + 3)
+ });
+
+ let result = join_handle.await??;
+ assert_eq!(result, 8);
+ Ok(())
+}
If the task panics, the error is a JoinError
that contains the panic:
use tokio::task;
+use std::io;
+use std::panic;
+
+#[tokio::main]
+async fn main() -> io::Result<()> {
+ let join_handle: task::JoinHandle<Result<i32, io::Error>> = tokio::spawn(async {
+ panic!("boom");
+ });
+
+ let err = join_handle.await.unwrap_err();
+ assert!(err.is_panic());
+ Ok(())
+}
+
Child being detached and outliving its parent:
+ +use tokio::task;
+use tokio::time;
+use std::time::Duration;
+
+let original_task = task::spawn(async {
+ let _detached_task = task::spawn(async {
+ // Here we sleep to make sure that the first task returns before.
+ time::sleep(Duration::from_millis(10)).await;
+ // This will be called, even though the JoinHandle is dropped.
+ println!("♫ Still alive ♫");
+ });
+});
+
+original_task.await.expect("The task being joined has panicked");
+println!("Original task is joined.");
+
+// We make sure that the new task has time to run, before the main
+// task returns.
+
+time::sleep(Duration::from_millis(1000)).await;
Abort the task associated with the handle.
+Awaiting a cancelled task might complete as usual if the task was
+already completed at the time it was cancelled, but most likely it
+will fail with a cancelled JoinError
.
Be aware that tasks spawned using spawn_blocking
cannot be aborted
+because they are not async. If you call abort
on a spawn_blocking
+task, then this will not have any effect, and the task will continue
+running normally. The exception is if the task has not started running
+yet; in that case, calling abort
may prevent the task from starting.
See also the module level docs for more information on cancellation.
+ +use tokio::time;
+
+let mut handles = Vec::new();
+
+handles.push(tokio::spawn(async {
+ time::sleep(time::Duration::from_secs(10)).await;
+ true
+}));
+
+handles.push(tokio::spawn(async {
+ time::sleep(time::Duration::from_secs(10)).await;
+ false
+}));
+
+for handle in &handles {
+ handle.abort();
+}
+
+for handle in handles {
+ assert!(handle.await.unwrap_err().is_cancelled());
+}
Checks if the task associated with this JoinHandle
has finished.
Please note that this method can return false
even if abort
has been
+called on the task. This is because the cancellation process may take
+some time, and this method does not return true
until it has
+completed.
use tokio::time;
+
+let handle1 = tokio::spawn(async {
+ // do some stuff here
+});
+let handle2 = tokio::spawn(async {
+ // do some other stuff here
+ time::sleep(time::Duration::from_secs(10)).await;
+});
+// Wait for the task to finish
+handle2.abort();
+time::sleep(time::Duration::from_secs(1)).await;
+assert!(handle1.is_finished());
+assert!(handle2.is_finished());
Returns a new AbortHandle
that can be used to remotely abort this task.
Awaiting a task cancelled by the AbortHandle
might complete as usual if the task was
+already completed at the time it was cancelled, but most likely it
+will fail with a cancelled JoinError
.
use tokio::{time, task};
+
+let mut handles = Vec::new();
+
+handles.push(tokio::spawn(async {
+ time::sleep(time::Duration::from_secs(10)).await;
+ true
+}));
+
+handles.push(tokio::spawn(async {
+ time::sleep(time::Duration::from_secs(10)).await;
+ false
+}));
+
+let abort_handles: Vec<task::AbortHandle> = handles.iter().map(|h| h.abort_handle()).collect();
+
+for handle in abort_handles {
+ handle.abort();
+}
+
+for handle in handles {
+ assert!(handle.await.unwrap_err().is_cancelled());
+}
pub fn interval(period: Duration) -> Interval
Creates new Interval
that yields with interval of period
. The first
+tick completes immediately. The default [MissedTickBehavior
] is
+Burst
, but this can be configured
+by calling set_missed_tick_behavior
.
An interval will tick indefinitely. At any time, the Interval
value can
+be dropped. This cancels the interval.
This function is equivalent to
+interval_at(Instant::now(), period)
.
This function panics if period
is zero.
use tokio::time::{self, Duration};
+
+#[tokio::main]
+async fn main() {
+ let mut interval = time::interval(Duration::from_millis(10));
+
+ interval.tick().await; // ticks immediately
+ interval.tick().await; // ticks after 10ms
+ interval.tick().await; // ticks after 10ms
+
+ // approximately 20ms have elapsed.
+}
A simple example using interval
to execute a task every two seconds.
The difference between interval
and sleep
is that an Interval
+measures the time since the last tick, which means that .tick().await
+may wait for a shorter time than the duration specified for the interval
+if some time has passed between calls to .tick().await
.
If the tick in the example below was replaced with sleep
, the task
+would only be executed once every three seconds, and not every two
+seconds.
use tokio::time;
+
+async fn task_that_takes_a_second() {
+ println!("hello");
+ time::sleep(time::Duration::from_secs(1)).await
+}
+
+#[tokio::main]
+async fn main() {
+ let mut interval = time::interval(time::Duration::from_secs(2));
+ for _i in 0..5 {
+ interval.tick().await;
+ task_that_takes_a_second().await;
+ }
+}
pub fn interval_at(start: Instant, period: Duration) -> Interval
Creates new Interval
that yields with interval of period
with the
+first tick completing at start
. The default [MissedTickBehavior
] is
+Burst
, but this can be configured
+by calling set_missed_tick_behavior
.
An interval will tick indefinitely. At any time, the Interval
value can
+be dropped. This cancels the interval.
This function panics if period
is zero.
use tokio::time::{interval_at, Duration, Instant};
+
+#[tokio::main]
+async fn main() {
+ let start = Instant::now() + Duration::from_millis(50);
+ let mut interval = interval_at(start, Duration::from_millis(10));
+
+ interval.tick().await; // ticks after 50ms
+ interval.tick().await; // ticks after 10ms
+ interval.tick().await; // ticks after 10ms
+
+ // approximately 70ms have elapsed.
+}
pub fn sleep(duration: Duration) -> Sleep ⓘ
Waits until duration
has elapsed.
Equivalent to sleep_until(Instant::now() + duration)
. An asynchronous
+analog to std::thread::sleep
.
No work is performed while awaiting on the sleep future to complete. Sleep
+operates at millisecond granularity and should not be used for tasks that
+require high-resolution timers. The implementation is platform specific,
+and some platforms (specifically Windows) will provide timers with a
+larger resolution than 1 ms.
To run something regularly on a schedule, see interval
.
The maximum duration for a sleep is 68719476734 milliseconds (approximately 2.2 years).
+Canceling a sleep instance is done by dropping the returned future. No additional +cleanup work is required.
+Wait 100ms and print “100 ms have elapsed”.
+ +use tokio::time::{sleep, Duration};
+
+#[tokio::main]
+async fn main() {
+ sleep(Duration::from_millis(100)).await;
+ println!("100 ms have elapsed");
+}
See the documentation for the Sleep
type for more examples.
This function panics if there is no current timer set.
+It can be triggered when Builder::enable_time
or
+Builder::enable_all
are not included in the builder.
It can also panic whenever a timer is created outside of a
+Tokio runtime. That is why rt.block_on(sleep(...))
will panic,
+since the function is executed outside of the runtime.
+Whereas rt.block_on(async {sleep(...).await})
doesn’t panic.
+And this is because wrapping the function on an async makes it lazy,
+and so gets executed inside the runtime successfully without
+panicking.
pub fn sleep_until(deadline: Instant) -> Sleep ⓘ
Waits until deadline
is reached.
No work is performed while awaiting on the sleep future to complete. Sleep
+operates at millisecond granularity and should not be used for tasks that
+require high-resolution timers.
To run something regularly on a schedule, see interval
.
Canceling a sleep instance is done by dropping the returned future. No additional +cleanup work is required.
+Wait 100ms and print “100 ms have elapsed”.
+ +use tokio::time::{sleep_until, Instant, Duration};
+
+#[tokio::main]
+async fn main() {
+ sleep_until(Instant::now() + Duration::from_millis(100)).await;
+ println!("100 ms have elapsed");
+}
See the documentation for the Sleep
type for more examples.
This function panics if there is no current timer set.
+It can be triggered when Builder::enable_time
or
+Builder::enable_all
are not included in the builder.
It can also panic whenever a timer is created outside of a
+Tokio runtime. That is why rt.block_on(sleep(...))
will panic,
+since the function is executed outside of the runtime.
+Whereas rt.block_on(async {sleep(...).await})
doesn’t panic.
+And this is because wrapping the function on an async makes it lazy,
+and so gets executed inside the runtime successfully without
+panicking.
pub fn timeout<F>(duration: Duration, future: F) -> Timeout<F> ⓘwhere
+ F: Future,
Requires a Future
to complete before the specified duration has elapsed.
If the future completes before the duration has elapsed, then the completed +value is returned. Otherwise, an error is returned and the future is +canceled.
+Note that the timeout is checked before polling the future, so if the future +does not yield during execution then it is possible for the future to complete +and exceed the timeout without returning an error.
+This function returns a future whose return type is Result
<T,
Elapsed
>
, where T
is the
+return type of the provided future.
If the provided future completes immediately, then the future returned from
+this function is guaranteed to complete immediately with an Ok
variant
+no matter the provided duration.
Cancelling a timeout is done by dropping the future. No additional cleanup +or other work is required.
+The original future may be obtained by calling Timeout::into_inner
. This
+consumes the Timeout
.
Create a new Timeout
set to expire in 10 milliseconds.
use tokio::time::timeout;
+use tokio::sync::oneshot;
+
+use std::time::Duration;
+
+let (tx, rx) = oneshot::channel();
+
+// Wrap the future with a `Timeout` set to expire in 10 milliseconds.
+if let Err(_) = timeout(Duration::from_millis(10), rx).await {
+ println!("did not receive value within 10 ms");
+}
This function panics if there is no current timer set.
+It can be triggered when Builder::enable_time
or
+Builder::enable_all
are not included in the builder.
It can also panic whenever a timer is created outside of a
+Tokio runtime. That is why rt.block_on(sleep(...))
will panic,
+since the function is executed outside of the runtime.
+Whereas rt.block_on(async {sleep(...).await})
doesn’t panic.
+And this is because wrapping the function on an async makes it lazy,
+and so gets executed inside the runtime successfully without
+panicking.
Utilities for tracking time (Tokio re-exports).
+Duration
.interval
and interval_at
.sleep
and sleep_until
.timeout
and timeout_at
.Interval
that yields with interval of period
. The first
+tick completes immediately. The default [MissedTickBehavior
] is
+Burst
, but this can be configured
+by calling set_missed_tick_behavior
.Interval
that yields with interval of period
with the
+first tick completing at start
. The default [MissedTickBehavior
] is
+Burst
, but this can be configured
+by calling set_missed_tick_behavior
.duration
has elapsed.deadline
is reached.Future
to complete before the specified duration has elapsed.pub struct Instant { /* private fields */ }
A measurement of a monotonically nondecreasing clock.
+Opaque and useful only with Duration
.
Instants are always guaranteed to be no less than any previously measured +instant when created, and are often useful for tasks such as measuring +benchmarks or timing how long an operation takes.
+Note, however, that instants are not guaranteed to be steady. In other +words, each tick of the underlying clock may not be the same length (e.g. +some seconds may be longer than others). An instant may jump forwards or +experience time dilation (slow down or speed up), but it will never go +backwards.
+Instants are opaque types that can only be compared to one another. There is +no method to get “the number of seconds” from an instant. Instead, it only +allows measuring the duration between two instants (or comparing two +instants).
+The size of an Instant
struct may vary depending on the target operating
+system.
This type wraps the inner std
variant and is used to align the Tokio
+clock for uses of now()
. This can be useful for testing where you can
+take advantage of time::pause()
and time::advance()
.
Returns an instant corresponding to “now”.
+use tokio::time::Instant;
+
+let now = Instant::now();
Returns the amount of time elapsed from another instant to this one, or +zero duration if that instant is later than this one.
+Returns the amount of time elapsed from another instant to this one, or +None if that instant is later than this one.
+use tokio::time::{Duration, Instant, sleep};
+
+#[tokio::main]
+async fn main() {
+ let now = Instant::now();
+ sleep(Duration::new(1, 0)).await;
+ let new_now = Instant::now();
+ println!("{:?}", new_now.checked_duration_since(now));
+ println!("{:?}", now.checked_duration_since(new_now)); // None
+}
Returns the amount of time elapsed from another instant to this one, or +zero duration if that instant is later than this one.
+use tokio::time::{Duration, Instant, sleep};
+
+#[tokio::main]
+async fn main() {
+ let now = Instant::now();
+ sleep(Duration::new(1, 0)).await;
+ let new_now = Instant::now();
+ println!("{:?}", new_now.saturating_duration_since(now));
+ println!("{:?}", now.saturating_duration_since(new_now)); // 0ns
+}
Returns the amount of time elapsed since this instant was created, +or zero duration if that this instant is in the future.
+use tokio::time::{Duration, Instant, sleep};
+
+#[tokio::main]
+async fn main() {
+ let instant = Instant::now();
+ let three_secs = Duration::from_secs(3);
+ sleep(three_secs).await;
+ assert!(instant.elapsed() >= three_secs);
+}
Returns Some(t)
where t
is the time self + duration
if t
can be
+represented as Instant
(which means it’s inside the bounds of the
+underlying data structure), None
otherwise.
Returns Some(t)
where t
is the time self - duration
if t
can be
+represented as Instant
(which means it’s inside the bounds of the
+underlying data structure), None
otherwise.
+=
operation. Read moreself
and other
) and is used by the <=
+operator. Read more-=
operation. Read morepub struct Interval { /* private fields */ }
Interval returned by interval
and interval_at
.
This type allows you to wait on a sequence of instants with a certain
+duration between each instant. Unlike calling sleep
in a loop, this lets
+you count the time spent between the calls to sleep
as well.
An Interval
can be turned into a Stream
with IntervalStream
.
Completes when the next instant in the interval has been reached.
+This method is cancellation safe. If tick
is used as the branch in a tokio::select!
and
+another branch completes first, then no tick has been consumed.
use tokio::time;
+
+use std::time::Duration;
+
+#[tokio::main]
+async fn main() {
+ let mut interval = time::interval(Duration::from_millis(10));
+
+ interval.tick().await;
+ // approximately 0ms have elapsed. The first tick completes immediately.
+ interval.tick().await;
+ interval.tick().await;
+
+ // approximately 20ms have elapsed.
+}
Polls for the next instant in the interval to be reached.
+This method can return the following values:
+Poll::Pending
if the next instant has not yet been reached.Poll::Ready(instant)
if the next instant has been reached.When this method returns Poll::Pending
, the current task is scheduled
+to receive a wakeup when the instant has elapsed. Note that on multiple
+calls to poll_tick
, only the Waker
from the
+Context
passed to the most recent call is scheduled to receive a
+wakeup.
Resets the interval to complete one period after the current time.
+This method ignores [MissedTickBehavior
] strategy.
This is equivalent to calling reset_at(Instant::now() + period)
.
use tokio::time;
+
+use std::time::Duration;
+
+#[tokio::main]
+async fn main() {
+ let mut interval = time::interval(Duration::from_millis(100));
+
+ interval.tick().await;
+
+ time::sleep(Duration::from_millis(50)).await;
+ interval.reset();
+
+ interval.tick().await;
+ interval.tick().await;
+
+ // approximately 250ms have elapsed.
+}
Resets the interval immediately.
+This method ignores [MissedTickBehavior
] strategy.
This is equivalent to calling reset_at(Instant::now())
.
use tokio::time;
+
+use std::time::Duration;
+
+#[tokio::main]
+async fn main() {
+ let mut interval = time::interval(Duration::from_millis(100));
+
+ interval.tick().await;
+
+ time::sleep(Duration::from_millis(50)).await;
+ interval.reset_immediately();
+
+ interval.tick().await;
+ interval.tick().await;
+
+ // approximately 150ms have elapsed.
+}
Resets the interval after the specified std::time::Duration
.
This method ignores [MissedTickBehavior
] strategy.
This is equivalent to calling reset_at(Instant::now() + after)
.
use tokio::time;
+
+use std::time::Duration;
+
+#[tokio::main]
+async fn main() {
+ let mut interval = time::interval(Duration::from_millis(100));
+ interval.tick().await;
+
+ time::sleep(Duration::from_millis(50)).await;
+
+ let after = Duration::from_millis(20);
+ interval.reset_after(after);
+
+ interval.tick().await;
+ interval.tick().await;
+
+ // approximately 170ms have elapsed.
+}
Resets the interval to a crate::time::Instant
deadline.
Sets the next tick to expire at the given instant. If the instant is in
+the past, then the [MissedTickBehavior
] strategy will be used to
+catch up. If the instant is in the future, then the next tick will
+complete at the given instant, even if that means that it will sleep for
+longer than the duration of this Interval
. If the Interval
had
+any missed ticks before calling this method, then those are discarded.
use tokio::time::{self, Instant};
+
+use std::time::Duration;
+
+#[tokio::main]
+async fn main() {
+ let mut interval = time::interval(Duration::from_millis(100));
+ interval.tick().await;
+
+ time::sleep(Duration::from_millis(50)).await;
+
+ let deadline = Instant::now() + Duration::from_millis(30);
+ interval.reset_at(deadline);
+
+ interval.tick().await;
+ interval.tick().await;
+
+ // approximately 180ms have elapsed.
+}
Returns the [MissedTickBehavior
] strategy currently being used.
Sets the [MissedTickBehavior
] strategy that should be used.
pub struct Sleep { /* private fields */ }
Future returned by sleep
and sleep_until
.
This type does not implement the Unpin
trait, which means that if you
+use it with select!
or by calling poll
, you have to pin it first.
+If you use it with .await
, this does not apply.
Wait 100ms and print “100 ms have elapsed”.
+ +use tokio::time::{sleep, Duration};
+
+#[tokio::main]
+async fn main() {
+ sleep(Duration::from_millis(100)).await;
+ println!("100 ms have elapsed");
+}
Use with select!
. Pinning the Sleep
with tokio::pin!
is
+necessary when the same Sleep
is selected on multiple times.
use tokio::time::{self, Duration, Instant};
+
+#[tokio::main]
+async fn main() {
+ let sleep = time::sleep(Duration::from_millis(10));
+ tokio::pin!(sleep);
+
+ loop {
+ tokio::select! {
+ () = &mut sleep => {
+ println!("timer elapsed");
+ sleep.as_mut().reset(Instant::now() + Duration::from_millis(50));
+ },
+ }
+ }
+}
Use in a struct with boxing. By pinning the Sleep
with a Box
, the
+HasSleep
struct implements Unpin
, even though Sleep
does not.
use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+use tokio::time::Sleep;
+
+struct HasSleep {
+ sleep: Pin<Box<Sleep>>,
+}
+
+impl Future for HasSleep {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
+ self.sleep.as_mut().poll(cx)
+ }
+}
Use in a struct with pin projection. This method avoids the Box
, but
+the HasSleep
struct will not be Unpin
as a consequence.
use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+use tokio::time::Sleep;
+use pin_project_lite::pin_project;
+
+pin_project! {
+ struct HasSleep {
+ #[pin]
+ sleep: Sleep,
+ }
+}
+
+impl Future for HasSleep {
+ type Output = ();
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
+ self.project().sleep.poll(cx)
+ }
+}
Returns true
if Sleep
has elapsed.
A Sleep
instance is elapsed when the requested duration has elapsed.
Resets the Sleep
instance to a new deadline.
Calling this function allows changing the instant at which the Sleep
+future completes without having to create new associated state.
This function can be called both before and after the future has +completed.
+To call this method, you will usually combine the call with
+Pin::as_mut
, which lets you call the method without consuming the
+Sleep
itself.
use tokio::time::{Duration, Instant};
+
+let sleep = tokio::time::sleep(Duration::from_millis(10));
+tokio::pin!(sleep);
+
+sleep.as_mut().reset(Instant::now() + Duration::from_millis(20));
See also the top-level examples.
+pub struct Timeout<T> { /* private fields */ }
Future returned by timeout
and timeout_at
.
Redirecting to ../../actix_server/enum.MpTcp.html...
+ + + \ No newline at end of file diff --git a/actix_server/builder/struct.ServerBuilder.html b/actix_server/builder/struct.ServerBuilder.html new file mode 100644 index 0000000000..177f40ba35 --- /dev/null +++ b/actix_server/builder/struct.ServerBuilder.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_server/struct.ServerBuilder.html...
+ + + \ No newline at end of file diff --git a/actix_server/enum.MpTcp.html b/actix_server/enum.MpTcp.html new file mode 100644 index 0000000000..6ed5177ab0 --- /dev/null +++ b/actix_server/enum.MpTcp.html @@ -0,0 +1,28 @@ +pub enum MpTcp {
+ Disabled,
+ TcpFallback,
+ NoFallback,
+}
Multipath TCP (MPTCP) preference.
+Currently only useful on Linux.
+Also see ServerBuilder::mptcp()
.
MPTCP will not be used when binding sockets.
+MPTCP will be attempted when binding sockets. If errors occur, regular TCP will be +attempted, too.
+MPTCP will be used when binding sockets (with no fallback).
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRedirecting to ../../actix_server/struct.ServerHandle.html...
+ + + \ No newline at end of file diff --git a/actix_server/index.html b/actix_server/index.html new file mode 100644 index 0000000000..e11763622a --- /dev/null +++ b/actix_server/index.html @@ -0,0 +1,2 @@ +Redirecting to ../../actix_server/struct.Server.html...
+ + + \ No newline at end of file diff --git a/actix_server/sidebar-items.js b/actix_server/sidebar-items.js new file mode 100644 index 0000000000..9e69b2e9ce --- /dev/null +++ b/actix_server/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["MpTcp"],"struct":["Server","ServerBuilder","ServerHandle","TestServer"]}; \ No newline at end of file diff --git a/actix_server/struct.Server.html b/actix_server/struct.Server.html new file mode 100644 index 0000000000..fc72d75138 --- /dev/null +++ b/actix_server/struct.Server.html @@ -0,0 +1,165 @@ +pub struct Server { /* private fields */ }
General purpose TCP server that runs services receiving Tokio TcpStream
s.
Handles creating worker threads, restarting faulted workers, connection accepting, and +back-pressure logic.
+Creates a worker per CPU core (or the number specified in ServerBuilder::workers
) and
+distributes connections with a round-robin strategy.
The Server must be awaited or polled in order to start running. It will resolve when the +server has fully shut down.
+On UNIX systems, SIGTERM
will start a graceful shutdown and SIGQUIT
or SIGINT
will start a
+forced shutdown. On Windows, a Ctrl-C signal will start a forced shutdown.
A graceful shutdown will wait for all workers to stop first.
+The following is a TCP echo server. Test using telnet 127.0.0.1 8080
.
use std::io;
+
+use actix_rt::net::TcpStream;
+use actix_server::Server;
+use actix_service::{fn_service, ServiceFactoryExt as _};
+use bytes::BytesMut;
+use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
+
+#[actix_rt::main]
+async fn main() -> io::Result<()> {
+ let bind_addr = ("127.0.0.1", 8080);
+
+ Server::build()
+ .bind("echo", bind_addr, move || {
+ fn_service(move |mut stream: TcpStream| {
+ async move {
+ let mut size = 0;
+ let mut buf = BytesMut::new();
+
+ loop {
+ match stream.read_buf(&mut buf).await {
+ // end of stream; bail from loop
+ Ok(0) => break,
+
+ // write bytes back to stream
+ Ok(bytes_read) => {
+ stream.write_all(&buf[size..]).await.unwrap();
+ size += bytes_read;
+ }
+
+ Err(err) => {
+ eprintln!("Stream Error: {:?}", err);
+ return Err(());
+ }
+ }
+ }
+
+ Ok(())
+ }
+ })
+ .map_err(|err| eprintln!("Service Error: {:?}", err))
+ })?
+ .run()
+ .await
+}
Create server build.
+Get a Server
handle that can be used issue commands and change it’s state.
See ServerHandle for usage.
+f
. Read morepoll
will never again be called once it has
+completed. This method can be used to turn any Future
into a
+FusedFuture
. Read moreFuture<Output = T>
into a
+TryFuture<Ok = T, Error = ()
>.Future<Output = T>
into a
+TryFuture<Ok = T, Error = Never
>.Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ServerBuilder { /* private fields */ }
Server builder.
+Create new Server builder instance
+Sets number of workers to start.
+See bind()
for more details on how worker count affects the number of
+server factory instantiations.
The default worker count is the determined by std::thread::available_parallelism()
. See
+its documentation to determine what behavior you should expect when server is run.
num
must be greater than 0.
Panics if num
is 0.
Set max number of threads for each worker’s blocking task thread pool.
+One thread pool is set up per worker; not shared across workers.
+let builder = ServerBuilder::new()
+ .workers(4) // server has 4 worker thread.
+ .worker_max_blocking_threads(4); // every worker has 4 max blocking threads.
See [tokio::runtime::Builder::max_blocking_threads] for behavior reference.
+Set the maximum number of pending connections.
+This refers to the number of clients that can be waiting to be served. Exceeding this number +results in the client getting an error when attempting to connect. It should only affect +servers under significant load.
+Generally set in the 64-2048 range. Default value is 2048.
+This method should be called before bind()
method call.
Sets MultiPath TCP (MPTCP) preference on bound sockets.
+Multipath TCP (MPTCP) builds on top of TCP to improve connection redundancy and performance +by sharing a network data stream across multiple underlying TCP sessions. See mptcp.dev +for more info about MPTCP itself.
+MPTCP is available on Linux kernel version 5.6 and higher. In addition, you’ll also need to
+ensure the kernel option is enabled using sysctl net.mptcp.enabled=1
.
This method will have no effect if called after a bind()
.
Sets the maximum per-worker number of concurrent connections.
+All socket listeners will stop accepting connections when this limit is reached for +each worker.
+By default max connections is set to a 25k per worker.
+Sets flag to stop Actix System
after server shutdown.
This has no effect when server is running in a Tokio-only runtime.
+Disables OS signal handling.
+Timeout for graceful workers shutdown in seconds.
+After receiving a stop signal, workers have this much time to finish serving requests. +Workers still alive after the timeout are force dropped.
+By default shutdown timeout sets to 30 seconds.
+Adds new service to the server.
+Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.
+The factory
will be instantiated multiple times in most scenarios. The number of
+instantiations is number of workers
× number of sockets resolved by
+addrs
.
For example, if you’ve manually set workers
to 2, and use 127.0.0.1
+as the bind addrs
, then factory
will be instantiated twice. However, using localhost
+as the bind addrs
can often resolve to both 127.0.0.1
(IPv4) and ::1
(IPv6), causing
+the factory
to be instantiated 4 times (2 workers × 2 bind addresses).
Using a bind address of 0.0.0.0
, which signals to use all interfaces, may also multiple
+the number of instantiations in a similar way.
Returns an io::Error
if:
addrs
cannot be resolved into one or more socket addresses;Adds new service to the server using a UDS (unix domain socket) listener already bound.
+Useful when running as a systemd service and a socket FD is acquired externally.
+The factory
will be instantiated multiple times in most scenarios. The number of
+instantiations is: number of workers
.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ServerHandle { /* private fields */ }
Server handle.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TestServer;
A testing server.
+TestServer
is very simple test server that simplify process of writing integration tests for
+network applications.
use actix_service::fn_service;
+use actix_server::TestServer;
+
+#[actix_rt::main]
+async fn main() {
+ let srv = TestServer::start(|| fn_service(
+ |sock| async move {
+ println!("New connection: {:?}", sock);
+ Ok::<_, ()>(())
+ }
+ ));
+
+ println!("SOCKET: {:?}", srv.connect());
+}
Start new TestServer
using application factory and default server config.
Start new TestServer
using application factory and server builder.
Get first available unused local address.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRedirecting to ../../actix_server/struct.TestServer.html...
+ + + \ No newline at end of file diff --git a/actix_service/all.html b/actix_service/all.html new file mode 100644 index 0000000000..966a24c712 --- /dev/null +++ b/actix_service/all.html @@ -0,0 +1 @@ +Redirecting to ../../actix_service/fn.apply_fn.html...
+ + + \ No newline at end of file diff --git a/actix_service/apply/fn.apply_fn_factory.html b/actix_service/apply/fn.apply_fn_factory.html new file mode 100644 index 0000000000..19abc6a55e --- /dev/null +++ b/actix_service/apply/fn.apply_fn_factory.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/fn.apply_fn_factory.html...
+ + + \ No newline at end of file diff --git a/actix_service/apply_cfg/fn.apply_cfg.html b/actix_service/apply_cfg/fn.apply_cfg.html new file mode 100644 index 0000000000..8844f540eb --- /dev/null +++ b/actix_service/apply_cfg/fn.apply_cfg.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/fn.apply_cfg.html...
+ + + \ No newline at end of file diff --git a/actix_service/apply_cfg/fn.apply_cfg_factory.html b/actix_service/apply_cfg/fn.apply_cfg_factory.html new file mode 100644 index 0000000000..cf95833565 --- /dev/null +++ b/actix_service/apply_cfg/fn.apply_cfg_factory.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/fn.apply_cfg_factory.html...
+ + + \ No newline at end of file diff --git a/actix_service/boxed/fn.factory.html b/actix_service/boxed/fn.factory.html new file mode 100644 index 0000000000..641f0d59b9 --- /dev/null +++ b/actix_service/boxed/fn.factory.html @@ -0,0 +1,11 @@ +pub fn factory<SF, Req>(
+ factory: SF
+) -> BoxServiceFactory<SF::Config, Req, SF::Response, SF::Error, SF::InitError>
Wraps a service factory that returns service trait objects.
+pub fn rc_service<S, Req>(service: S) -> RcService<Req, S::Response, S::Error>
Wraps service as a trait object using RcService
.
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error>
Wraps service as a trait object using BoxService
.
Trait object forms of services and service factories.
+RcService
.BoxService
.Box
.Rc
.pub struct BoxServiceFactory<Cfg, Req, Res, Err, InitErr>(/* private fields */);
Wrapper for a service factory that will map it’s services to boxed trait object services.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
pub type BoxService<Req, Res, Err> = Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
Type alias for service trait object using Box
.
struct BoxService<Req, Res, Err>(/* private fields */);
pub type RcService<Req, Res, Err> = Rc<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
Type alias for service trait object using Rc
.
struct RcService<Req, Res, Err> { /* private fields */ }
Redirecting to ../../actix_service/trait.ServiceExt.html...
+ + + \ No newline at end of file diff --git a/actix_service/ext/trait.ServiceFactoryExt.html b/actix_service/ext/trait.ServiceFactoryExt.html new file mode 100644 index 0000000000..84e5329c62 --- /dev/null +++ b/actix_service/ext/trait.ServiceFactoryExt.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/trait.ServiceFactoryExt.html...
+ + + \ No newline at end of file diff --git a/actix_service/ext/trait.TransformExt.html b/actix_service/ext/trait.TransformExt.html new file mode 100644 index 0000000000..a50c50ec28 --- /dev/null +++ b/actix_service/ext/trait.TransformExt.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/trait.TransformExt.html...
+ + + \ No newline at end of file diff --git a/actix_service/fn.apply.html b/actix_service/fn.apply.html new file mode 100644 index 0000000000..c1d07dc2fc --- /dev/null +++ b/actix_service/fn.apply.html @@ -0,0 +1,5 @@ +pub fn apply<T, S, I, Req>(t: T, factory: I) -> ApplyTransform<T, S, Req>where
+ I: IntoServiceFactory<S, Req>,
+ S: ServiceFactory<Req>,
+ T: Transform<S::Service, Req, InitError = S::InitError>,
pub fn apply_cfg<S1, Req, F, Cfg, Fut, S2, Err>(
+ srv: S1,
+ f: F
+) -> impl ServiceFactory<Req, Config = Cfg, Response = S2::Response, Error = S2::Error, Service = S2, InitError = Err, Future = Fut> + Clone
Convert Fn(Config, &Service1) -> Future<Service2>
fn to a service factory.
pub fn apply_cfg_factory<SF, Req, F, Cfg, Fut, S>(
+ factory: SF,
+ f: F
+) -> impl ServiceFactory<Req, Config = Cfg, Response = S::Response, Error = S::Error, Service = S, InitError = SF::InitError> + Clone
Convert Fn(Config, &ServiceFactory1) -> Future<ServiceFactory2>
fn to a service factory.
Service1 get constructed from T
factory.
pub fn apply_fn<I, S, F, Fut, Req, In, Res, Err>(
+ service: I,
+ wrap_fn: F
+) -> Apply<S, F, Req, In, Res, Err>
Apply transform function to a service.
+The In and Out type params refer to the request and response types for the wrapped service.
+pub fn apply_fn_factory<I, SF, F, Fut, Req, In, Res, Err>(
+ service: I,
+ f: F
+) -> ApplyFactory<SF, F, Req, In, Res, Err>where
+ I: IntoServiceFactory<SF, In>,
+ SF: ServiceFactory<In, Error = Err>,
+ F: Fn(Req, &SF::Service) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
Service factory that produces apply_fn
service.
The In and Out type params refer to the request and response types for the wrapped service.
+pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(
+ f: F
+) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
Create ServiceFactory
for function that can produce services
use std::io;
+use actix_service::{fn_factory, fn_service, Service, ServiceFactory};
+use futures_util::future::ok;
+
+/// Service that divides two usize values.
+async fn div((x, y): (usize, usize)) -> Result<usize, io::Error> {
+ if y == 0 {
+ Err(io::Error::new(io::ErrorKind::Other, "divide by zero"))
+ } else {
+ Ok(x / y)
+ }
+}
+
+#[actix_rt::main]
+async fn main() -> io::Result<()> {
+ // Create service factory that produces `div` services
+ let factory = fn_factory(|| {
+ ok::<_, io::Error>(fn_service(div))
+ });
+
+ // construct new service
+ let srv = factory.new_service(()).await?;
+
+ // now we can use `div` service
+ let result = srv.call((10, 20)).await?;
+
+ println!("10 / 20 = {}", result);
+
+ Ok(())
+}
pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Req, Err>(
+ f: F
+) -> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
Create ServiceFactory
for function that accepts config argument and can produce services
Any function that has following form Fn(Config) -> Future<Output = Service>
could act as
+a ServiceFactory
.
use std::io;
+use actix_service::{fn_factory_with_config, fn_service, Service, ServiceFactory};
+use futures_util::future::ok;
+
+#[actix_rt::main]
+async fn main() -> io::Result<()> {
+ // Create service factory. factory uses config argument for
+ // services it generates.
+ let factory = fn_factory_with_config(|y: usize| {
+ ok::<_, io::Error>(fn_service(move |x: usize| ok::<_, io::Error>(x * y)))
+ });
+
+ // construct new service with config argument
+ let srv = factory.new_service(10).await?;
+
+ let result = srv.call(10).await?;
+ assert_eq!(result, 100);
+
+ println!("10 * 10 = {}", result);
+ Ok(())
+}
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(
+ f: F
+) -> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
Create ServiceFactory
for function that can act as a Service
pub fn into_service<I, S, Req>(tp: I) -> Swhere
+ I: IntoService<S, Req>,
+ S: Service<Req>,
Convert object of type U
to a service S
pub fn map_config<I, SF, Req, F, Cfg>(
+ factory: I,
+ f: F
+) -> MapConfig<SF, Req, F, Cfg>
Adapt external config argument to a config for provided service factory
+Note that this function consumes the receiving service factory and returns +a wrapped version of it.
+pub fn unit_config<I, SF, Cfg, Req>(factory: I) -> UnitConfig<SF, Cfg, Req>
Replace config with unit.
+Redirecting to ../../actix_service/fn.fn_factory.html...
+ + + \ No newline at end of file diff --git a/actix_service/fn_service/fn.fn_factory_with_config.html b/actix_service/fn_service/fn.fn_factory_with_config.html new file mode 100644 index 0000000000..e828658a3f --- /dev/null +++ b/actix_service/fn_service/fn.fn_factory_with_config.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/fn.fn_factory_with_config.html...
+ + + \ No newline at end of file diff --git a/actix_service/fn_service/fn.fn_service.html b/actix_service/fn_service/fn.fn_service.html new file mode 100644 index 0000000000..64dfab958b --- /dev/null +++ b/actix_service/fn_service/fn.fn_service.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/fn.fn_service.html...
+ + + \ No newline at end of file diff --git a/actix_service/index.html b/actix_service/index.html new file mode 100644 index 0000000000..d37f667a48 --- /dev/null +++ b/actix_service/index.html @@ -0,0 +1,3 @@ +See Service
docs for information on this crate’s foundational trait.
poll_ready
that always signals readiness.poll_ready
that forwards readiness checks to a
+named struct field.Service
ServiceFactory
Request
to a Response
.Service
s that provides a variety of convenient adapters.Service
s.ServiceFactory
s that provides a variety of convenient adapters.Transform
s that provides a variety of convenient adapters.Fn(Config, &Service1) -> Future<Service2>
fn to a service factory.Fn(Config, &ServiceFactory1) -> Future<ServiceFactory2>
fn to a service factory.apply_fn
service.ServiceFactory
for function that can produce servicesServiceFactory
for function that accepts config argument and can produce servicesServiceFactory
for function that can act as a Service
U
to a service S
Redirecting to macro.always_ready.html...
+ + + \ No newline at end of file diff --git a/actix_service/macro.always_ready.html b/actix_service/macro.always_ready.html new file mode 100644 index 0000000000..4c898a6b65 --- /dev/null +++ b/actix_service/macro.always_ready.html @@ -0,0 +1,24 @@ +macro_rules! always_ready {
+ () => { ... };
+}
An implementation of poll_ready
that always signals readiness.
This should only be used for basic leaf services that have no concept of un-readiness.
+For wrapper or other service types, use forward_ready!
for simple cases or write a bespoke
+poll_ready
implementation.
use actix_service::Service;
+use futures_util::future::{ready, Ready};
+
+struct IdentityService;
+
+impl Service<u32> for IdentityService {
+ type Response = u32;
+ type Error = ();
+ type Future = Ready<Result<Self::Response, Self::Error>>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, req: u32) -> Self::Future {
+ ready(Ok(req))
+ }
+}
Redirecting to macro.forward_ready.html...
+ + + \ No newline at end of file diff --git a/actix_service/macro.forward_ready.html b/actix_service/macro.forward_ready.html new file mode 100644 index 0000000000..8ea9acd512 --- /dev/null +++ b/actix_service/macro.forward_ready.html @@ -0,0 +1,28 @@ +macro_rules! forward_ready { + ($field:ident) => { ... }; +}
An implementation of poll_ready
that forwards readiness checks to a
+named struct field.
Tuple structs are not supported.
+use actix_service::Service;
+use futures_util::future::{ready, Ready};
+
+struct WrapperService<S> {
+ inner: S,
+}
+
+impl<S> Service<()> for WrapperService<S>
+where
+ S: Service<()>,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ actix_service::forward_ready!(inner);
+
+ fn call(&self, req: ()) -> Self::Future {
+ self.inner.call(req)
+ }
+}
Redirecting to ../../actix_service/fn.map_config.html...
+ + + \ No newline at end of file diff --git a/actix_service/map_config/fn.unit_config.html b/actix_service/map_config/fn.unit_config.html new file mode 100644 index 0000000000..5e35d658e3 --- /dev/null +++ b/actix_service/map_config/fn.unit_config.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/fn.unit_config.html...
+ + + \ No newline at end of file diff --git a/actix_service/sidebar-items.js b/actix_service/sidebar-items.js new file mode 100644 index 0000000000..668241bb4e --- /dev/null +++ b/actix_service/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["apply","apply_cfg","apply_cfg_factory","apply_fn","apply_fn_factory","fn_factory","fn_factory_with_config","fn_service","into_service","map_config","unit_config"],"macro":["always_ready","forward_ready"],"mod":["boxed"],"struct":["ApplyTransform"],"trait":["IntoService","IntoServiceFactory","Service","ServiceExt","ServiceFactory","ServiceFactoryExt","Transform","TransformExt"]}; \ No newline at end of file diff --git a/actix_service/struct.ApplyTransform.html b/actix_service/struct.ApplyTransform.html new file mode 100644 index 0000000000..de72778d24 --- /dev/null +++ b/actix_service/struct.ApplyTransform.html @@ -0,0 +1,35 @@ +pub struct ApplyTransform<T, S, Req>(/* private fields */);
Service
created by this factory.Self
to a ServiceFactory
pub trait IntoService<S, Req>where
+ S: Service<Req>,{
+ // Required method
+ fn into_service(self) -> S;
+}
Trait for types that can be converted to a Service
Convert to a Service
pub trait IntoServiceFactory<SF, Req>where
+ SF: ServiceFactory<Req>,{
+ // Required method
+ fn into_factory(self) -> SF;
+}
Trait for types that can be converted to a ServiceFactory
Convert Self
to a ServiceFactory
pub trait Service<Req> {
+ type Response;
+ type Error;
+ type Future: Future<Output = Result<Self::Response, Self::Error>>;
+
+ // Required methods
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
+ fn call(&self, req: Req) -> Self::Future;
+}
An asynchronous operation from Request
to a Response
.
The Service
trait models a request/response interaction, receiving requests and returning
+replies. You can think about a service as a function with one argument that returns some result
+asynchronously. Conceptually, the operation looks like this:
async fn(Request) -> Result<Response, Err>
The Service
trait just generalizes this form. Requests are defined as a generic type parameter
+and responses and other details are defined as associated types on the trait impl. Notice that
+this design means that services can receive many request types and converge them to a single
+response type.
Services can also have mutable state that influence computation by using a Cell
, RefCell
+or Mutex
. Services intentionally do not take &mut self
to reduce overhead in the
+common cases.
Service
provides a symmetric and uniform API; the same abstractions can be used to represent
+both clients and servers. Services describe only transformation operations which encourage
+simple API surfaces. This leads to simpler design of each service, improves test-ability and
+makes composition easier.
struct MyService;
+
+impl Service<u8> for MyService {
+ type Response = u64;
+ type Error = MyError;
+ type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { ... }
+
+ fn call(&self, req: u8) -> Self::Future { ... }
+}
Sometimes it is not necessary to implement the Service trait. For example, the above service
+could be rewritten as a simple function and passed to fn_service
.
async fn my_service(req: u8) -> Result<u64, MyError>;
+
+let svc = fn_service(my_service)
+svc.call(123)
Returns Ready
when the service is able to process requests.
If the service is at capacity, then Pending
is returned and the task is notified when the
+service becomes ready again. This function is expected to be called while on a task.
This is a best effort implementation. False positives are permitted. It is permitted for
+the service to return Ready
from a poll_ready
call and the next invocation of call
+results in an error.
poll_ready
might be called on a different task to call
..poll_ready()
is called for all services at once.Process the request and return the response asynchronously.
+This function is expected to be callable off-task. As such, implementations of call
should
+take care to not call poll_ready
. If the service is at capacity and the request is unable
+to be handled, the returned Future
should resolve to an error.
Invoking call
without first invoking poll_ready
is permitted. Implementations must be
+resilient to this fact.
This impl is deprecated since v2 because the Service
trait now receives shared reference.
pub trait ServiceExt<Req>: Service<Req> {
+ // Provided methods
+ fn map<F, R>(self, f: F) -> Map<Self, F, Req, R>
+ where Self: Sized,
+ F: FnMut(Self::Response) -> R { ... }
+ fn map_err<F, E>(self, f: F) -> MapErr<Self, Req, F, E>
+ where Self: Sized,
+ F: Fn(Self::Error) -> E { ... }
+ fn and_then<I, S1>(self, service: I) -> AndThenService<Self, S1, Req>
+ where Self: Sized,
+ I: IntoService<S1, Self::Response>,
+ S1: Service<Self::Response, Error = Self::Error> { ... }
+}
An extension trait for Service
s that provides a variety of convenient adapters.
Map this service’s output to a different type, returning a new service +of the resulting type.
+This function is similar to the Option::map
or Iterator::map
where
+it will change the type of the underlying service.
Note that this function consumes the receiving service and returns a
+wrapped version of it, similar to the existing map
methods in the
+standard library.
Map this service’s error to a different error, returning a new service.
+This function is similar to the Result::map_err
where it will change
+the error type of the underlying service. For example, this can be useful to
+ensure that services have the same error type.
Note that this function consumes the receiving service and returns a +wrapped version of it.
+Call another service after call to this one has resolved successfully.
+This function can be used to chain two services together and ensure that the second service +isn’t called until call to the fist service have finished. Result of the call to the first +service is used as an input parameter for the second service’s call.
+Note that this function consumes the receiving service and returns a wrapped version of it.
+pub trait ServiceFactory<Req> {
+ type Response;
+ type Error;
+ type Config;
+ type Service: Service<Req, Response = Self::Response, Error = Self::Error>;
+ type InitError;
+ type Future: Future<Output = Result<Self::Service, Self::InitError>>;
+
+ // Required method
+ fn new_service(&self, cfg: Self::Config) -> Self::Future;
+}
Factory for creating Service
s.
This is useful for cases where new Service
s must be produced. One case is a TCP
+server listener: a listener accepts new connections, constructs a new Service
for each using
+the ServiceFactory
trait, and uses the new Service
to process inbound requests on that new
+connection.
Config
is a service factory configuration type.
Simple factories may be able to use fn_factory
or fn_factory_with_config
to
+reduce boilerplate.
Create and return a new service asynchronously.
+pub trait ServiceFactoryExt<Req>: ServiceFactory<Req> {
+ // Provided methods
+ fn map<F, R>(self, f: F) -> MapServiceFactory<Self, F, Req, R>
+ where Self: Sized,
+ F: FnMut(Self::Response) -> R + Clone { ... }
+ fn map_err<F, E>(self, f: F) -> MapErrServiceFactory<Self, Req, F, E>
+ where Self: Sized,
+ F: Fn(Self::Error) -> E + Clone { ... }
+ fn map_init_err<F, E>(self, f: F) -> MapInitErr<Self, F, Req, E>
+ where Self: Sized,
+ F: Fn(Self::InitError) -> E + Clone { ... }
+ fn and_then<I, SF1>(
+ self,
+ factory: I
+ ) -> AndThenServiceFactory<Self, SF1, Req>
+ where Self: Sized,
+ Self::Config: Clone,
+ I: IntoServiceFactory<SF1, Self::Response>,
+ SF1: ServiceFactory<Self::Response, Config = Self::Config, Error = Self::Error, InitError = Self::InitError> { ... }
+}
An extension trait for ServiceFactory
s that provides a variety of convenient adapters.
Map this service’s output to a different type, returning a new service +of the resulting type.
+Map this service’s error to a different error, returning a new service.
+Map this factory’s init error to a different error, returning a new service.
+Call another service after call to this one has resolved successfully.
+pub trait Transform<S, Req> {
+ type Response;
+ type Error;
+ type Transform: Service<Req, Response = Self::Response, Error = Self::Error>;
+ type InitError;
+ type Future: Future<Output = Result<Self::Transform, Self::InitError>>;
+
+ // Required method
+ fn new_transform(&self, service: S) -> Self::Future;
+}
Defines the interface of a service factory that wraps inner service during construction.
+Transformers wrap an inner service and runs during inbound and/or outbound processing in the +service lifecycle. It may modify request and/or response.
+For example, a timeout service wrapper:
+ +pub struct Timeout<S> {
+ service: S,
+ timeout: Duration,
+}
+
+impl<S: Service<Req>, Req> Service<Req> for Timeout<S> {
+ type Response = S::Response;
+ type Error = TimeoutError<S::Error>;
+ type Future = TimeoutServiceResponse<S>;
+
+ actix_service::forward_ready!(service);
+
+ fn call(&self, req: Req) -> Self::Future {
+ TimeoutServiceResponse {
+ fut: self.service.call(req),
+ sleep: Sleep::new(clock::now() + self.timeout),
+ }
+ }
+}
This wrapper service is decoupled from the underlying service implementation and could be +applied to any service.
+The Transform
trait defines the interface of a service wrapper. Transform
is often
+implemented for middleware, defining how to construct a middleware Service. A Service that is
+constructed by the factory takes the Service that follows it during execution as a parameter,
+assuming ownership of the next Service.
A transform for the Timeout
middleware could look like this:
pub struct TimeoutTransform {
+ timeout: Duration,
+}
+
+impl<S: Service<Req>, Req> Transform<S, Req> for TimeoutTransform {
+ type Response = S::Response;
+ type Error = TimeoutError<S::Error>;
+ type InitError = S::Error;
+ type Transform = Timeout<S>;
+ type Future = Ready<Result<Self::Transform, Self::InitError>>;
+
+ fn new_transform(&self, service: S) -> Self::Future {
+ ready(Ok(Timeout {
+ service,
+ timeout: self.timeout,
+ }))
+ }
+}
Creates and returns a new Transform component, asynchronously
+pub trait TransformExt<S, Req>: Transform<S, Req> {
+ // Provided method
+ fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, Req, F, E>
+ where Self: Sized,
+ F: Fn(Self::InitError) -> E + Clone { ... }
+}
An extension trait for Transform
s that provides a variety of convenient adapters.
Redirecting to ../../actix_service/fn.apply.html...
+ + + \ No newline at end of file diff --git a/actix_service/transform/struct.ApplyTransform.html b/actix_service/transform/struct.ApplyTransform.html new file mode 100644 index 0000000000..0b77d7d51e --- /dev/null +++ b/actix_service/transform/struct.ApplyTransform.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/struct.ApplyTransform.html...
+ + + \ No newline at end of file diff --git a/actix_service/transform/trait.Transform.html b/actix_service/transform/trait.Transform.html new file mode 100644 index 0000000000..e5457e698b --- /dev/null +++ b/actix_service/transform/trait.Transform.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../actix_service/trait.Transform.html...
+ + + \ No newline at end of file diff --git a/actix_tls/accept/enum.TlsError.html b/actix_tls/accept/enum.TlsError.html new file mode 100644 index 0000000000..dcedd98dec --- /dev/null +++ b/actix_tls/accept/enum.TlsError.html @@ -0,0 +1,45 @@ +pub enum TlsError<TlsErr, SvcErr> {
+ Timeout,
+ Tls(TlsErr),
+ Service(SvcErr),
+}
TLS handshake error, TLS timeout, or inner service error.
+All TLS acceptors from this crate will return the SvcErr
type parameter as Infallible
,
+which can be cast to your own service type, inferred or otherwise, using into_service_error
.
TLS handshake has timed-out.
+Wraps TLS service errors.
+Wraps service errors.
+Casts the infallible service error type returned from acceptors into caller’s type.
+let a: TlsError<u32, Infallible> = TlsError::Tls(42);
+let _b: TlsError<u32, u64> = a.into_service_error();
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn max_concurrent_tls_connect(num: usize)
Sets the maximum per-worker concurrent TLS connection limit.
+All listeners will stop accepting connections when this limit is reached. +It can be used to regulate the global TLS CPU usage.
+By default, the connection limit is 256.
+TLS connection acceptor services.
+native-tls
based TLS connection acceptor service.openssl
based TLS acceptor service.rustls
v0.20 based TLS connection acceptor service.rustls
v0.21 based TLS connection acceptor service.rustls
v0.22 based TLS connection acceptor service.rustls
v0.23 based TLS connection acceptor service.native-tls
based TLS connection acceptor service.
See Acceptor
for main service factory docs.
native-tls
that are useful for acceptors.native-tls
crate.native-tls
based async TLS stream in order to implement [ActixStream
].pub struct Error(/* private fields */);
An error returned from the TLS implementation.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsAcceptor(/* private fields */);
A wrapper around a native_tls::TlsAcceptor
, providing an async accept
+method.
Accepts a new client connection with the provided stream.
+This function will internally call TlsAcceptor::accept
to connect
+the stream and returns a future representing the resolution of the
+connection operation. The returned future will resolve to either
+TlsStream<S>
or Error
depending if it’s successful or not.
This is typically used after a new socket has been accepted from a
+TcpListener
. That socket is then passed to this function to perform
+the server half of accepting a client connection.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Acceptor { /* private fields */ }
Accept TLS connections via the native-tls
crate.
Constructs native-tls
based acceptor service factory.
Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+Default timeout is 3 seconds.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct AcceptorService { /* private fields */ }
Native-TLS based acceptor service.
+Service
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsStream<IO>(/* private fields */);
Wraps a native-tls
based async TLS stream in order to implement [ActixStream
].
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreopenssl
based TLS acceptor service.
See Acceptor
for main service factory docs.
openssl
that are useful for acceptors.openssl
crate.openssl
based async TLS stream in order to implement [ActixStream
].pub enum HandshakeError<S> {
+ SetupFailure(ErrorStack),
+ Failure(MidHandshakeSslStream<S>),
+ WouldBlock(MidHandshakeSslStream<S>),
+}
An error or intermediate state after a TLS handshake attempt.
+Setup failed.
+The handshake failed.
+The handshake encountered a WouldBlock
error midway through.
This error will never be returned for blocking streams.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRe-exports from openssl
that are useful for acceptors.
SslAcceptor
s.pub struct AlpnError(/* private fields */);
An error returned from an ALPN selection callback.
+Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Error { /* private fields */ }
An SSL error.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Ssl(/* private fields */);
The state of an SSL/TLS session.
+Ssl
objects are created from an SslContext
, which provides configuration defaults.
+These defaults can be overridden on a per-Ssl
basis, however.
Returns a new extra data index.
+Each invocation of this function is guaranteed to return a distinct index. These can be used +to store data in the context that can be retrieved later by callbacks, for example.
+This corresponds to SSL_get_ex_new_index
.
Initiates a client-side TLS handshake.
+This corresponds to SSL_connect
.
OpenSSL’s default configuration is insecure. It is highly recommended to use
+SslConnector
rather than Ssl
directly, as it manages that configuration.
This corresponds to SSL_connect
.
Initiates a server-side TLS handshake.
+This corresponds to SSL_accept
.
OpenSSL’s default configuration is insecure. It is highly recommended to use
+SslAcceptor
rather than Ssl
directly, as it manages that configuration.
This corresponds to SSL_accept
.
Configure as an outgoing stream from a client.
+This corresponds to SSL_set_connect_state
.
Configure as an incoming stream to a server.
+This corresponds to SSL_set_accept_state
.
Like SslContextBuilder::set_verify
.
This corresponds to SSL_set_verify
.
Returns the verify mode that was set using set_verify
.
This corresponds to SSL_set_verify_mode
.
Like SslContextBuilder::set_verify_callback
.
This corresponds to SSL_set_verify
.
Like SslContextBuilder::set_tmp_dh
.
This corresponds to SSL_set_tmp_dh
.
Like SslContextBuilder::set_tmp_dh_callback
.
This corresponds to SSL_set_tmp_dh_callback
.
Like SslContextBuilder::set_tmp_ecdh
.
This corresponds to SSL_set_tmp_ecdh
.
Like SslContextBuilder::set_alpn_protos
.
Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_set_alpn_protos
.
Returns the current cipher if the session is active.
+This corresponds to SSL_get_current_cipher
.
Returns a short string describing the state of the session.
+This corresponds to SSL_state_string
.
Returns a longer string describing the state of the session.
+This corresponds to SSL_state_string_long
.
Sets the host name to be sent to the server for Server Name Indication (SNI).
+It has no effect for a server-side connection.
+This corresponds to SSL_set_tlsext_host_name
.
Returns the peer’s certificate, if present.
+This corresponds to SSL_get_peer_certificate
.
Returns the certificate chain of the peer, if present.
+On the client side, the chain includes the leaf certificate, but on the server side it does +not. Fun!
+This corresponds to SSL_get_peer_cert_chain
.
Returns the verified certificate chain of the peer, including the leaf certificate.
+If verification was not successful (i.e. verify_result
does not return
+X509VerifyResult::OK
), this chain may be incomplete or invalid.
Requires OpenSSL 1.1.0 or newer.
+This corresponds to SSL_get0_verified_chain
.
Like [SslContext::certificate
].
This corresponds to SSL_get_certificate
.
Like SslContext::private_key
.
This corresponds to SSL_get_privatekey
.
version_str
Returns the protocol version of the session.
+This corresponds to SSL_version
.
Returns a string describing the protocol version of the session.
+This corresponds to SSL_get_version
.
Returns the protocol selected via Application Layer Protocol Negotiation (ALPN).
+The protocol’s name is returned is an opaque sequence of bytes. It is up to the client +to interpret it.
+Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_get0_alpn_selected
.
Enables the DTLS extension “use_srtp” as defined in RFC5764.
+This corresponds to SSL_set_tlsext_use_srtp
.
This corresponds to SSL_set_tlsext_use_srtp
.
Gets all SRTP profiles that are enabled for handshake via set_tlsext_use_srtp
+DTLS extension “use_srtp” as defined in RFC5764 has to be enabled.
+This corresponds to SSL_get_srtp_profiles
.
This corresponds to SSL_get_srtp_profiles
.
Gets the SRTP profile selected by handshake.
+DTLS extension “use_srtp” as defined in RFC5764 has to be enabled.
+This corresponds to SSL_get_selected_srtp_profile
.
Returns the number of bytes remaining in the currently processed TLS record.
+If this is greater than 0, the next call to read
will not call down to the underlying
+stream.
This corresponds to SSL_pending
.
Returns the servername sent by the client via Server Name Indication (SNI).
+It is only useful on the server side.
+While the SNI specification requires that servernames be valid domain names (and therefore
+ASCII), OpenSSL does not enforce this restriction. If the servername provided by the client
+is not valid UTF-8, this function will return None
. The servername_raw
method returns
+the raw bytes and does not have this restriction.
This corresponds to SSL_get_servername
.
Returns the servername sent by the client via Server Name Indication (SNI).
+It is only useful on the server side.
+Unlike servername
, this method does not require the name be valid UTF-8.
This corresponds to SSL_get_servername
.
Changes the context corresponding to the current connection.
+It is most commonly used in the Server Name Indication (SNI) callback.
+This corresponds to SSL_set_SSL_CTX
.
Returns the context corresponding to the current connection.
+This corresponds to SSL_get_SSL_CTX
.
Returns a mutable reference to the X509 verification configuration.
+Requires BoringSSL or OpenSSL 1.0.2 or newer.
+This corresponds to SSL_get0_param
.
Returns the certificate verification result.
+This corresponds to SSL_get_verify_result
.
Returns a shared reference to the SSL session.
+This corresponds to SSL_get_session
.
Copies the client_random
value sent by the client in the TLS handshake into a buffer.
Returns the number of bytes copied, or if the buffer is empty, the size of the client_random
+value.
Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer.
+This corresponds to SSL_get_client_random
.
Copies the server_random
value sent by the server in the TLS handshake into a buffer.
Returns the number of bytes copied, or if the buffer is empty, the size of the server_random
+value.
Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer.
+This corresponds to SSL_get_server_random
.
Derives keying material for application use in accordance to RFC 5705.
+This corresponds to SSL_export_keying_material
.
Derives keying material for application use in accordance to RFC 5705.
+This function is only usable with TLSv1.3, wherein there is no distinction between an empty context and no
+context. Therefore, unlike export_keying_material
, context
must always be supplied.
Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_export_keying_material_early
.
Sets the session to be used.
+This should be called before the handshake to attempt to reuse a previously established +session. If the server is not willing to reuse the session, a new one will be transparently +negotiated.
+The caller of this method is responsible for ensuring that the session is associated
+with the same SslContext
as this Ssl
.
This corresponds to SSL_set_session
.
Determines if the session provided to set_session
was successfully reused.
This corresponds to SSL_session_reused
.
Sets the status response a client wishes the server to reply with.
+This corresponds to SSL_set_tlsext_status_type
.
Determines if current session used Extended Master Secret
+Returns None
if the handshake is still in-progress.
This corresponds to SSL_get_extms_support
.
Returns the server’s OCSP response, if present.
+This corresponds to SSL_get_tlsext_status_ocsp_resp
.
Sets the OCSP response to be returned to the client.
+This corresponds to SSL_set_tlsext_status_oscp_resp
.
Determines if this Ssl
is configured for server-side or client-side use.
This corresponds to SSL_is_server
.
Sets the extra data at the specified index.
+This can be used to provide data to callbacks registered with the context. Use the
+Ssl::new_ex_index
method to create an Index
.
This corresponds to SSL_set_ex_data
.
Returns a reference to the extra data at the specified index.
+This corresponds to SSL_get_ex_data
.
Returns a mutable reference to the extra data at the specified index.
+This corresponds to SSL_get_ex_data
.
Sets the maximum amount of early data that will be accepted on this connection.
+Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+This corresponds to SSL_set_max_early_data
.
Gets the maximum amount of early data that can be sent on this connection.
+Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+This corresponds to SSL_get_max_early_data
.
Copies the contents of the last Finished message sent to the peer into the provided buffer.
+The total size of the message is returned, so this can be used to determine the size of the +buffer required.
+This corresponds to SSL_get_finished
.
Copies the contents of the last Finished message received from the peer into the provided +buffer.
+The total size of the message is returned, so this can be used to determine the size of the +buffer required.
+This corresponds to SSL_get_peer_finished
.
Determines if the initial handshake has been completed.
+This corresponds to SSL_is_init_finished
.
Determines if the client’s hello message is in the SSLv2 format.
+This can only be used inside of the client hello callback. Otherwise, false
is returned.
Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_client_hello_isv2
.
Returns the legacy version field of the client’s hello message.
+This can only be used inside of the client hello callback. Otherwise, None
is returned.
Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_client_hello_get0_legacy_version
.
Returns the random field of the client’s hello message.
+This can only be used inside of the client hello callback. Otherwise, None
is returned.
Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_client_hello_get0_random
.
Returns the session ID field of the client’s hello message.
+This can only be used inside of the client hello callback. Otherwise, None
is returned.
Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_client_hello_get0_session_id
.
Returns the ciphers field of the client’s hello message.
+This can only be used inside of the client hello callback. Otherwise, None
is returned.
Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_client_hello_get0_ciphers
.
Decodes a slice of wire-format cipher suite specification bytes. Unsupported cipher suites +are ignored.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_bytes_to_cipher_list
.
Returns the compression methods field of the client’s hello message.
+This can only be used inside of the client hello callback. Otherwise, None
is returned.
Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_client_hello_get0_compression_methods
.
Sets the MTU used for DTLS connections.
+This corresponds to SSL_set_mtu
.
Returns the PSK identity hint used during connection setup.
+May return None
if no PSK identity hint was used during the connection setup.
This corresponds to SSL_get_psk_identity_hint
.
Returns the PSK identity used during connection setup.
+This corresponds to SSL_get_psk_identity
.
This corresponds to SSL_add0_chain_cert
.
Sets a new default TLS/SSL method for SSL objects
+Loads the private key from a file.
+This corresponds to SSL_use_Private_Key_file
.
Sets the private key.
+This corresponds to SSL_use_PrivateKey
.
Sets the certificate
+This corresponds to SSL_use_certificate
.
Loads a certificate chain from a file.
+The file should contain a sequence of PEM-formatted certificates, the first being the leaf +certificate, and the remainder forming the chain of certificates up to and including the +trusted root certificate.
+This corresponds to SSL_use_certificate_chain_file
.
Sets ca certificate that client trusted
+This corresponds to SSL_add_client_CA
.
This corresponds to SSL_set_client_CA_list
.
Sets the minimum supported protocol version.
+A value of None
will enable protocol versions down to the lowest version supported by
+OpenSSL.
Requires BoringSSL or OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_set_min_proto_version
.
Sets the maximum supported protocol version.
+A value of None
will enable protocol versions up to the highest version supported by
+OpenSSL.
Requires BoringSSL or OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_set_max_proto_version
.
Sets the list of supported ciphers for the TLSv1.3 protocol.
+The set_cipher_list
method controls the cipher suites for protocols before TLSv1.3.
The format consists of TLSv1.3 cipher suite names separated by :
characters in order of
+preference.
Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+This corresponds to SSL_set_ciphersuites
.
Sets the list of supported ciphers for protocols before TLSv1.3.
+The set_ciphersuites
method controls the cipher suites for TLSv1.3.
See ciphers
for details on the format.
This corresponds to SSL_set_cipher_list
.
Set the certificate store used for certificate verification
+This corresponds to SSL_set_cert_store
.
Sets the number of TLS 1.3 session tickets that will be sent to a client after a full +handshake.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_set_num_tickets
.
Gets the number of TLS 1.3 session tickets that will be sent to a client after a full +handshake.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_get_num_tickets
.
Set the context’s security level to a value between 0 and 5, inclusive. +A security value of 0 allows allows all parameters and algorithms.
+Requires OpenSSL 1.1.0 or newer.
+This corresponds to SSL_set_security_level
.
Get the connection’s security level, which controls the allowed parameters +and algorithms.
+Requires OpenSSL 1.1.0 or newer.
+This corresponds to SSL_get_security_level
.
Get the temporary key provided by the peer that is used during key +exchange.
+This corresponds to SSL_get_peer_tmp_key
.
Returns the temporary key from the local end of the connection that is +used during key exchange.
+This corresponds to SSL_get_tmp_key
.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SslAcceptor(/* private fields */);
A type which wraps server-side streams in a TLS session.
+OpenSSL’s default configuration is highly insecure. This connector manages the OpenSSL +structures, configuring cipher suites, session options, and more.
+Creates a new builder configured to connect to non-legacy clients. This should generally be +considered a reasonable default choice.
+This corresponds to the intermediate configuration of version 5 of Mozilla’s server side TLS +recommendations. See its documentation for more details on specifics.
+Creates a new builder configured to connect to modern clients.
+This corresponds to the modern configuration of version 5 of Mozilla’s server side TLS recommendations. +See its documentation for more details on specifics.
+Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+Creates a new builder configured to connect to non-legacy clients. This should generally be +considered a reasonable default choice.
+This corresponds to the intermediate configuration of version 4 of Mozilla’s server side TLS +recommendations. See its documentation for more details on specifics.
+Creates a new builder configured to connect to modern clients.
+This corresponds to the modern configuration of version 4 of Mozilla’s server side TLS recommendations. +See its documentation for more details on specifics.
+Initiates a server-side TLS session on a stream.
+Consumes the SslAcceptor
, returning the inner raw SslContext
.
Returns a shared reference to the inner raw SslContext
.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SslAcceptorBuilder(/* private fields */);
A builder for SslAcceptor
s.
Consumes the builder, returning a SslAcceptor
.
Configures the certificate verification method for new connections.
+This corresponds to SSL_CTX_set_verify
.
Configures the certificate verification method for new connections and +registers a verification callback.
+The callback is passed a boolean indicating if OpenSSL’s internal verification succeeded as
+well as a reference to the X509StoreContext
which can be used to examine the certificate
+chain. It should return a boolean indicating if verification succeeded.
This corresponds to SSL_CTX_set_verify
.
Configures the server name indication (SNI) callback for new connections.
+SNI is used to allow a single server to handle requests for multiple domains, each of which +has its own certificate chain and configuration.
+Obtain the server name with the servername
method and then set the corresponding context
+with set_ssl_context
This corresponds to SSL_CTX_set_tlsext_servername_callback
.
Sets the certificate verification depth.
+If the peer’s certificate chain is longer than this value, verification will fail.
+This corresponds to SSL_CTX_set_verify_depth
.
Sets a custom certificate store for verifying peer certificates.
+Requires OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_set0_verify_cert_store
.
Replaces the context’s certificate store.
+This corresponds to SSL_CTX_set_cert_store
.
Controls read ahead behavior.
+If enabled, OpenSSL will read as much data as is available from the underlying stream, +instead of a single record at a time.
+It has no effect when used with DTLS.
+This corresponds to SSL_CTX_set_read_ahead
.
Sets the mode used by the context, returning the previous mode.
+This corresponds to SSL_CTX_set_mode
.
Sets the parameters to be used during ephemeral Diffie-Hellman key exchange.
+This corresponds to SSL_CTX_set_tmp_dh
.
Sets the callback which will generate parameters to be used during ephemeral Diffie-Hellman +key exchange.
+The callback is provided with a reference to the Ssl
for the session, as well as a boolean
+indicating if the selected cipher is export-grade, and the key length. The export and key
+length options are archaic and should be ignored in almost all cases.
This corresponds to SSL_CTX_set_tmp_dh_callback
.
Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange.
+This corresponds to SSL_CTX_set_tmp_ecdh
.
Use the default locations of trusted certificates for verification.
+These locations are read from the SSL_CERT_FILE
and SSL_CERT_DIR
environment variables
+if present, or defaults specified at OpenSSL build time otherwise.
This corresponds to SSL_CTX_set_default_verify_paths
.
Loads trusted root certificates from a file.
+The file should contain a sequence of PEM-formatted CA certificates.
+This corresponds to SSL_CTX_load_verify_locations
.
Sets the list of CA names sent to the client.
+The CA certificates must still be added to the trust root - they are not automatically set +as trusted by this method.
+This corresponds to SSL_CTX_set_client_CA_list
.
Add the provided CA certificate to the list sent by the server to the client when +requesting client-side TLS authentication.
+This corresponds to SSL_CTX_add_client_CA
.
Set the context identifier for sessions.
+This value identifies the server’s session cache to clients, telling them when they’re +able to reuse sessions. It should be set to a unique value per server, unless multiple +servers share a session cache.
+This value should be set when using client certificates, or each request will fail its +handshake and need to be restarted.
+This corresponds to SSL_CTX_set_session_id_context
.
Loads a leaf certificate from a file.
+Only a single certificate will be loaded - use add_extra_chain_cert
to add the remainder
+of the certificate chain, or set_certificate_chain_file
to load the entire chain from a
+single file.
This corresponds to SSL_CTX_use_certificate_file
.
Loads a certificate chain from a file.
+The file should contain a sequence of PEM-formatted certificates, the first being the leaf +certificate, and the remainder forming the chain of certificates up to and including the +trusted root certificate.
+This corresponds to SSL_CTX_use_certificate_chain_file
.
Sets the leaf certificate.
+Use add_extra_chain_cert
to add the remainder of the certificate chain.
This corresponds to SSL_CTX_use_certificate
.
Appends a certificate to the certificate chain.
+This chain should contain all certificates necessary to go from the certificate specified by
+set_certificate
to a trusted root.
This corresponds to SSL_CTX_add_extra_chain_cert
.
Loads the private key from a file.
+This corresponds to SSL_CTX_use_PrivateKey_file
.
Sets the private key.
+This corresponds to SSL_CTX_use_PrivateKey
.
Sets the list of supported ciphers for protocols before TLSv1.3.
+The set_ciphersuites
method controls the cipher suites for TLSv1.3.
See ciphers
for details on the format.
This corresponds to SSL_CTX_set_cipher_list
.
Sets the list of supported ciphers for the TLSv1.3 protocol.
+The set_cipher_list
method controls the cipher suites for protocols before TLSv1.3.
The format consists of TLSv1.3 cipher suite names separated by :
characters in order of
+preference.
Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+This corresponds to SSL_CTX_set_ciphersuites
.
Sets the options used by the context, returning the old set.
+This enables the specified options, but does not disable unspecified options. Use
+clear_options
for that.
This corresponds to SSL_CTX_set_options
.
Returns the options used by the context.
+This corresponds to SSL_CTX_get_options
.
Clears the options used by the context, returning the old set.
+This corresponds to SSL_CTX_clear_options
.
Sets the minimum supported protocol version.
+A value of None
will enable protocol versions down to the lowest version supported by
+OpenSSL.
Requires BoringSSL or OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_min_proto_version
.
Sets the maximum supported protocol version.
+A value of None
will enable protocol versions up to the highest version supported by
+OpenSSL.
Requires BoringSSL or OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_max_proto_version
.
Gets the minimum supported protocol version.
+A value of None
indicates that all versions down to the lowest version supported by
+OpenSSL are enabled.
Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
+This corresponds to SSL_CTX_get_min_proto_version
.
Gets the maximum supported protocol version.
+A value of None
indicates that all versions up to the highest version supported by
+OpenSSL are enabled.
Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
+This corresponds to SSL_CTX_get_max_proto_version
.
Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
+The input must be in ALPN “wire format”. It consists of a sequence of supported protocol
+names prefixed by their byte length. For example, the protocol list consisting of spdy/1
+and http/1.1
is encoded as b"\x06spdy/1\x08http/1.1"
. The protocols are ordered by
+preference.
Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_alpn_protos
.
Enables the DTLS extension “use_srtp” as defined in RFC5764.
+This corresponds to SSL_CTX_set_tlsext_use_srtp
.
Sets the callback used by a server to select a protocol for Application Layer Protocol +Negotiation (ALPN).
+The callback is provided with the client’s protocol list in ALPN wire format. See the
+documentation for SslContextBuilder::set_alpn_protos
for details. It should return one
+of those protocols on success. The select_next_proto
function implements the standard
+protocol selection algorithm.
Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_alpn_select_cb
.
Checks for consistency between the private key and certificate.
+This corresponds to SSL_CTX_check_private_key
.
Returns a shared reference to the context’s certificate store.
+This corresponds to SSL_CTX_get_cert_store
.
Returns a mutable reference to the context’s certificate store.
+This corresponds to SSL_CTX_get_cert_store
.
Returns a reference to the X509 verification configuration.
+Requires BoringSSL or OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_get0_param
.
Returns a mutable reference to the X509 verification configuration.
+Requires BoringSSL or OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_get0_param
.
Sets the callback dealing with OCSP stapling.
+On the client side, this callback is responsible for validating the OCSP status response
+returned by the server. The status may be retrieved with the SslRef::ocsp_status
method.
+A response of Ok(true)
indicates that the OCSP status is valid, and a response of
+Ok(false)
indicates that the OCSP status is invalid and the handshake should be
+terminated.
On the server side, this callback is responsible for setting the OCSP status response to be
+returned to clients. The status may be set with the SslRef::set_ocsp_status
method. A
+response of Ok(true)
indicates that the OCSP status should be returned to the client, and
+Ok(false)
indicates that the status should not be returned to the client.
This corresponds to SSL_CTX_set_tlsext_status_cb
.
Sets the callback for providing an identity and pre-shared key for a TLS-PSK client.
+The callback will be called with the SSL context, an identity hint if one was provided +by the server, a mutable slice for each of the identity and pre-shared key bytes. The +identity must be written as a null-terminated C string.
+This corresponds to SSL_CTX_set_psk_client_callback
.
set_psk_client_callback
Sets the callback for providing an identity and pre-shared key for a TLS-PSK server.
+The callback will be called with the SSL context, an identity provided by the client, +and, a mutable slice for the pre-shared key bytes. The callback returns the number of +bytes in the pre-shared key.
+This corresponds to SSL_CTX_set_psk_server_callback
.
Sets the callback which is called when new sessions are negotiated.
+This can be used by clients to implement session caching. While in TLSv1.2 the session is
+available to access via SslRef::session
immediately after the handshake completes, this
+is not the case for TLSv1.3. There, a session is not generally available immediately, and
+the server may provide multiple session tokens to the client over a single session. The new
+session callback is a portable way to deal with both cases.
Note that session caching must be enabled for the callback to be invoked, and it defaults
+off for clients. set_session_cache_mode
controls that behavior.
This corresponds to SSL_CTX_sess_set_new_cb
.
Sets the callback which is called when sessions are removed from the context.
+Sessions can be removed because they have timed out or because they are considered faulty.
+This corresponds to SSL_CTX_sess_set_remove_cb
.
Sets the callback which is called when a client proposed to resume a session but it was not +found in the internal cache.
+The callback is passed a reference to the session ID provided by the client. It should +return the session corresponding to that ID if available. This is only used for servers, not +clients.
+The returned SslSession
must not be associated with a different SslContext
.
This corresponds to SSL_CTX_sess_set_get_cb
.
Sets the TLS key logging callback.
+The callback is invoked whenever TLS key material is generated, and is passed a line of NSS +SSLKEYLOGFILE-formatted text. This can be used by tools like Wireshark to decrypt message +traffic. The line does not contain a trailing newline.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_set_keylog_callback
.
Sets the session caching mode use for connections made with the context.
+Returns the previous session caching mode.
+This corresponds to SSL_CTX_set_session_cache_mode
.
Sets the callback for generating an application cookie for TLS1.3 +stateless handshakes.
+The callback will be called with the SSL context and a slice into which the cookie +should be written. The callback should return the number of bytes written.
+This corresponds to SSL_CTX_set_stateless_cookie_generate_cb
.
Sets the callback for verifying an application cookie for TLS1.3 +stateless handshakes.
+The callback will be called with the SSL context and the cookie supplied by the +client. It should return true if and only if the cookie is valid.
+Note that the OpenSSL implementation independently verifies the integrity of +application cookies using an HMAC before invoking the supplied callback.
+This corresponds to SSL_CTX_set_stateless_cookie_verify_cb
.
Sets the callback for generating a DTLSv1 cookie
+The callback will be called with the SSL context and a slice into which the cookie +should be written. The callback should return the number of bytes written.
+This corresponds to SSL_CTX_set_cookie_generate_cb
.
Sets the callback for verifying a DTLSv1 cookie
+The callback will be called with the SSL context and the cookie supplied by the +client. It should return true if and only if the cookie is valid.
+This corresponds to SSL_CTX_set_cookie_verify_cb
.
Sets the extra data at the specified index.
+This can be used to provide data to callbacks registered with the context. Use the
+SslContext::new_ex_index
method to create an Index
.
This corresponds to SSL_CTX_set_ex_data
.
Adds a custom extension for a TLS/DTLS client or server for all supported protocol versions.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_add_custom_ext
.
Sets the maximum amount of early data that will be accepted on incoming connections.
+Defaults to 0.
+Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+This corresponds to SSL_CTX_set_max_early_data
.
Sets a callback which will be invoked just after the client’s hello message is received.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_set_client_hello_cb
.
Sets the context’s session cache size limit, returning the previous limit.
+A value of 0 means that the cache size is unbounded.
+This corresponds to SSL_CTX_sess_set_cache_size
.
Sets the context’s supported signature algorithms.
+Requires OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_set1_sigalgs_list
.
Sets the context’s supported elliptic curve groups.
+Requires BoringSSL or OpenSSL 1.1.1 or LibreSSL 2.5.1 or newer.
+This corresponds to SSL_CTX_set1_groups_list
.
Sets the number of TLS 1.3 session tickets that will be sent to a client after a full +handshake.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_set_num_tickets
.
Set the context’s security level to a value between 0 and 5, inclusive. +A security value of 0 allows allows all parameters and algorithms.
+Requires OpenSSL 1.1.0 or newer.
+This corresponds to SSL_CTX_set_security_level
.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Acceptor { /* private fields */ }
Accept TLS connections via the openssl
crate.
Create openssl
based acceptor service factory.
Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+Default timeout is 3 seconds.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct AcceptorService { /* private fields */ }
OpenSSL based acceptor service.
+Service
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsStream<IO>(/* private fields */);
Wraps an openssl
based async TLS stream in order to implement [ActixStream
].
Like SslStream::connect
.
A convenience method wrapping poll_connect
.
Like SslStream::accept
.
A convenience method wrapping poll_accept
.
Like SslStream::do_handshake
.
A convenience method wrapping poll_do_handshake
.
A convenience method wrapping poll_read_early_data
.
A convenience method wrapping poll_write_early_data
.
Returns a shared reference to the Ssl
object associated with this stream.
Returns a pinned mutable reference to the underlying stream.
+buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morerustls
v0.20 based TLS connection acceptor service.
See Acceptor
for main service factory docs.
rustls
that are useful for acceptors.rustls
crate.rustls
based async TLS stream in order to implement [ActixStream
].pub struct ServerConfig {
+ pub ignore_client_order: bool,
+ pub max_fragment_size: Option<usize>,
+ pub session_storage: Arc<dyn StoresServerSessions + Send + Sync>,
+ pub ticketer: Arc<dyn ProducesTickets>,
+ pub cert_resolver: Arc<dyn ResolvesServerCert>,
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub key_log: Arc<dyn KeyLog>,
+ pub max_early_data_size: u32,
+ pub send_half_rtt_data: bool,
+ /* private fields */
+}
Common configuration for a set of server sessions.
+Making one of these can be expensive, and should be +once per process rather than once per connection.
+These must be created via the ServerConfig::builder()
function.
ServerConfig::max_fragment_size
: the default is None
: TLS packets are not fragmented to a specific size.ServerConfig::session_storage
: the default stores 256 sessions in memory.ServerConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ServerConfig::key_log
: key material is not logged.ignore_client_order: bool
Ignore the client’s ciphersuite order. Instead, +choose the top ciphersuite in the server list +which is supported by the client.
+max_fragment_size: Option<usize>
The maximum size of TLS message we’ll emit. If None, we don’t limit TLS +message lengths except to the 2**16 limit specified in the standard.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ServerConnection::new.
+Setting this value to the TCP MSS may improve latency for stream-y workloads.
+session_storage: Arc<dyn StoresServerSessions + Send + Sync>
How to store client sessions.
+ticketer: Arc<dyn ProducesTickets>
How to produce tickets.
+cert_resolver: Arc<dyn ResolvesServerCert>
How to choose a server cert and key.
+alpn_protocols: Vec<Vec<u8>>
Protocol names we support, most preferred first. +If empty we don’t do ALPN at all.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+max_early_data_size: u32
Amount of early data to accept for sessions created by +this config. Specify 0 to disable early data. The +default is 0.
+Read the early data via [ServerConnection::early_data
].
The units for this are both plaintext bytes, and ciphertext +bytes, depending on whether the server accepts a client’s early_data +or not. It is therefore recommended to include some slop in +this value to account for the unknown amount of ciphertext +expansion in the latter case.
+send_half_rtt_data: bool
Whether the server should send “0.5RTT” data. This means the server +sends data after its first flight of handshake messages, without +waiting for the client to complete the handshake.
+This can improve TTFB latency for either server-speaks-first protocols,
+or client-speaks-first protocols when paired with “0RTT” data. This
+comes at the cost of a subtle weakening of the normal handshake
+integrity guarantees that TLS provides. Note that the initial
+ClientHello
is indirectly authenticated because it is included
+in the transcript used to derive the keys used to encrypt the data.
This only applies to TLS1.3 connections. TLS1.2 connections cannot +do this optimisation and this setting is ignored for them. It is +also ignored for TLS1.3 connections that even attempt client +authentication.
+This defaults to false. This means the first application data
+sent by the server comes after receiving and validating the client’s
+handshake up to the Finished
message. This is the safest option.
Create builder to build up the server configuration.
+For more information, see the [ConfigBuilder
] documentation.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Acceptor { /* private fields */ }
Accept TLS connections via the rustls
crate.
Constructs rustls
based acceptor service factory.
Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+Default timeout is 3 seconds.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct AcceptorService { /* private fields */ }
Rustls based acceptor service.
+Service
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsStream<IO>(/* private fields */);
Wraps a rustls
based async TLS stream in order to implement [ActixStream
].
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morerustls
v0.21 based TLS connection acceptor service.
See Acceptor
for main service factory docs.
rustls
that are useful for acceptors.rustls
crate.rustls
based async TLS stream in order to implement [ActixStream
].pub struct ServerConfig {
+ pub ignore_client_order: bool,
+ pub max_fragment_size: Option<usize>,
+ pub session_storage: Arc<dyn StoresServerSessions + Send + Sync>,
+ pub ticketer: Arc<dyn ProducesTickets>,
+ pub cert_resolver: Arc<dyn ResolvesServerCert>,
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub key_log: Arc<dyn KeyLog>,
+ pub max_early_data_size: u32,
+ pub send_half_rtt_data: bool,
+ pub send_tls13_tickets: usize,
+ /* private fields */
+}
Common configuration for a set of server sessions.
+Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
+from the operating system to add to the RootCertStore
passed to a ClientCertVerifier
+builder may take on the order of a few hundred milliseconds.
These must be created via the ServerConfig::builder()
function.
ServerConfig::max_fragment_size
: the default is None
: TLS packets are not fragmented to a specific size.ServerConfig::session_storage
: the default stores 256 sessions in memory.ServerConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ServerConfig::key_log
: key material is not logged.ServerConfig::send_tls13_tickets
: 4 tickets are sent.ignore_client_order: bool
Ignore the client’s ciphersuite order. Instead, +choose the top ciphersuite in the server list +which is supported by the client.
+max_fragment_size: Option<usize>
The maximum size of TLS message we’ll emit. If None, we don’t limit TLS +message lengths except to the 2**16 limit specified in the standard.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ServerConnection::new.
+Setting this value to the TCP MSS may improve latency for stream-y workloads.
+session_storage: Arc<dyn StoresServerSessions + Send + Sync>
How to store client sessions.
+ticketer: Arc<dyn ProducesTickets>
How to produce tickets.
+cert_resolver: Arc<dyn ResolvesServerCert>
How to choose a server cert and key.
+alpn_protocols: Vec<Vec<u8>>
Protocol names we support, most preferred first. +If empty we don’t do ALPN at all.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+max_early_data_size: u32
Amount of early data to accept for sessions created by +this config. Specify 0 to disable early data. The +default is 0.
+Read the early data via [ServerConnection::early_data
].
The units for this are both plaintext bytes, and ciphertext +bytes, depending on whether the server accepts a client’s early_data +or not. It is therefore recommended to include some slop in +this value to account for the unknown amount of ciphertext +expansion in the latter case.
+send_half_rtt_data: bool
Whether the server should send “0.5RTT” data. This means the server +sends data after its first flight of handshake messages, without +waiting for the client to complete the handshake.
+This can improve TTFB latency for either server-speaks-first protocols,
+or client-speaks-first protocols when paired with “0RTT” data. This
+comes at the cost of a subtle weakening of the normal handshake
+integrity guarantees that TLS provides. Note that the initial
+ClientHello
is indirectly authenticated because it is included
+in the transcript used to derive the keys used to encrypt the data.
This only applies to TLS1.3 connections. TLS1.2 connections cannot +do this optimisation and this setting is ignored for them. It is +also ignored for TLS1.3 connections that even attempt client +authentication.
+This defaults to false. This means the first application data
+sent by the server comes after receiving and validating the client’s
+handshake up to the Finished
message. This is the safest option.
send_tls13_tickets: usize
How many TLS1.3 tickets to send immediately after a successful +handshake.
+Because TLS1.3 tickets are single-use, this allows +a client to perform multiple resumptions.
+The default is 4.
+If this is 0, no tickets are sent and clients will not be able to +do any resumption.
+Create builder to build up the server configuration.
+For more information, see the [ConfigBuilder
] documentation.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Acceptor { /* private fields */ }
Accept TLS connections via the rustls
crate.
Constructs rustls
based acceptor service factory.
Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+Default timeout is 3 seconds.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct AcceptorService { /* private fields */ }
Rustls based acceptor service.
+Service
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsStream<IO>(/* private fields */);
Wraps a rustls
based async TLS stream in order to implement [ActixStream
].
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morerustls
v0.22 based TLS connection acceptor service.
See Acceptor
for main service factory docs.
rustls
that are useful for acceptors.rustls
crate.rustls
based async TLS stream in order to implement [ActixStream
].pub struct ServerConfig {
+ pub ignore_client_order: bool,
+ pub max_fragment_size: Option<usize>,
+ pub session_storage: Arc<dyn StoresServerSessions + Send + Sync>,
+ pub ticketer: Arc<dyn ProducesTickets>,
+ pub cert_resolver: Arc<dyn ResolvesServerCert>,
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub key_log: Arc<dyn KeyLog>,
+ pub enable_secret_extraction: bool,
+ pub max_early_data_size: u32,
+ pub send_half_rtt_data: bool,
+ pub send_tls13_tickets: usize,
+ /* private fields */
+}
Common configuration for a set of server sessions.
+Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
+from the operating system to add to the RootCertStore
passed to a ClientCertVerifier
+builder may take on the order of a few hundred milliseconds.
These must be created via the ServerConfig::builder()
or ServerConfig::builder_with_provider()
+function.
ServerConfig::max_fragment_size
: the default is None
(meaning 16kB).ServerConfig::session_storage
: the default stores 256 sessions in memory.ServerConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ServerConfig::key_log
: key material is not logged.ServerConfig::send_tls13_tickets
: 4 tickets are sent.ignore_client_order: bool
Ignore the client’s ciphersuite order. Instead, +choose the top ciphersuite in the server list +which is supported by the client.
+max_fragment_size: Option<usize>
The maximum size of plaintext input to be emitted in a single TLS record. +A value of None is equivalent to the TLS maximum of 16 kB.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ServerConnection::new.
+Setting this value to a little less than the TCP MSS may improve latency +for stream-y workloads.
+session_storage: Arc<dyn StoresServerSessions + Send + Sync>
How to store client sessions.
+ticketer: Arc<dyn ProducesTickets>
How to produce tickets.
+cert_resolver: Arc<dyn ResolvesServerCert>
How to choose a server cert and key. This is usually set by +[ConfigBuilder::with_single_cert] or [ConfigBuilder::with_cert_resolver]. +For async applications, see also [Acceptor].
+alpn_protocols: Vec<Vec<u8>>
Protocol names we support, most preferred first. +If empty we don’t do ALPN at all.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+enable_secret_extraction: bool
Allows traffic secrets to be extracted after the handshake, +e.g. for kTLS setup.
+max_early_data_size: u32
Amount of early data to accept for sessions created by +this config. Specify 0 to disable early data. The +default is 0.
+Read the early data via [ServerConnection::early_data
].
The units for this are both plaintext bytes, and ciphertext +bytes, depending on whether the server accepts a client’s early_data +or not. It is therefore recommended to include some slop in +this value to account for the unknown amount of ciphertext +expansion in the latter case.
+send_half_rtt_data: bool
Whether the server should send “0.5RTT” data. This means the server +sends data after its first flight of handshake messages, without +waiting for the client to complete the handshake.
+This can improve TTFB latency for either server-speaks-first protocols,
+or client-speaks-first protocols when paired with “0RTT” data. This
+comes at the cost of a subtle weakening of the normal handshake
+integrity guarantees that TLS provides. Note that the initial
+ClientHello
is indirectly authenticated because it is included
+in the transcript used to derive the keys used to encrypt the data.
This only applies to TLS1.3 connections. TLS1.2 connections cannot +do this optimisation and this setting is ignored for them. It is +also ignored for TLS1.3 connections that even attempt client +authentication.
+This defaults to false. This means the first application data
+sent by the server comes after receiving and validating the client’s
+handshake up to the Finished
message. This is the safest option.
send_tls13_tickets: usize
How many TLS1.3 tickets to send immediately after a successful +handshake.
+Because TLS1.3 tickets are single-use, this allows +a client to perform multiple resumptions.
+The default is 4.
+If this is 0, no tickets are sent and clients will not be able to +do any resumption.
+Create a builder for a server configuration with the default
+[CryptoProvider
]: [crypto::ring::default_provider
] and safe ciphersuite and protocol
+defaults.
For more information, see the [ConfigBuilder
] documentation.
Create a builder for a server configuration with the default
+[CryptoProvider
]: [crypto::ring::default_provider
], safe ciphersuite defaults and
+the provided protocol versions.
Panics if provided an empty slice of supported versions.
+For more information, see the [ConfigBuilder
] documentation.
Create a builder for a server configuration with a specific [CryptoProvider
].
This will use the provider’s configured ciphersuites. You must additionally choose
+which protocol versions to enable, using with_protocol_versions
or
+with_safe_default_protocol_versions
and handling the Result
in case a protocol
+version is not supported by the provider’s ciphersuites.
For more information, see the [ConfigBuilder
] documentation.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Acceptor { /* private fields */ }
Accept TLS connections via the rustls
crate.
Constructs rustls
based acceptor service factory.
Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+Default timeout is 3 seconds.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct AcceptorService { /* private fields */ }
Rustls based acceptor service.
+Service
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsStream<IO>(/* private fields */);
Wraps a rustls
based async TLS stream in order to implement [ActixStream
].
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morerustls
v0.23 based TLS connection acceptor service.
See Acceptor
for main service factory docs.
rustls
that are useful for acceptors.rustls
crate.rustls
based async TLS stream in order to implement [ActixStream
].pub struct ServerConfig {Show 15 fields
+ pub ignore_client_order: bool,
+ pub max_fragment_size: Option<usize>,
+ pub session_storage: Arc<dyn StoresServerSessions + Send + Sync>,
+ pub ticketer: Arc<dyn ProducesTickets>,
+ pub cert_resolver: Arc<dyn ResolvesServerCert>,
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub key_log: Arc<dyn KeyLog>,
+ pub enable_secret_extraction: bool,
+ pub max_early_data_size: u32,
+ pub send_half_rtt_data: bool,
+ pub send_tls13_tickets: usize,
+ pub time_provider: Arc<dyn TimeProvider>,
+ pub cert_compressors: Vec<&'static dyn CertCompressor>,
+ pub cert_compression_cache: Arc<CompressionCache>,
+ pub cert_decompressors: Vec<&'static dyn CertDecompressor>,
+ /* private fields */
+}
Common configuration for a set of server sessions.
+Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
+from the operating system to add to the RootCertStore
passed to a ClientCertVerifier
+builder may take on the order of a few hundred milliseconds.
These must be created via the ServerConfig::builder()
or ServerConfig::builder_with_provider()
+function.
ServerConfig::max_fragment_size
: the default is None
(meaning 16kB).ServerConfig::session_storage
: if the std
feature is enabled, the default stores 256
+sessions in memory. If the std
feature is not enabled, the default is to not store any
+sessions. In a no-std context, by enabling the hashbrown
feature you may provide your
+own session_storage
using ServerSessionMemoryCache
and a crate::lock::MakeMutex
+implementation.ServerConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ServerConfig::key_log
: key material is not logged.ServerConfig::send_tls13_tickets
: 4 tickets are sent.ServerConfig::cert_compressors
: depends on the crate features, see [compress::default_cert_compressors()
].ServerConfig::cert_compression_cache
: caches the most recently used 4 compressionsServerConfig::cert_decompressors
: depends on the crate features, see [compress::default_cert_decompressors()
].ignore_client_order: bool
Ignore the client’s ciphersuite order. Instead, +choose the top ciphersuite in the server list +which is supported by the client.
+max_fragment_size: Option<usize>
The maximum size of plaintext input to be emitted in a single TLS record. +A value of None is equivalent to the TLS maximum of 16 kB.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ServerConnection::new.
+Setting this value to a little less than the TCP MSS may improve latency +for stream-y workloads.
+session_storage: Arc<dyn StoresServerSessions + Send + Sync>
How to store client sessions.
+ticketer: Arc<dyn ProducesTickets>
How to produce tickets.
+cert_resolver: Arc<dyn ResolvesServerCert>
How to choose a server cert and key. This is usually set by +[ConfigBuilder::with_single_cert] or [ConfigBuilder::with_cert_resolver]. +For async applications, see also [Acceptor].
+alpn_protocols: Vec<Vec<u8>>
Protocol names we support, most preferred first. +If empty we don’t do ALPN at all.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+enable_secret_extraction: bool
Allows traffic secrets to be extracted after the handshake, +e.g. for kTLS setup.
+max_early_data_size: u32
Amount of early data to accept for sessions created by +this config. Specify 0 to disable early data. The +default is 0.
+Read the early data via [ServerConnection::early_data
].
The units for this are both plaintext bytes, and ciphertext +bytes, depending on whether the server accepts a client’s early_data +or not. It is therefore recommended to include some slop in +this value to account for the unknown amount of ciphertext +expansion in the latter case.
+send_half_rtt_data: bool
Whether the server should send “0.5RTT” data. This means the server +sends data after its first flight of handshake messages, without +waiting for the client to complete the handshake.
+This can improve TTFB latency for either server-speaks-first protocols,
+or client-speaks-first protocols when paired with “0RTT” data. This
+comes at the cost of a subtle weakening of the normal handshake
+integrity guarantees that TLS provides. Note that the initial
+ClientHello
is indirectly authenticated because it is included
+in the transcript used to derive the keys used to encrypt the data.
This only applies to TLS1.3 connections. TLS1.2 connections cannot +do this optimisation and this setting is ignored for them. It is +also ignored for TLS1.3 connections that even attempt client +authentication.
+This defaults to false. This means the first application data
+sent by the server comes after receiving and validating the client’s
+handshake up to the Finished
message. This is the safest option.
send_tls13_tickets: usize
How many TLS1.3 tickets to send immediately after a successful +handshake.
+Because TLS1.3 tickets are single-use, this allows +a client to perform multiple resumptions.
+The default is 4.
+If this is 0, no tickets are sent and clients will not be able to +do any resumption.
+time_provider: Arc<dyn TimeProvider>
Provides the current system time
+cert_compressors: Vec<&'static dyn CertCompressor>
How to compress the server’s certificate chain.
+If a client supports this extension, and advertises support +for one of the compression algorithms included here, the +server certificate will be compressed according to RFC8779.
+This only applies to TLS1.3 connections. It is ignored for +TLS1.2 connections.
+cert_compression_cache: Arc<CompressionCache>
Caching for compressed certificates.
+This is optional: [compress::CompressionCache::Disabled
] gives
+a cache that does no caching.
cert_decompressors: Vec<&'static dyn CertDecompressor>
How to decompress the clients’s certificate chain.
+If this is non-empty, the RFC8779 certificate compression +extension is offered when requesting client authentication, +and any compressed certificates are transparently decompressed +during the handshake.
+This only applies to TLS1.3 connections. It is ignored for +TLS1.2 connections.
+Create a builder for a server configuration with
+[the process-default CryptoProvider
][CryptoProvider#using-the-per-process-default-cryptoprovider]
+and safe protocol version defaults.
For more information, see the [ConfigBuilder
] documentation.
Create a builder for a server configuration with
+[the process-default CryptoProvider
][CryptoProvider#using-the-per-process-default-cryptoprovider]
+and the provided protocol versions.
Panics if
+CryptoProvider
cannot be resolved using a combination of
+the crate features and process default.For more information, see the [ConfigBuilder
] documentation.
Create a builder for a server configuration with a specific [CryptoProvider
].
This will use the provider’s configured ciphersuites. You must additionally choose
+which protocol versions to enable, using with_protocol_versions
or
+with_safe_default_protocol_versions
and handling the Result
in case a protocol
+version is not supported by the provider’s ciphersuites.
For more information, see the [ConfigBuilder
] documentation.
Create a builder for a server configuration with no default implementation details.
+This API must be used by no_std
users.
You must provide a specific [TimeProvider
].
You must provide a specific [CryptoProvider
].
This will use the provider’s configured ciphersuites. You must additionally choose
+which protocol versions to enable, using with_protocol_versions
or
+with_safe_default_protocol_versions
and handling the Result
in case a protocol
+version is not supported by the provider’s ciphersuites.
For more information, see the [ConfigBuilder
] documentation.
Return true
if connections made with this ServerConfig
will
+operate in FIPS mode.
This is different from [CryptoProvider::fips()
]: [CryptoProvider::fips()
]
+is concerned only with cryptography, whereas this also covers TLS-level
+configuration that NIST recommends.
Return the crypto provider used to construct this client configuration.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Acceptor { /* private fields */ }
Accept TLS connections via the rustls
crate.
Constructs rustls
based acceptor service factory.
Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+Default timeout is 3 seconds.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct AcceptorService { /* private fields */ }
Rustls based acceptor service.
+Service
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsStream<IO>(/* private fields */);
Wraps a rustls
based async TLS stream in order to implement [ActixStream
].
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRedirecting to ../../../actix_tls/connect/struct.Connection.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/connector/struct.Connector.html b/actix_tls/connect/connector/struct.Connector.html new file mode 100644 index 0000000000..7ed48d9274 --- /dev/null +++ b/actix_tls/connect/connector/struct.Connector.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_tls/connect/struct.Connector.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/connector/struct.ConnectorService.html b/actix_tls/connect/connector/struct.ConnectorService.html new file mode 100644 index 0000000000..022ca9353a --- /dev/null +++ b/actix_tls/connect/connector/struct.ConnectorService.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_tls/connect/struct.ConnectorService.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/enum.ConnectError.html b/actix_tls/connect/enum.ConnectError.html new file mode 100644 index 0000000000..d0619fa037 --- /dev/null +++ b/actix_tls/connect/enum.ConnectError.html @@ -0,0 +1,29 @@ +pub enum ConnectError {
+ Resolver(Box<dyn Error>),
+ NoRecords,
+ InvalidInput,
+ Unresolved,
+ Io(Error),
+}
Errors that can result from using a connector service.
+Failed to resolve the hostname.
+No DNS records.
+Invalid input.
+Unresolved host name.
+Connection IO error.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRedirecting to ../../../actix_tls/connect/enum.ConnectError.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/host/trait.Host.html b/actix_tls/connect/host/trait.Host.html new file mode 100644 index 0000000000..d28ee92572 --- /dev/null +++ b/actix_tls/connect/host/trait.Host.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_tls/connect/trait.Host.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/index.html b/actix_tls/connect/index.html new file mode 100644 index 0000000000..c54b2c5a4c --- /dev/null +++ b/actix_tls/connect/index.html @@ -0,0 +1,13 @@ +TCP and TLS connector services.
+Host
(if needed) with given Resolver
and collect list of socket addresses.TcpStream
.TcpStream
with the TCP connector service.AsyncRead
and AsyncWrite
.Redirecting to ../../../actix_tls/connect/struct.ConnectInfo.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/native_tls/index.html b/actix_tls/connect/native_tls/index.html new file mode 100644 index 0000000000..74c97ac442 --- /dev/null +++ b/actix_tls/connect/native_tls/index.html @@ -0,0 +1,3 @@ +Native-TLS based connector service.
+See TlsConnector
for main connector service factory docs.
native-tls
and tokio-native-tls
that are useful for connectors.native-tls
.pub struct AsyncTlsStream<S>(/* private fields */);
A wrapper around an underlying raw stream which implements the TLS or SSL +protocol.
+A TlsStream<S>
represents a handshake that has been completed successfully
+and both the server and the client are ready for receiving and sending
+data. Bytes read from a TlsStream
are decrypted from S
and bytes written
+to a TlsStream
are encrypted when passing through to S
.
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnector(/* private fields */);
A builder for client-side TLS connections.
+use native_tls::TlsConnector;
+use std::io::{Read, Write};
+use std::net::TcpStream;
+
+let connector = TlsConnector::new().unwrap();
+
+let stream = TcpStream::connect("google.com:443").unwrap();
+let mut stream = connector.connect("google.com", stream).unwrap();
+
+stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+let mut res = vec![];
+stream.read_to_end(&mut res).unwrap();
+println!("{}", String::from_utf8_lossy(&res));
Returns a new connector with default settings.
+Returns a new builder for a TlsConnector
.
Initiates a TLS handshake.
+The provided domain will be used for both SNI and certificate hostname +validation.
+If the socket is nonblocking and a WouldBlock
error is returned during
+the handshake, a HandshakeError::WouldBlock
error will be returned
+which can be used to restart the handshake when the socket is ready
+again.
The domain is ignored if both SNI and hostname verification are +disabled.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnector { /* private fields */ }
Connector service and factory using native-tls
.
Constructs new connector service from a native-tls
connector.
This type is it’s own service factory, so it can be used in that setting, too.
+source
. Read moreThe native-tls
connector is both it’s ServiceFactory and Service impl type.
+As the factory and service share the same type and state.
Service
created by this factory.Service
instance.gService
Self
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreOpenSSL based connector service.
+See TlsConnector
for main connector service factory docs.
openssl
and tokio-openssl
that are useful for connectors.openssl
.openssl
.pub enum HandshakeError<S> {
+ SetupFailure(ErrorStack),
+ Failure(MidHandshakeSslStream<S>),
+ WouldBlock(MidHandshakeSslStream<S>),
+}
An error or intermediate state after a TLS handshake attempt.
+Setup failed.
+The handshake failed.
+The handshake encountered a WouldBlock
error midway through.
This error will never be returned for blocking streams.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRe-exports from openssl
and tokio-openssl
that are useful for connectors.
openssl::ssl::SslStream
.SslConnector
s.SslContext
will speak.pub struct AsyncSslStream<S>(/* private fields */);
An asynchronous version of openssl::ssl::SslStream
.
Like SslStream::new
.
Like SslStream::connect
.
A convenience method wrapping poll_connect
.
Like SslStream::accept
.
A convenience method wrapping poll_accept
.
Like SslStream::do_handshake
.
A convenience method wrapping poll_do_handshake
.
A convenience method wrapping poll_read_early_data
.
A convenience method wrapping poll_write_early_data
.
buf
into the object. Read morepoll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Error { /* private fields */ }
An SSL error.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SslConnector(/* private fields */);
A type which wraps client-side streams in a TLS session.
+OpenSSL’s default configuration is highly insecure. This connector manages the OpenSSL +structures, configuring cipher suites, session options, hostname verification, and more.
+OpenSSL’s built-in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0, +and a custom implementation is used when linking against OpenSSL 1.0.1.
+Creates a new builder for TLS connections.
+The default configuration is subject to change, and is currently derived from Python.
+Initiates a client-side TLS session on a stream.
+The domain is used for SNI and hostname verification.
+Returns a structure allowing for configuration of a single TLS session before connection.
+Consumes the SslConnector
, returning the inner raw SslContext
.
Returns a shared reference to the inner raw SslContext
.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SslConnectorBuilder(/* private fields */);
A builder for SslConnector
s.
Consumes the builder, returning an SslConnector
.
Configures the certificate verification method for new connections.
+This corresponds to SSL_CTX_set_verify
.
Configures the certificate verification method for new connections and +registers a verification callback.
+The callback is passed a boolean indicating if OpenSSL’s internal verification succeeded as
+well as a reference to the X509StoreContext
which can be used to examine the certificate
+chain. It should return a boolean indicating if verification succeeded.
This corresponds to SSL_CTX_set_verify
.
Configures the server name indication (SNI) callback for new connections.
+SNI is used to allow a single server to handle requests for multiple domains, each of which +has its own certificate chain and configuration.
+Obtain the server name with the servername
method and then set the corresponding context
+with set_ssl_context
This corresponds to SSL_CTX_set_tlsext_servername_callback
.
Sets the certificate verification depth.
+If the peer’s certificate chain is longer than this value, verification will fail.
+This corresponds to SSL_CTX_set_verify_depth
.
Sets a custom certificate store for verifying peer certificates.
+Requires OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_set0_verify_cert_store
.
Replaces the context’s certificate store.
+This corresponds to SSL_CTX_set_cert_store
.
Controls read ahead behavior.
+If enabled, OpenSSL will read as much data as is available from the underlying stream, +instead of a single record at a time.
+It has no effect when used with DTLS.
+This corresponds to SSL_CTX_set_read_ahead
.
Sets the mode used by the context, returning the previous mode.
+This corresponds to SSL_CTX_set_mode
.
Sets the parameters to be used during ephemeral Diffie-Hellman key exchange.
+This corresponds to SSL_CTX_set_tmp_dh
.
Sets the callback which will generate parameters to be used during ephemeral Diffie-Hellman +key exchange.
+The callback is provided with a reference to the Ssl
for the session, as well as a boolean
+indicating if the selected cipher is export-grade, and the key length. The export and key
+length options are archaic and should be ignored in almost all cases.
This corresponds to SSL_CTX_set_tmp_dh_callback
.
Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange.
+This corresponds to SSL_CTX_set_tmp_ecdh
.
Use the default locations of trusted certificates for verification.
+These locations are read from the SSL_CERT_FILE
and SSL_CERT_DIR
environment variables
+if present, or defaults specified at OpenSSL build time otherwise.
This corresponds to SSL_CTX_set_default_verify_paths
.
Loads trusted root certificates from a file.
+The file should contain a sequence of PEM-formatted CA certificates.
+This corresponds to SSL_CTX_load_verify_locations
.
Sets the list of CA names sent to the client.
+The CA certificates must still be added to the trust root - they are not automatically set +as trusted by this method.
+This corresponds to SSL_CTX_set_client_CA_list
.
Add the provided CA certificate to the list sent by the server to the client when +requesting client-side TLS authentication.
+This corresponds to SSL_CTX_add_client_CA
.
Set the context identifier for sessions.
+This value identifies the server’s session cache to clients, telling them when they’re +able to reuse sessions. It should be set to a unique value per server, unless multiple +servers share a session cache.
+This value should be set when using client certificates, or each request will fail its +handshake and need to be restarted.
+This corresponds to SSL_CTX_set_session_id_context
.
Loads a leaf certificate from a file.
+Only a single certificate will be loaded - use add_extra_chain_cert
to add the remainder
+of the certificate chain, or set_certificate_chain_file
to load the entire chain from a
+single file.
This corresponds to SSL_CTX_use_certificate_file
.
Loads a certificate chain from a file.
+The file should contain a sequence of PEM-formatted certificates, the first being the leaf +certificate, and the remainder forming the chain of certificates up to and including the +trusted root certificate.
+This corresponds to SSL_CTX_use_certificate_chain_file
.
Sets the leaf certificate.
+Use add_extra_chain_cert
to add the remainder of the certificate chain.
This corresponds to SSL_CTX_use_certificate
.
Appends a certificate to the certificate chain.
+This chain should contain all certificates necessary to go from the certificate specified by
+set_certificate
to a trusted root.
This corresponds to SSL_CTX_add_extra_chain_cert
.
Loads the private key from a file.
+This corresponds to SSL_CTX_use_PrivateKey_file
.
Sets the private key.
+This corresponds to SSL_CTX_use_PrivateKey
.
Sets the list of supported ciphers for protocols before TLSv1.3.
+The set_ciphersuites
method controls the cipher suites for TLSv1.3.
See ciphers
for details on the format.
This corresponds to SSL_CTX_set_cipher_list
.
Sets the list of supported ciphers for the TLSv1.3 protocol.
+The set_cipher_list
method controls the cipher suites for protocols before TLSv1.3.
The format consists of TLSv1.3 cipher suite names separated by :
characters in order of
+preference.
Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+This corresponds to SSL_CTX_set_ciphersuites
.
Sets the options used by the context, returning the old set.
+This enables the specified options, but does not disable unspecified options. Use
+clear_options
for that.
This corresponds to SSL_CTX_set_options
.
Returns the options used by the context.
+This corresponds to SSL_CTX_get_options
.
Clears the options used by the context, returning the old set.
+This corresponds to SSL_CTX_clear_options
.
Sets the minimum supported protocol version.
+A value of None
will enable protocol versions down to the lowest version supported by
+OpenSSL.
Requires BoringSSL or OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_min_proto_version
.
Sets the maximum supported protocol version.
+A value of None
will enable protocol versions up to the highest version supported by
+OpenSSL.
Requires BoringSSL or OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_max_proto_version
.
Gets the minimum supported protocol version.
+A value of None
indicates that all versions down to the lowest version supported by
+OpenSSL are enabled.
Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
+This corresponds to SSL_CTX_get_min_proto_version
.
Gets the maximum supported protocol version.
+A value of None
indicates that all versions up to the highest version supported by
+OpenSSL are enabled.
Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
+This corresponds to SSL_CTX_get_max_proto_version
.
Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
+The input must be in ALPN “wire format”. It consists of a sequence of supported protocol
+names prefixed by their byte length. For example, the protocol list consisting of spdy/1
+and http/1.1
is encoded as b"\x06spdy/1\x08http/1.1"
. The protocols are ordered by
+preference.
Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_alpn_protos
.
Enables the DTLS extension “use_srtp” as defined in RFC5764.
+This corresponds to SSL_CTX_set_tlsext_use_srtp
.
Sets the callback used by a server to select a protocol for Application Layer Protocol +Negotiation (ALPN).
+The callback is provided with the client’s protocol list in ALPN wire format. See the
+documentation for SslContextBuilder::set_alpn_protos
for details. It should return one
+of those protocols on success. The select_next_proto
function implements the standard
+protocol selection algorithm.
Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+This corresponds to SSL_CTX_set_alpn_select_cb
.
Checks for consistency between the private key and certificate.
+This corresponds to SSL_CTX_check_private_key
.
Returns a shared reference to the context’s certificate store.
+This corresponds to SSL_CTX_get_cert_store
.
Returns a mutable reference to the context’s certificate store.
+This corresponds to SSL_CTX_get_cert_store
.
Returns a reference to the X509 verification configuration.
+Requires BoringSSL or OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_get0_param
.
Returns a mutable reference to the X509 verification configuration.
+Requires BoringSSL or OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_get0_param
.
Sets the callback dealing with OCSP stapling.
+On the client side, this callback is responsible for validating the OCSP status response
+returned by the server. The status may be retrieved with the SslRef::ocsp_status
method.
+A response of Ok(true)
indicates that the OCSP status is valid, and a response of
+Ok(false)
indicates that the OCSP status is invalid and the handshake should be
+terminated.
On the server side, this callback is responsible for setting the OCSP status response to be
+returned to clients. The status may be set with the SslRef::set_ocsp_status
method. A
+response of Ok(true)
indicates that the OCSP status should be returned to the client, and
+Ok(false)
indicates that the status should not be returned to the client.
This corresponds to SSL_CTX_set_tlsext_status_cb
.
Sets the callback for providing an identity and pre-shared key for a TLS-PSK client.
+The callback will be called with the SSL context, an identity hint if one was provided +by the server, a mutable slice for each of the identity and pre-shared key bytes. The +identity must be written as a null-terminated C string.
+This corresponds to SSL_CTX_set_psk_client_callback
.
set_psk_client_callback
Sets the callback for providing an identity and pre-shared key for a TLS-PSK server.
+The callback will be called with the SSL context, an identity provided by the client, +and, a mutable slice for the pre-shared key bytes. The callback returns the number of +bytes in the pre-shared key.
+This corresponds to SSL_CTX_set_psk_server_callback
.
Sets the callback which is called when new sessions are negotiated.
+This can be used by clients to implement session caching. While in TLSv1.2 the session is
+available to access via SslRef::session
immediately after the handshake completes, this
+is not the case for TLSv1.3. There, a session is not generally available immediately, and
+the server may provide multiple session tokens to the client over a single session. The new
+session callback is a portable way to deal with both cases.
Note that session caching must be enabled for the callback to be invoked, and it defaults
+off for clients. set_session_cache_mode
controls that behavior.
This corresponds to SSL_CTX_sess_set_new_cb
.
Sets the callback which is called when sessions are removed from the context.
+Sessions can be removed because they have timed out or because they are considered faulty.
+This corresponds to SSL_CTX_sess_set_remove_cb
.
Sets the callback which is called when a client proposed to resume a session but it was not +found in the internal cache.
+The callback is passed a reference to the session ID provided by the client. It should +return the session corresponding to that ID if available. This is only used for servers, not +clients.
+The returned SslSession
must not be associated with a different SslContext
.
This corresponds to SSL_CTX_sess_set_get_cb
.
Sets the TLS key logging callback.
+The callback is invoked whenever TLS key material is generated, and is passed a line of NSS +SSLKEYLOGFILE-formatted text. This can be used by tools like Wireshark to decrypt message +traffic. The line does not contain a trailing newline.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_set_keylog_callback
.
Sets the session caching mode use for connections made with the context.
+Returns the previous session caching mode.
+This corresponds to SSL_CTX_set_session_cache_mode
.
Sets the callback for generating an application cookie for TLS1.3 +stateless handshakes.
+The callback will be called with the SSL context and a slice into which the cookie +should be written. The callback should return the number of bytes written.
+This corresponds to SSL_CTX_set_stateless_cookie_generate_cb
.
Sets the callback for verifying an application cookie for TLS1.3 +stateless handshakes.
+The callback will be called with the SSL context and the cookie supplied by the +client. It should return true if and only if the cookie is valid.
+Note that the OpenSSL implementation independently verifies the integrity of +application cookies using an HMAC before invoking the supplied callback.
+This corresponds to SSL_CTX_set_stateless_cookie_verify_cb
.
Sets the callback for generating a DTLSv1 cookie
+The callback will be called with the SSL context and a slice into which the cookie +should be written. The callback should return the number of bytes written.
+This corresponds to SSL_CTX_set_cookie_generate_cb
.
Sets the callback for verifying a DTLSv1 cookie
+The callback will be called with the SSL context and the cookie supplied by the +client. It should return true if and only if the cookie is valid.
+This corresponds to SSL_CTX_set_cookie_verify_cb
.
Sets the extra data at the specified index.
+This can be used to provide data to callbacks registered with the context. Use the
+SslContext::new_ex_index
method to create an Index
.
This corresponds to SSL_CTX_set_ex_data
.
Adds a custom extension for a TLS/DTLS client or server for all supported protocol versions.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_add_custom_ext
.
Sets the maximum amount of early data that will be accepted on incoming connections.
+Defaults to 0.
+Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+This corresponds to SSL_CTX_set_max_early_data
.
Sets a callback which will be invoked just after the client’s hello message is received.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_set_client_hello_cb
.
Sets the context’s session cache size limit, returning the previous limit.
+A value of 0 means that the cache size is unbounded.
+This corresponds to SSL_CTX_sess_set_cache_size
.
Sets the context’s supported signature algorithms.
+Requires OpenSSL 1.0.2 or newer.
+This corresponds to SSL_CTX_set1_sigalgs_list
.
Sets the context’s supported elliptic curve groups.
+Requires BoringSSL or OpenSSL 1.1.1 or LibreSSL 2.5.1 or newer.
+This corresponds to SSL_CTX_set1_groups_list
.
Sets the number of TLS 1.3 session tickets that will be sent to a client after a full +handshake.
+Requires OpenSSL 1.1.1 or newer.
+This corresponds to SSL_CTX_set_num_tickets
.
Set the context’s security level to a value between 0 and 5, inclusive. +A security value of 0 allows allows all parameters and algorithms.
+Requires OpenSSL 1.1.0 or newer.
+This corresponds to SSL_CTX_set_security_level
.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SslMethod(/* private fields */);
A type specifying the kind of protocol an SslContext
will speak.
Support all versions of the TLS protocol.
+This corresponds to TLS_method
.
Support all versions of the DTLS protocol.
+This corresponds to DTLS_method
.
Support all versions of the TLS protocol, explicitly as a client.
+This corresponds to TLS_client_method
.
Support all versions of the TLS protocol, explicitly as a server.
+This corresponds to TLS_server_method
.
Constructs an SslMethod
from a pointer to the underlying OpenSSL value.
The caller must ensure the pointer is valid.
+Returns a pointer to the underlying OpenSSL value.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnector { /* private fields */ }
Connector service factory using openssl
.
Constructs new connector service factory from an openssl
connector.
Constructs new connector service from an openssl
connector.
Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnectorService { /* private fields */ }
Connector service using openssl
.
Service
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRedirecting to ../../../actix_tls/connect/trait.Resolve.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/resolver/struct.Resolver.html b/actix_tls/connect/resolver/struct.Resolver.html new file mode 100644 index 0000000000..e3562aefbe --- /dev/null +++ b/actix_tls/connect/resolver/struct.Resolver.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_tls/connect/struct.Resolver.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/resolver/struct.ResolverService.html b/actix_tls/connect/resolver/struct.ResolverService.html new file mode 100644 index 0000000000..8cfb92b983 --- /dev/null +++ b/actix_tls/connect/resolver/struct.ResolverService.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_tls/connect/struct.ResolverService.html...
+ + + \ No newline at end of file diff --git a/actix_tls/connect/rustls_0_20/fn.native_roots_cert_store.html b/actix_tls/connect/rustls_0_20/fn.native_roots_cert_store.html new file mode 100644 index 0000000000..5854d04e0c --- /dev/null +++ b/actix_tls/connect/rustls_0_20/fn.native_roots_cert_store.html @@ -0,0 +1,3 @@ +pub fn native_roots_cert_store() -> Result<RootCertStore>
Returns root certificates via rustls-native-certs
crate as a rustls certificate store.
See rustls_native_certs::load_native_certs()
for more info on behavior and errors.
pub fn webpki_roots_cert_store() -> RootCertStore
Returns standard root certificates from webpki-roots
crate as a rustls certificate store.
Rustls based connector service.
+See TlsConnector
for main connector service factory docs.
rustls
v0.20 ecosystem that are useful for connectors.rustls
.rustls
.rustls-native-certs
crate as a rustls certificate store.webpki-roots
crate as a rustls certificate store.Re-exports from the rustls
v0.20 ecosystem that are useful for connectors.
pub static TLS_SERVER_ROOTS: TlsServerTrustAnchors<'static>
pub struct AsyncTlsStream<IO> { /* private fields */ }
A wrapper around an underlying raw stream which implements the TLS or SSL +protocol.
+Note: that it does not guarantee the final data to be sent.
+To be cautious, you must manually call flush
.
poll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ClientConfig {
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub session_storage: Arc<dyn StoresClientSessions>,
+ pub max_fragment_size: Option<usize>,
+ pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
+ pub enable_tickets: bool,
+ pub enable_sni: bool,
+ pub key_log: Arc<dyn KeyLog>,
+ pub enable_early_data: bool,
+ /* private fields */
+}
Common configuration for (typically) all connections made by +a program.
+Making one of these can be expensive, and should be +once per process rather than once per connection.
+These must be created via the ClientConfig::builder()
function.
ClientConfig::max_fragment_size
: the default is None
: TLS packets are not fragmented to a specific size.ClientConfig::session_storage
: the default stores 256 sessions in memory.ClientConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ClientConfig::key_log
: key material is not logged.alpn_protocols: Vec<Vec<u8>>
Which ALPN protocols we include in our client hello. +If empty, no ALPN extension is sent.
+session_storage: Arc<dyn StoresClientSessions>
How we store session data or tickets.
+max_fragment_size: Option<usize>
The maximum size of TLS message we’ll emit. If None, we don’t limit TLS +message lengths except to the 2**16 limit specified in the standard.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ClientConnection::new.
+Setting this value to the TCP MSS may improve latency for stream-y workloads.
+client_auth_cert_resolver: Arc<dyn ResolvesClientCert>
How to decide what client auth certificate/keys to use.
+enable_tickets: bool
Whether to support RFC5077 tickets. You must provide a working
+session_storage
member for this to have any meaningful
+effect.
The default is true.
+enable_sni: bool
Whether to send the Server Name Indication (SNI) extension +during the client handshake.
+The default is true.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+enable_early_data: bool
Whether to send data on the first flight (“early data”) in +TLS 1.3 handshakes.
+The default is false.
+Create a builder to build up the client configuration.
+For more information, see the [ConfigBuilder
] documentation.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnector { /* private fields */ }
Connector service factory using rustls
.
Constructs new connector service factory from a rustls
client configuration.
Constructs new connector service from a rustls
client configuration.
source
. Read moreService
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnectorService { /* private fields */ }
Connector service using rustls
.
source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn native_roots_cert_store() -> Result<RootCertStore>
Returns root certificates via rustls-native-certs
crate as a rustls certificate store.
See rustls_native_certs::load_native_certs()
for more info on behavior and errors.
pub fn webpki_roots_cert_store() -> RootCertStore
Returns standard root certificates from webpki-roots
crate as a rustls certificate store.
Rustls based connector service.
+See TlsConnector
for main connector service factory docs.
rustls
v0.21 ecosystem that are useful for connectors.rustls
.rustls
.rustls-native-certs
crate as a rustls certificate store.webpki-roots
crate as a rustls certificate store.pub const TLS_SERVER_ROOTS: &'static [TrustAnchor<'static>];
Re-exports from the rustls
v0.21 ecosystem that are useful for connectors.
pub struct AsyncTlsStream<IO> { /* private fields */ }
A wrapper around an underlying raw stream which implements the TLS or SSL +protocol.
+Note: that it does not guarantee the final data to be sent.
+To be cautious, you must manually call flush
.
poll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ClientConfig {
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub resumption: Resumption,
+ pub max_fragment_size: Option<usize>,
+ pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
+ pub enable_sni: bool,
+ pub key_log: Arc<dyn KeyLog>,
+ pub enable_early_data: bool,
+ /* private fields */
+}
Common configuration for (typically) all connections made by a program.
+Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
+from the operating system to add to the RootCertStore
passed to with_root_certificates()
+(the rustls-native-certs crate is often used for this) may take on the order of a few hundred
+milliseconds.
These must be created via the ClientConfig::builder()
function.
ClientConfig::max_fragment_size
: the default is None
: TLS packets are not fragmented to a specific size.ClientConfig::resumption
: supports resumption with up to 256 server names, using session
+ids or tickets, with a max of eight tickets per server.ClientConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ClientConfig::key_log
: key material is not logged.alpn_protocols: Vec<Vec<u8>>
Which ALPN protocols we include in our client hello. +If empty, no ALPN extension is sent.
+resumption: Resumption
How and when the client can resume a previous session.
+max_fragment_size: Option<usize>
The maximum size of TLS message we’ll emit. If None, we don’t limit TLS +message lengths except to the 2**16 limit specified in the standard.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ClientConnection::new.
+Setting this value to the TCP MSS may improve latency for stream-y workloads.
+client_auth_cert_resolver: Arc<dyn ResolvesClientCert>
How to decide what client auth certificate/keys to use.
+enable_sni: bool
Whether to send the Server Name Indication (SNI) extension +during the client handshake.
+The default is true.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+enable_early_data: bool
Whether to send data on the first flight (“early data”) in +TLS 1.3 handshakes.
+The default is false.
+Create a builder to build up the client configuration.
+For more information, see the [ConfigBuilder
] documentation.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnector { /* private fields */ }
Connector service factory using rustls
.
Constructs new connector service factory from a rustls
client configuration.
Constructs new connector service from a rustls
client configuration.
source
. Read moreService
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnectorService { /* private fields */ }
Connector service using rustls
.
source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn native_roots_cert_store() -> Result<RootCertStore>
Returns root certificates via rustls-native-certs
crate as a rustls certificate store.
See rustls_native_certs::load_native_certs()
for more info on behavior and errors.
pub fn webpki_roots_cert_store() -> RootCertStore
Returns standard root certificates from webpki-roots
crate as a rustls certificate store.
Rustls based connector service.
+See TlsConnector
for main connector service factory docs.
rustls
v0.22 ecosystem that are useful for connectors.rustls
.rustls
.rustls-native-certs
crate as a rustls certificate store.webpki-roots
crate as a rustls certificate store.pub const TLS_SERVER_ROOTS: &'static [TrustAnchor<'static>];
Re-exports from the rustls
v0.22 ecosystem that are useful for connectors.
pub struct AsyncTlsStream<IO> { /* private fields */ }
A wrapper around an underlying raw stream which implements the TLS or SSL +protocol.
+Note: that it does not guarantee the final data to be sent.
+To be cautious, you must manually call flush
.
poll_write
, except that it writes from a slice of buffers. Read morepoll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ClientConfig {
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub resumption: Resumption,
+ pub max_fragment_size: Option<usize>,
+ pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
+ pub enable_sni: bool,
+ pub key_log: Arc<dyn KeyLog>,
+ pub enable_secret_extraction: bool,
+ pub enable_early_data: bool,
+ /* private fields */
+}
Common configuration for (typically) all connections made by a program.
+Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
+from the operating system to add to the RootCertStore
passed to with_root_certificates()
+(the rustls-native-certs crate is often used for this) may take on the order of a few hundred
+milliseconds.
These must be created via the ClientConfig::builder()
or ClientConfig::builder_with_provider()
+function.
ClientConfig::max_fragment_size
: the default is None
(meaning 16kB).ClientConfig::resumption
: supports resumption with up to 256 server names, using session
+ids or tickets, with a max of eight tickets per server.ClientConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ClientConfig::key_log
: key material is not logged.alpn_protocols: Vec<Vec<u8>>
Which ALPN protocols we include in our client hello. +If empty, no ALPN extension is sent.
+resumption: Resumption
How and when the client can resume a previous session.
+max_fragment_size: Option<usize>
The maximum size of plaintext input to be emitted in a single TLS record. +A value of None is equivalent to the TLS maximum of 16 kB.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ClientConnection::new.
+Setting this value to a little less than the TCP MSS may improve latency +for stream-y workloads.
+client_auth_cert_resolver: Arc<dyn ResolvesClientCert>
How to decide what client auth certificate/keys to use.
+enable_sni: bool
Whether to send the Server Name Indication (SNI) extension +during the client handshake.
+The default is true.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+enable_secret_extraction: bool
Allows traffic secrets to be extracted after the handshake, +e.g. for kTLS setup.
+enable_early_data: bool
Whether to send data on the first flight (“early data”) in +TLS 1.3 handshakes.
+The default is false.
+Create a builder for a client configuration with the default
+[CryptoProvider
]: [crypto::ring::default_provider
] and safe ciphersuite and
+protocol defaults.
For more information, see the [ConfigBuilder
] documentation.
Create a builder for a client configuration with the default
+[CryptoProvider
]: [crypto::ring::default_provider
], safe ciphersuite defaults and
+the provided protocol versions.
Panics if provided an empty slice of supported versions.
+For more information, see the [ConfigBuilder
] documentation.
Create a builder for a client configuration with a specific [CryptoProvider
].
This will use the provider’s configured ciphersuites. You must additionally choose
+which protocol versions to enable, using with_protocol_versions
or
+with_safe_default_protocol_versions
and handling the Result
in case a protocol
+version is not supported by the provider’s ciphersuites.
For more information, see the [ConfigBuilder
] documentation.
Access configuration options whose use is dangerous and requires +extra care.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnector { /* private fields */ }
Connector service factory using rustls
.
Constructs new connector service factory from a rustls
client configuration.
Constructs new connector service from a rustls
client configuration.
source
. Read moreService
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnectorService { /* private fields */ }
Connector service using rustls
.
source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn native_roots_cert_store() -> Result<RootCertStore>
Returns root certificates via rustls-native-certs
crate as a rustls certificate store.
See rustls_native_certs::load_native_certs()
for more info on behavior and errors.
pub fn webpki_roots_cert_store() -> RootCertStore
Returns standard root certificates from webpki-roots
crate as a rustls certificate store.
Rustls based connector service.
+See TlsConnector
for main connector service factory docs.
rustls
v0.23 ecosystem that are useful for connectors.rustls
.rustls
.rustls-native-certs
crate as a rustls certificate store.webpki-roots
crate as a rustls certificate store.pub const TLS_SERVER_ROOTS: &'static [TrustAnchor<'static>];
Re-exports from the rustls
v0.23 ecosystem that are useful for connectors.
pub struct AsyncTlsStream<IO> { /* private fields */ }
A wrapper around an underlying raw stream which implements the TLS or SSL +protocol.
+Note: that it does not guarantee the final data to be sent.
+To be cautious, you must manually call flush
.
Note: that it does not guarantee the final data to be sent.
+To be cautious, you must manually call flush
.
poll_write_vectored
+implementation. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ClientConfig {
+ pub alpn_protocols: Vec<Vec<u8>>,
+ pub resumption: Resumption,
+ pub max_fragment_size: Option<usize>,
+ pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
+ pub enable_sni: bool,
+ pub key_log: Arc<dyn KeyLog>,
+ pub enable_secret_extraction: bool,
+ pub enable_early_data: bool,
+ pub time_provider: Arc<dyn TimeProvider>,
+ pub cert_decompressors: Vec<&'static dyn CertDecompressor>,
+ pub cert_compressors: Vec<&'static dyn CertCompressor>,
+ pub cert_compression_cache: Arc<CompressionCache>,
+ /* private fields */
+}
Common configuration for (typically) all connections made by a program.
+Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
+from the operating system to add to the RootCertStore
passed to with_root_certificates()
+(the rustls-native-certs crate is often used for this) may take on the order of a few hundred
+milliseconds.
These must be created via the ClientConfig::builder()
or ClientConfig::builder_with_provider()
+function.
ClientConfig::max_fragment_size
: the default is None
(meaning 16kB).ClientConfig::resumption
: supports resumption with up to 256 server names, using session
+ids or tickets, with a max of eight tickets per server.ClientConfig::alpn_protocols
: the default is empty – no ALPN protocol is negotiated.ClientConfig::key_log
: key material is not logged.ClientConfig::cert_decompressors
: depends on the crate features, see [compress::default_cert_decompressors()
].ClientConfig::cert_compressors
: depends on the crate features, see [compress::default_cert_compressors()
].ClientConfig::cert_compression_cache
: caches the most recently used 4 compressionsalpn_protocols: Vec<Vec<u8>>
Which ALPN protocols we include in our client hello. +If empty, no ALPN extension is sent.
+resumption: Resumption
How and when the client can resume a previous session.
+max_fragment_size: Option<usize>
The maximum size of plaintext input to be emitted in a single TLS record. +A value of None is equivalent to the TLS maximum of 16 kB.
+rustls enforces an arbitrary minimum of 32 bytes for this field. +Out of range values are reported as errors from ClientConnection::new.
+Setting this value to a little less than the TCP MSS may improve latency +for stream-y workloads.
+client_auth_cert_resolver: Arc<dyn ResolvesClientCert>
How to decide what client auth certificate/keys to use.
+enable_sni: bool
Whether to send the Server Name Indication (SNI) extension +during the client handshake.
+The default is true.
+key_log: Arc<dyn KeyLog>
How to output key material for debugging. The default +does nothing.
+enable_secret_extraction: bool
Allows traffic secrets to be extracted after the handshake, +e.g. for kTLS setup.
+enable_early_data: bool
Whether to send data on the first flight (“early data”) in +TLS 1.3 handshakes.
+The default is false.
+time_provider: Arc<dyn TimeProvider>
Provides the current system time
+cert_decompressors: Vec<&'static dyn CertDecompressor>
How to decompress the server’s certificate chain.
+If this is non-empty, the RFC8779 certificate compression +extension is offered, and any compressed certificates are +transparently decompressed during the handshake.
+This only applies to TLS1.3 connections. It is ignored for +TLS1.2 connections.
+cert_compressors: Vec<&'static dyn CertCompressor>
How to compress the client’s certificate chain.
+If a server supports this extension, and advertises support +for one of the compression algorithms included here, the +client certificate will be compressed according to RFC8779.
+This only applies to TLS1.3 connections. It is ignored for +TLS1.2 connections.
+cert_compression_cache: Arc<CompressionCache>
Caching for compressed certificates.
+This is optional: [compress::CompressionCache::Disabled
] gives
+a cache that does no caching.
Create a builder for a client configuration with
+[the process-default CryptoProvider
][CryptoProvider#using-the-per-process-default-cryptoprovider]
+and safe protocol version defaults.
For more information, see the [ConfigBuilder
] documentation.
Create a builder for a client configuration with
+[the process-default CryptoProvider
][CryptoProvider#using-the-per-process-default-cryptoprovider]
+and the provided protocol versions.
Panics if
+CryptoProvider
cannot be resolved using a combination of
+the crate features and process default.For more information, see the [ConfigBuilder
] documentation.
Create a builder for a client configuration with a specific [CryptoProvider
].
This will use the provider’s configured ciphersuites. You must additionally choose
+which protocol versions to enable, using with_protocol_versions
or
+with_safe_default_protocol_versions
and handling the Result
in case a protocol
+version is not supported by the provider’s ciphersuites.
For more information, see the [ConfigBuilder
] documentation.
Create a builder for a client configuration with no default implementation details.
+This API must be used by no_std
users.
You must provide a specific [TimeProvider
].
You must provide a specific [CryptoProvider
].
This will use the provider’s configured ciphersuites. You must additionally choose
+which protocol versions to enable, using with_protocol_versions
or
+with_safe_default_protocol_versions
and handling the Result
in case a protocol
+version is not supported by the provider’s ciphersuites.
For more information, see the [ConfigBuilder
] documentation.
Return true if connections made with this ClientConfig
will
+operate in FIPS mode.
This is different from [CryptoProvider::fips()
]: [CryptoProvider::fips()
]
+is concerned only with cryptography, whereas this also covers TLS-level
+configuration that NIST recommends.
Return the crypto provider used to construct this client configuration.
+Access configuration options whose use is dangerous and requires +extra care.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnector { /* private fields */ }
Connector service factory using rustls
.
Constructs new connector service factory from a rustls
client configuration.
Constructs new connector service from a rustls
client configuration.
source
. Read moreService
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsConnectorService { /* private fields */ }
Connector service using rustls
.
source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ConnectInfo<R> { /* private fields */ }
Connection request information.
+May contain known/pre-resolved socket address(es) or a host that needs resolving with DNS.
+Constructs new connection info using a request.
+Constructs new connection info from request and known socket address.
+Since socket address is known, Connector
will skip the DNS
+resolution step.
Set connection port.
+If request provided a port, this will override it.
+Set connection socket address.
+Set list of addresses.
+Set local address to connection with.
+Useful in situations where the IP address bound to a particular network interface is known. +This would make sure the socket is opened through that interface.
+Get borrowed iterator of resolved request addresses.
+let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
+
+let conn = ConnectInfo::new("localhost");
+let mut addrs = conn.addrs();
+assert!(addrs.next().is_none());
+
+let conn = ConnectInfo::with_addr("localhost", addr);
+let mut addrs = conn.addrs();
+assert_eq!(addrs.next().unwrap(), addr);
Take owned iterator resolved request addresses.
+let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
+
+let mut conn = ConnectInfo::new("localhost");
+let mut addrs = conn.take_addrs();
+assert!(addrs.next().is_none());
+
+let mut conn = ConnectInfo::with_addr("localhost", addr);
+let mut addrs = conn.take_addrs();
+assert_eq!(addrs.next().unwrap(), addr);
self
and other
values to be equal, and is used
+by ==
.Service
created by this factory.Service
instance.gService
created by this factory.Service
instance.gService
created by this factory.Service
instance.gSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Connection<R, IO> { /* private fields */ }
Wraps underlying I/O and the connection request that initiated it.
+Deconstructs into IO and request parts.
+Replaces underlying IO, returning old IO and new Connection
.
The native-tls
connector is both it’s ServiceFactory and Service impl type.
+As the factory and service share the same type and state.
Service
created by this factory.Service
instance.gService
created by this factory.Service
instance.gService
created by this factory.Service
instance.gService
created by this factory.Service
instance.gService
created by this factory.Service
instance.gService
created by this factory.Service
instance.gSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Connector { /* private fields */ }
Combined resolver and TCP connector service factory.
+Used to create ConnectorService
s which receive connection information, resolve DNS if
+required, and return a TCP stream.
Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ConnectorService { /* private fields */ }
Combined resolver and TCP connector service.
+Service implementation receives connection information, resolves DNS if required, and returns +a TCP stream.
+source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Resolver { /* private fields */ }
DNS resolver service factory.
+Service
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ResolverService { /* private fields */ }
DNS resolver service.
+source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreTCP connector service.
+See TcpConnector
for main connector service factory docs.
#[non_exhaustive]pub struct TcpConnector;
TCP connector service factory.
+Returns a new TCP connector service.
+source
. Read moreService
created by this factory.Service
instance.gSelf
to a ServiceFactory
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read more#[non_exhaustive]pub struct TcpConnectorService;
TCP connector service.
+source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait Host: Unpin + 'static {
+ // Required method
+ fn hostname(&self) -> &str;
+
+ // Provided method
+ fn port(&self) -> Option<u16> { ... }
+}
An interface for types where host parts (hostname and port) can be derived.
+The WHATWG URL Standard defines the terminology used for this trait and its methods.
++------------------------+
+| host |
++-----------------+------+
+| hostname | port |
+| | |
+| sub.example.com : 8080 |
++-----------------+------+
+
pub trait Resolve {
+ // Required method
+ fn lookup<'a>(
+ &'a self,
+ host: &'a str,
+ port: u16
+ ) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn StdError>>>;
+}
Custom async DNS resolvers.
+use std::net::SocketAddr;
+
+use actix_tls::connect::{Resolve, Resolver};
+use futures_util::future::LocalBoxFuture;
+
+// use trust-dns async tokio resolver
+use trust_dns_resolver::TokioAsyncResolver;
+
+struct MyResolver {
+ trust_dns: TokioAsyncResolver,
+};
+
+// impl Resolve trait and convert given host address str and port to SocketAddr.
+impl Resolve for MyResolver {
+ fn lookup<'a>(
+ &'a self,
+ host: &'a str,
+ port: u16,
+ ) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>> {
+ Box::pin(async move {
+ let res = self
+ .trust_dns
+ .lookup_ip(host)
+ .await?
+ .iter()
+ .map(|ip| SocketAddr::new(ip, port))
+ .collect();
+ Ok(res)
+ })
+ }
+}
+
+let my_resolver = MyResolver {
+ trust_dns: TokioAsyncResolver::tokio_from_system_conf().unwrap(),
+};
+
+// wrap custom resolver
+let resolver = Resolver::custom(my_resolver);
+
+// resolver can be passed to connector factory where returned service factory
+// can be used to construct new connector services for use in clients
+let factory = actix_tls::connect::Connector::new(resolver);
+let connector = factory.service();
pub fn trace<S, Req, I, F>(
+ service_factory: I,
+ make_span: F
+) -> ApplyTransform<TracingTransform<S::Service, S, F>, S, Req>
Wraps the provided service factory with a transform that automatically +enters/exits the given span.
+The span to be entered/exited can be provided via a closure. The closure +is passed in a reference to the request being handled by the service.
+For example:
+ +let traced_service = trace(
+ web_service,
+ |req: &Request| Some(span!(Level::INFO, "request", req.id))
+);
Actix tracing - support for tokio tracing with Actix services.
+Service
implementation that automatically enters/exits tracing spans
+for the wrapped inner service.Transform
implementation that wraps services with a TracingService
.pub struct TracingService<S, F> { /* private fields */ }
A Service
implementation that automatically enters/exits tracing spans
+for the wrapped inner service.
source
. Read moreService
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TracingTransform<S, U, F> { /* private fields */ }
A Transform
implementation that wraps services with a TracingService
.
TransformService
value created by this factorySubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreTask-notifying counter.
+pub struct Counter(/* private fields */);
Simple counter with ability to notify task on reaching specific number
+Counter could be cloned, total n-count is shared across all clones.
+pub struct CounterGuard(/* private fields */);
An RAII structure that keeps the underlying counter incremented until this guard is dropped.
+Redirecting to ../../../actix_utils/future/enum.Either.html...
+ + + \ No newline at end of file diff --git a/actix_utils/future/enum.Either.html b/actix_utils/future/enum.Either.html new file mode 100644 index 0000000000..01bb7a79e1 --- /dev/null +++ b/actix_utils/future/enum.Either.html @@ -0,0 +1,49 @@ +pub enum Either<L, R> {
+ Left {
+ value: L,
+ },
+ Right {
+ value: R,
+ },
+}
Combines two different futures that have the same output type.
+Construct variants with Either::left
and Either::right
.
use actix_utils::future::{ready, Ready, Either};
+
+let res = Either::<_, Ready<usize>>::left(ready(42));
+assert_eq!(res.await, 42);
+
+let res = Either::<Ready<usize>, _>::right(ready(43));
+assert_eq!(res.await, 43);
Unwraps into inner value when left and right have a common type.
+pub fn poll_fn<F, T>(f: F) -> PollFn<F> ⓘ
Creates a future driven by the provided function that receives a task context.
+let res = poll_fn(|_| Poll::Ready(42)).await;
+assert_eq!(res, 42);
+
+let mut i = 5;
+let res = poll_fn(|cx| {
+ i -= 1;
+
+ if i > 0 {
+ cx.waker().wake_by_ref();
+ Poll::Pending
+ } else {
+ Poll::Ready(42)
+ }
+})
+.await;
+assert_eq!(res, 42);
pub fn ready<T>(val: T) -> Ready<T> ⓘ
Creates a future that is immediately ready with a value.
+use actix_utils::future::ready;
+
+let a = ready(1);
+assert_eq!(a.await, 1);
+
+// sync
+let a = ready(1);
+assert_eq!(a.into_inner(), 1);
Helpers for constructing futures.
+Redirecting to ../../../actix_utils/future/fn.poll_fn.html...
+ + + \ No newline at end of file diff --git a/actix_utils/future/poll_fn/struct.PollFn.html b/actix_utils/future/poll_fn/struct.PollFn.html new file mode 100644 index 0000000000..04123051b8 --- /dev/null +++ b/actix_utils/future/poll_fn/struct.PollFn.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_utils/future/struct.PollFn.html...
+ + + \ No newline at end of file diff --git a/actix_utils/future/ready/fn.err.html b/actix_utils/future/ready/fn.err.html new file mode 100644 index 0000000000..0a788ef236 --- /dev/null +++ b/actix_utils/future/ready/fn.err.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_utils/future/fn.err.html...
+ + + \ No newline at end of file diff --git a/actix_utils/future/ready/fn.ok.html b/actix_utils/future/ready/fn.ok.html new file mode 100644 index 0000000000..fa6909c764 --- /dev/null +++ b/actix_utils/future/ready/fn.ok.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_utils/future/fn.ok.html...
+ + + \ No newline at end of file diff --git a/actix_utils/future/ready/fn.ready.html b/actix_utils/future/ready/fn.ready.html new file mode 100644 index 0000000000..1d4d8e94a2 --- /dev/null +++ b/actix_utils/future/ready/fn.ready.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_utils/future/fn.ready.html...
+ + + \ No newline at end of file diff --git a/actix_utils/future/ready/struct.Ready.html b/actix_utils/future/ready/struct.Ready.html new file mode 100644 index 0000000000..05abbb0261 --- /dev/null +++ b/actix_utils/future/ready/struct.Ready.html @@ -0,0 +1,11 @@ + + + + +Redirecting to ../../../actix_utils/future/struct.Ready.html...
+ + + \ No newline at end of file diff --git a/actix_utils/future/sidebar-items.js b/actix_utils/future/sidebar-items.js new file mode 100644 index 0000000000..7c52428907 --- /dev/null +++ b/actix_utils/future/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Either"],"fn":["err","ok","poll_fn","ready"],"struct":["PollFn","Ready"]}; \ No newline at end of file diff --git a/actix_utils/future/struct.PollFn.html b/actix_utils/future/struct.PollFn.html new file mode 100644 index 0000000000..7a9cc1445f --- /dev/null +++ b/actix_utils/future/struct.PollFn.html @@ -0,0 +1,21 @@ +pub struct PollFn<F> { /* private fields */ }
Future for the poll_fn
function.
pub struct Ready<T> { /* private fields */ }
Unwraps the value from this immediately ready future.
+Various utilities used in the Actix ecosystem.
+A UTF-8 encoded read-only string using Bytes
as storage.
See docs for ByteString
.
Bytes
] as a storage.pub struct ByteString(/* private fields */);
An immutable UTF-8 encoded string with [Bytes
] as a storage.
Unwraps this ByteString
into the underlying Bytes
object.
Creates a new ByteString
from a &'static str
.
Creates a new ByteString
from a Bytes.
This function is unsafe because it does not check the bytes passed to it are valid UTF-8.
+If this constraint is violated, it may cause memory unsafety issues with future users of
+the ByteString
, as we assume that ByteString
s are valid UTF-8. However, the most likely
+issue is that the data gets corrupted.
Returns a new byte string that is equivalent to the given subset
.
When processing a ByteString
buffer with other tools, one often gets a &str
which is in
+fact a slice of the original ByteString
; i.e., a subset of it. This function turns that
+&str
into another ByteString
, as if one had sliced the ByteString
with the offsets
+that correspond to subset
.
Corresponds to [Bytes::slice_ref
].
This operation is O(1)
.
Panics if subset
is not a sub-slice of this byte string.
Note that strings which are only subsets from an equality perspective do not uphold this +requirement; see examples.
+let string = ByteString::from_static(" foo ");
+let subset = string.trim();
+let substring = string.slice_ref(subset);
+assert_eq!(substring, "foo");
// panics because the given slice is not derived from the original byte string, despite
+// being a logical subset of the string
+ByteString::from_static("foo bar").slice_ref("foo");
Returns true
if self
has a length of zero bytes.
let s = "";
+assert!(s.is_empty());
+
+let s = "not empty";
+assert!(!s.is_empty());
Checks that index
-th byte is the first byte in a UTF-8 code point
+sequence or the end of the string.
The start and end of the string (when index == self.len()
) are
+considered to be boundaries.
Returns false
if index
is greater than self.len()
.
let s = "Löwe 老虎 Léopard";
+assert!(s.is_char_boundary(0));
+// start of `老`
+assert!(s.is_char_boundary(6));
+assert!(s.is_char_boundary(s.len()));
+
+// second byte of `ö`
+assert!(!s.is_char_boundary(2));
+
+// third byte of `老`
+assert!(!s.is_char_boundary(8));
round_char_boundary
)Finds the closest x
not exceeding index
where is_char_boundary(x)
is true
.
This method can help you truncate a string so that it’s still valid UTF-8, but doesn’t +exceed a given number of bytes. Note that this is done purely at the character level +and can still visually split graphemes, even though the underlying characters aren’t +split. For example, the emoji 🧑🔬 (scientist) could be split so that the string only +includes 🧑 (person) instead.
+#![feature(round_char_boundary)]
+let s = "❤️🧡💛💚💙💜";
+assert_eq!(s.len(), 26);
+assert!(!s.is_char_boundary(13));
+
+let closest = s.floor_char_boundary(13);
+assert_eq!(closest, 10);
+assert_eq!(&s[..closest], "❤️🧡");
round_char_boundary
)Finds the closest x
not below index
where is_char_boundary(x)
is true
.
If index
is greater than the length of the string, this returns the length of the string.
This method is the natural complement to floor_char_boundary
. See that method
+for more details.
#![feature(round_char_boundary)]
+let s = "❤️🧡💛💚💙💜";
+assert_eq!(s.len(), 26);
+assert!(!s.is_char_boundary(13));
+
+let closest = s.ceil_char_boundary(13);
+assert_eq!(closest, 14);
+assert_eq!(&s[..closest], "❤️🧡💛");
Converts a string slice to a raw pointer.
+As string slices are a slice of bytes, the raw pointer points to a
+u8
. This pointer will be pointing to the first byte of the string
+slice.
The caller must ensure that the returned pointer is never written to.
+If you need to mutate the contents of the string slice, use as_mut_ptr
.
let s = "Hello";
+let ptr = s.as_ptr();
Returns a subslice of str
.
This is the non-panicking alternative to indexing the str
. Returns
+None
whenever equivalent indexing operation would panic.
let v = String::from("🗻∈🌏");
+
+assert_eq!(Some("🗻"), v.get(0..4));
+
+// indices not on UTF-8 sequence boundaries
+assert!(v.get(1..).is_none());
+assert!(v.get(..8).is_none());
+
+// out of bounds
+assert!(v.get(..42).is_none());
Returns an unchecked subslice of str
.
This is the unchecked alternative to indexing the str
.
Callers of this function are responsible that these preconditions are +satisfied:
+Failing that, the returned string slice may reference invalid memory or
+violate the invariants communicated by the str
type.
let v = "🗻∈🌏";
+unsafe {
+ assert_eq!("🗻", v.get_unchecked(0..4));
+ assert_eq!("∈", v.get_unchecked(4..7));
+ assert_eq!("🌏", v.get_unchecked(7..11));
+}
get_unchecked(begin..end)
insteadCreates a string slice from another string slice, bypassing safety +checks.
+This is generally not recommended, use with caution! For a safe
+alternative see str
and Index
.
This new slice goes from begin
to end
, including begin
but
+excluding end
.
To get a mutable string slice instead, see the
+slice_mut_unchecked
method.
Callers of this function are responsible that three preconditions are +satisfied:
+begin
must not exceed end
.begin
and end
must be byte positions within the string slice.begin
and end
must lie on UTF-8 sequence boundaries.let s = "Löwe 老虎 Léopard";
+
+unsafe {
+ assert_eq!("Löwe 老虎 Léopard", s.slice_unchecked(0, 21));
+}
+
+let s = "Hello, world!";
+
+unsafe {
+ assert_eq!("world", s.slice_unchecked(7, 12));
+}
Divide one string slice into two at an index.
+The argument, mid
, should be a byte offset from the start of the
+string. It must also be on the boundary of a UTF-8 code point.
The two slices returned go from the start of the string slice to mid
,
+and from mid
to the end of the string slice.
To get mutable string slices instead, see the split_at_mut
+method.
Panics if mid
is not on a UTF-8 code point boundary, or if it is past
+the end of the last code point of the string slice. For a non-panicking
+alternative see split_at_checked
.
let s = "Per Martin-Löf";
+
+let (first, last) = s.split_at(3);
+
+assert_eq!("Per", first);
+assert_eq!(" Martin-Löf", last);
Divide one string slice into two at an index.
+The argument, mid
, should be a valid byte offset from the start of the
+string. It must also be on the boundary of a UTF-8 code point. The
+method returns None
if that’s not the case.
The two slices returned go from the start of the string slice to mid
,
+and from mid
to the end of the string slice.
To get mutable string slices instead, see the split_at_mut_checked
+method.
let s = "Per Martin-Löf";
+
+let (first, last) = s.split_at_checked(3).unwrap();
+assert_eq!("Per", first);
+assert_eq!(" Martin-Löf", last);
+
+assert_eq!(None, s.split_at_checked(13)); // Inside “ö”
+assert_eq!(None, s.split_at_checked(16)); // Beyond the string length
Returns an iterator over the char
s of a string slice.
As a string slice consists of valid UTF-8, we can iterate through a
+string slice by char
. This method returns such an iterator.
It’s important to remember that char
represents a Unicode Scalar
+Value, and might not match your idea of what a ‘character’ is. Iteration
+over grapheme clusters may be what you actually want. This functionality
+is not provided by Rust’s standard library, check crates.io instead.
Basic usage:
+ +let word = "goodbye";
+
+let count = word.chars().count();
+assert_eq!(7, count);
+
+let mut chars = word.chars();
+
+assert_eq!(Some('g'), chars.next());
+assert_eq!(Some('o'), chars.next());
+assert_eq!(Some('o'), chars.next());
+assert_eq!(Some('d'), chars.next());
+assert_eq!(Some('b'), chars.next());
+assert_eq!(Some('y'), chars.next());
+assert_eq!(Some('e'), chars.next());
+
+assert_eq!(None, chars.next());
Remember, char
s might not match your intuition about characters:
let y = "y̆";
+
+let mut chars = y.chars();
+
+assert_eq!(Some('y'), chars.next()); // not 'y̆'
+assert_eq!(Some('\u{0306}'), chars.next());
+
+assert_eq!(None, chars.next());
Returns an iterator over the char
s of a string slice, and their
+positions.
As a string slice consists of valid UTF-8, we can iterate through a
+string slice by char
. This method returns an iterator of both
+these char
s, as well as their byte positions.
The iterator yields tuples. The position is first, the char
is
+second.
Basic usage:
+ +let word = "goodbye";
+
+let count = word.char_indices().count();
+assert_eq!(7, count);
+
+let mut char_indices = word.char_indices();
+
+assert_eq!(Some((0, 'g')), char_indices.next());
+assert_eq!(Some((1, 'o')), char_indices.next());
+assert_eq!(Some((2, 'o')), char_indices.next());
+assert_eq!(Some((3, 'd')), char_indices.next());
+assert_eq!(Some((4, 'b')), char_indices.next());
+assert_eq!(Some((5, 'y')), char_indices.next());
+assert_eq!(Some((6, 'e')), char_indices.next());
+
+assert_eq!(None, char_indices.next());
Remember, char
s might not match your intuition about characters:
let yes = "y̆es";
+
+let mut char_indices = yes.char_indices();
+
+assert_eq!(Some((0, 'y')), char_indices.next()); // not (0, 'y̆')
+assert_eq!(Some((1, '\u{0306}')), char_indices.next());
+
+// note the 3 here - the previous character took up two bytes
+assert_eq!(Some((3, 'e')), char_indices.next());
+assert_eq!(Some((4, 's')), char_indices.next());
+
+assert_eq!(None, char_indices.next());
An iterator over the bytes of a string slice.
+As a string slice consists of a sequence of bytes, we can iterate +through a string slice by byte. This method returns such an iterator.
+let mut bytes = "bors".bytes();
+
+assert_eq!(Some(b'b'), bytes.next());
+assert_eq!(Some(b'o'), bytes.next());
+assert_eq!(Some(b'r'), bytes.next());
+assert_eq!(Some(b's'), bytes.next());
+
+assert_eq!(None, bytes.next());
Splits a string slice by whitespace.
+The iterator returned will return string slices that are sub-slices of +the original string slice, separated by any amount of whitespace.
+‘Whitespace’ is defined according to the terms of the Unicode Derived
+Core Property White_Space
. If you only want to split on ASCII whitespace
+instead, use split_ascii_whitespace
.
Basic usage:
+ +let mut iter = "A few words".split_whitespace();
+
+assert_eq!(Some("A"), iter.next());
+assert_eq!(Some("few"), iter.next());
+assert_eq!(Some("words"), iter.next());
+
+assert_eq!(None, iter.next());
All kinds of whitespace are considered:
+ +let mut iter = " Mary had\ta\u{2009}little \n\t lamb".split_whitespace();
+assert_eq!(Some("Mary"), iter.next());
+assert_eq!(Some("had"), iter.next());
+assert_eq!(Some("a"), iter.next());
+assert_eq!(Some("little"), iter.next());
+assert_eq!(Some("lamb"), iter.next());
+
+assert_eq!(None, iter.next());
If the string is empty or all whitespace, the iterator yields no string slices:
+ +assert_eq!("".split_whitespace().next(), None);
+assert_eq!(" ".split_whitespace().next(), None);
Splits a string slice by ASCII whitespace.
+The iterator returned will return string slices that are sub-slices of +the original string slice, separated by any amount of ASCII whitespace.
+To split by Unicode Whitespace
instead, use split_whitespace
.
Basic usage:
+ +let mut iter = "A few words".split_ascii_whitespace();
+
+assert_eq!(Some("A"), iter.next());
+assert_eq!(Some("few"), iter.next());
+assert_eq!(Some("words"), iter.next());
+
+assert_eq!(None, iter.next());
All kinds of ASCII whitespace are considered:
+ +let mut iter = " Mary had\ta little \n\t lamb".split_ascii_whitespace();
+assert_eq!(Some("Mary"), iter.next());
+assert_eq!(Some("had"), iter.next());
+assert_eq!(Some("a"), iter.next());
+assert_eq!(Some("little"), iter.next());
+assert_eq!(Some("lamb"), iter.next());
+
+assert_eq!(None, iter.next());
If the string is empty or all ASCII whitespace, the iterator yields no string slices:
+ +assert_eq!("".split_ascii_whitespace().next(), None);
+assert_eq!(" ".split_ascii_whitespace().next(), None);
An iterator over the lines of a string, as string slices.
+Lines are split at line endings that are either newlines (\n
) or
+sequences of a carriage return followed by a line feed (\r\n
).
Line terminators are not included in the lines returned by the iterator.
+Note that any carriage return (\r
) not immediately followed by a
+line feed (\n
) does not split a line. These carriage returns are
+thereby included in the produced lines.
The final line ending is optional. A string that ends with a final line +ending will return the same lines as an otherwise identical string +without a final line ending.
+Basic usage:
+ +let text = "foo\r\nbar\n\nbaz\r";
+let mut lines = text.lines();
+
+assert_eq!(Some("foo"), lines.next());
+assert_eq!(Some("bar"), lines.next());
+assert_eq!(Some(""), lines.next());
+// Trailing carriage return is included in the last line
+assert_eq!(Some("baz\r"), lines.next());
+
+assert_eq!(None, lines.next());
The final line does not require any ending:
+ +let text = "foo\nbar\n\r\nbaz";
+let mut lines = text.lines();
+
+assert_eq!(Some("foo"), lines.next());
+assert_eq!(Some("bar"), lines.next());
+assert_eq!(Some(""), lines.next());
+assert_eq!(Some("baz"), lines.next());
+
+assert_eq!(None, lines.next());
An iterator over the lines of a string.
+Returns an iterator of u16
over the string encoded as UTF-16.
let text = "Zażółć gęślą jaźń";
+
+let utf8_len = text.len();
+let utf16_len = text.encode_utf16().count();
+
+assert!(utf16_len <= utf8_len);
Returns true
if the given pattern matches a sub-slice of
+this string slice.
Returns false
if it does not.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
let bananas = "bananas";
+
+assert!(bananas.contains("nana"));
+assert!(!bananas.contains("apples"));
Returns true
if the given pattern matches a prefix of this
+string slice.
Returns false
if it does not.
The pattern can be a &str
, in which case this function will return true if
+the &str
is a prefix of this string slice.
The pattern can also be a char
, a slice of char
s, or a
+function or closure that determines if a character matches.
+These will only be checked against the first character of this string slice.
+Look at the second example below regarding behavior for slices of char
s.
let bananas = "bananas";
+
+assert!(bananas.starts_with("bana"));
+assert!(!bananas.starts_with("nana"));
let bananas = "bananas";
+
+// Note that both of these assert successfully.
+assert!(bananas.starts_with(&['b', 'a', 'n', 'a']));
+assert!(bananas.starts_with(&['a', 'b', 'c', 'd']));
Returns true
if the given pattern matches a suffix of this
+string slice.
Returns false
if it does not.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
let bananas = "bananas";
+
+assert!(bananas.ends_with("anas"));
+assert!(!bananas.ends_with("nana"));
Returns the byte index of the first character of this string slice that +matches the pattern.
+Returns None
if the pattern doesn’t match.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
Simple patterns:
+ +let s = "Löwe 老虎 Léopard Gepardi";
+
+assert_eq!(s.find('L'), Some(0));
+assert_eq!(s.find('é'), Some(14));
+assert_eq!(s.find("pard"), Some(17));
More complex patterns using point-free style and closures:
+ +let s = "Löwe 老虎 Léopard";
+
+assert_eq!(s.find(char::is_whitespace), Some(5));
+assert_eq!(s.find(char::is_lowercase), Some(1));
+assert_eq!(s.find(|c: char| c.is_whitespace() || c.is_lowercase()), Some(1));
+assert_eq!(s.find(|c: char| (c < 'o') && (c > 'a')), Some(4));
Not finding the pattern:
+ +let s = "Löwe 老虎 Léopard";
+let x: &[_] = &['1', '2'];
+
+assert_eq!(s.find(x), None);
Returns the byte index for the first character of the last match of the pattern in +this string slice.
+Returns None
if the pattern doesn’t match.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
Simple patterns:
+ +let s = "Löwe 老虎 Léopard Gepardi";
+
+assert_eq!(s.rfind('L'), Some(13));
+assert_eq!(s.rfind('é'), Some(14));
+assert_eq!(s.rfind("pard"), Some(24));
More complex patterns with closures:
+ +let s = "Löwe 老虎 Léopard";
+
+assert_eq!(s.rfind(char::is_whitespace), Some(12));
+assert_eq!(s.rfind(char::is_lowercase), Some(20));
Not finding the pattern:
+ +let s = "Löwe 老虎 Léopard";
+let x: &[_] = &['1', '2'];
+
+assert_eq!(s.rfind(x), None);
An iterator over substrings of this string slice, separated by +characters matched by a pattern.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator will be a DoubleEndedIterator
if the pattern
+allows a reverse search and forward/reverse search yields the same
+elements. This is true for, e.g., char
, but not for &str
.
If the pattern allows a reverse search but its results might differ
+from a forward search, the rsplit
method can be used.
Simple patterns:
+ +let v: Vec<&str> = "Mary had a little lamb".split(' ').collect();
+assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]);
+
+let v: Vec<&str> = "".split('X').collect();
+assert_eq!(v, [""]);
+
+let v: Vec<&str> = "lionXXtigerXleopard".split('X').collect();
+assert_eq!(v, ["lion", "", "tiger", "leopard"]);
+
+let v: Vec<&str> = "lion::tiger::leopard".split("::").collect();
+assert_eq!(v, ["lion", "tiger", "leopard"]);
+
+let v: Vec<&str> = "abc1def2ghi".split(char::is_numeric).collect();
+assert_eq!(v, ["abc", "def", "ghi"]);
+
+let v: Vec<&str> = "lionXtigerXleopard".split(char::is_uppercase).collect();
+assert_eq!(v, ["lion", "tiger", "leopard"]);
If the pattern is a slice of chars, split on each occurrence of any of the characters:
+ +let v: Vec<&str> = "2020-11-03 23:59".split(&['-', ' ', ':', '@'][..]).collect();
+assert_eq!(v, ["2020", "11", "03", "23", "59"]);
A more complex pattern, using a closure:
+ +let v: Vec<&str> = "abc1defXghi".split(|c| c == '1' || c == 'X').collect();
+assert_eq!(v, ["abc", "def", "ghi"]);
If a string contains multiple contiguous separators, you will end up +with empty strings in the output:
+ +let x = "||||a||b|c".to_string();
+let d: Vec<_> = x.split('|').collect();
+
+assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]);
Contiguous separators are separated by the empty string.
+ +let x = "(///)".to_string();
+let d: Vec<_> = x.split('/').collect();
+
+assert_eq!(d, &["(", "", "", ")"]);
Separators at the start or end of a string are neighbored +by empty strings.
+ +let d: Vec<_> = "010".split("0").collect();
+assert_eq!(d, &["", "1", ""]);
When the empty string is used as a separator, it separates +every character in the string, along with the beginning +and end of the string.
+ +let f: Vec<_> = "rust".split("").collect();
+assert_eq!(f, &["", "r", "u", "s", "t", ""]);
Contiguous separators can lead to possibly surprising behavior +when whitespace is used as the separator. This code is correct:
+ +let x = " a b c".to_string();
+let d: Vec<_> = x.split(' ').collect();
+
+assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]);
It does not give you:
+ +assert_eq!(d, &["a", "b", "c"]);
Use split_whitespace
for this behavior.
An iterator over substrings of this string slice, separated by
+characters matched by a pattern. Differs from the iterator produced by
+split
in that split_inclusive
leaves the matched part as the
+terminator of the substring.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb."
+ .split_inclusive('\n').collect();
+assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb."]);
If the last element of the string is matched, +that element will be considered the terminator of the preceding substring. +That substring will be the last item returned by the iterator.
+ +let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb.\n"
+ .split_inclusive('\n').collect();
+assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb.\n"]);
An iterator over substrings of the given string slice, separated by +characters matched by a pattern and yielded in reverse order.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator requires that the pattern supports a reverse
+search, and it will be a DoubleEndedIterator
if a forward/reverse
+search yields the same elements.
For iterating from the front, the split
method can be used.
Simple patterns:
+ +let v: Vec<&str> = "Mary had a little lamb".rsplit(' ').collect();
+assert_eq!(v, ["lamb", "little", "a", "had", "Mary"]);
+
+let v: Vec<&str> = "".rsplit('X').collect();
+assert_eq!(v, [""]);
+
+let v: Vec<&str> = "lionXXtigerXleopard".rsplit('X').collect();
+assert_eq!(v, ["leopard", "tiger", "", "lion"]);
+
+let v: Vec<&str> = "lion::tiger::leopard".rsplit("::").collect();
+assert_eq!(v, ["leopard", "tiger", "lion"]);
A more complex pattern, using a closure:
+ +let v: Vec<&str> = "abc1defXghi".rsplit(|c| c == '1' || c == 'X').collect();
+assert_eq!(v, ["ghi", "def", "abc"]);
An iterator over substrings of the given string slice, separated by +characters matched by a pattern.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
Equivalent to split
, except that the trailing substring
+is skipped if empty.
This method can be used for string data that is terminated, +rather than separated by a pattern.
+The returned iterator will be a DoubleEndedIterator
if the pattern
+allows a reverse search and forward/reverse search yields the same
+elements. This is true for, e.g., char
, but not for &str
.
If the pattern allows a reverse search but its results might differ
+from a forward search, the rsplit_terminator
method can be used.
let v: Vec<&str> = "A.B.".split_terminator('.').collect();
+assert_eq!(v, ["A", "B"]);
+
+let v: Vec<&str> = "A..B..".split_terminator(".").collect();
+assert_eq!(v, ["A", "", "B", ""]);
+
+let v: Vec<&str> = "A.B:C.D".split_terminator(&['.', ':'][..]).collect();
+assert_eq!(v, ["A", "B", "C", "D"]);
An iterator over substrings of self
, separated by characters
+matched by a pattern and yielded in reverse order.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
Equivalent to split
, except that the trailing substring is
+skipped if empty.
This method can be used for string data that is terminated, +rather than separated by a pattern.
+The returned iterator requires that the pattern supports a +reverse search, and it will be double ended if a forward/reverse +search yields the same elements.
+For iterating from the front, the split_terminator
method can be
+used.
let v: Vec<&str> = "A.B.".rsplit_terminator('.').collect();
+assert_eq!(v, ["B", "A"]);
+
+let v: Vec<&str> = "A..B..".rsplit_terminator(".").collect();
+assert_eq!(v, ["", "B", "", "A"]);
+
+let v: Vec<&str> = "A.B:C.D".rsplit_terminator(&['.', ':'][..]).collect();
+assert_eq!(v, ["D", "C", "B", "A"]);
An iterator over substrings of the given string slice, separated by a
+pattern, restricted to returning at most n
items.
If n
substrings are returned, the last substring (the n
th substring)
+will contain the remainder of the string.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator will not be double ended, because it is +not efficient to support.
+If the pattern allows a reverse search, the rsplitn
method can be
+used.
Simple patterns:
+ +let v: Vec<&str> = "Mary had a little lambda".splitn(3, ' ').collect();
+assert_eq!(v, ["Mary", "had", "a little lambda"]);
+
+let v: Vec<&str> = "lionXXtigerXleopard".splitn(3, "X").collect();
+assert_eq!(v, ["lion", "", "tigerXleopard"]);
+
+let v: Vec<&str> = "abcXdef".splitn(1, 'X').collect();
+assert_eq!(v, ["abcXdef"]);
+
+let v: Vec<&str> = "".splitn(1, 'X').collect();
+assert_eq!(v, [""]);
A more complex pattern, using a closure:
+ +let v: Vec<&str> = "abc1defXghi".splitn(2, |c| c == '1' || c == 'X').collect();
+assert_eq!(v, ["abc", "defXghi"]);
An iterator over substrings of this string slice, separated by a
+pattern, starting from the end of the string, restricted to returning
+at most n
items.
If n
substrings are returned, the last substring (the n
th substring)
+will contain the remainder of the string.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator will not be double ended, because it is not +efficient to support.
+For splitting from the front, the splitn
method can be used.
Simple patterns:
+ +let v: Vec<&str> = "Mary had a little lamb".rsplitn(3, ' ').collect();
+assert_eq!(v, ["lamb", "little", "Mary had a"]);
+
+let v: Vec<&str> = "lionXXtigerXleopard".rsplitn(3, 'X').collect();
+assert_eq!(v, ["leopard", "tiger", "lionX"]);
+
+let v: Vec<&str> = "lion::tiger::leopard".rsplitn(2, "::").collect();
+assert_eq!(v, ["leopard", "lion::tiger"]);
A more complex pattern, using a closure:
+ +let v: Vec<&str> = "abc1defXghi".rsplitn(2, |c| c == '1' || c == 'X').collect();
+assert_eq!(v, ["ghi", "abc1def"]);
Splits the string on the first occurrence of the specified delimiter and +returns prefix before delimiter and suffix after delimiter.
+assert_eq!("cfg".split_once('='), None);
+assert_eq!("cfg=".split_once('='), Some(("cfg", "")));
+assert_eq!("cfg=foo".split_once('='), Some(("cfg", "foo")));
+assert_eq!("cfg=foo=bar".split_once('='), Some(("cfg", "foo=bar")));
Splits the string on the last occurrence of the specified delimiter and +returns prefix before delimiter and suffix after delimiter.
+assert_eq!("cfg".rsplit_once('='), None);
+assert_eq!("cfg=foo".rsplit_once('='), Some(("cfg", "foo")));
+assert_eq!("cfg=foo=bar".rsplit_once('='), Some(("cfg=foo", "bar")));
An iterator over the disjoint matches of a pattern within the given string +slice.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator will be a DoubleEndedIterator
if the pattern
+allows a reverse search and forward/reverse search yields the same
+elements. This is true for, e.g., char
, but not for &str
.
If the pattern allows a reverse search but its results might differ
+from a forward search, the rmatches
method can be used.
let v: Vec<&str> = "abcXXXabcYYYabc".matches("abc").collect();
+assert_eq!(v, ["abc", "abc", "abc"]);
+
+let v: Vec<&str> = "1abc2abc3".matches(char::is_numeric).collect();
+assert_eq!(v, ["1", "2", "3"]);
An iterator over the disjoint matches of a pattern within this string slice, +yielded in reverse order.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator requires that the pattern supports a reverse
+search, and it will be a DoubleEndedIterator
if a forward/reverse
+search yields the same elements.
For iterating from the front, the matches
method can be used.
let v: Vec<&str> = "abcXXXabcYYYabc".rmatches("abc").collect();
+assert_eq!(v, ["abc", "abc", "abc"]);
+
+let v: Vec<&str> = "1abc2abc3".rmatches(char::is_numeric).collect();
+assert_eq!(v, ["3", "2", "1"]);
An iterator over the disjoint matches of a pattern within this string +slice as well as the index that the match starts at.
+For matches of pat
within self
that overlap, only the indices
+corresponding to the first match are returned.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator will be a DoubleEndedIterator
if the pattern
+allows a reverse search and forward/reverse search yields the same
+elements. This is true for, e.g., char
, but not for &str
.
If the pattern allows a reverse search but its results might differ
+from a forward search, the rmatch_indices
method can be used.
let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect();
+assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]);
+
+let v: Vec<_> = "1abcabc2".match_indices("abc").collect();
+assert_eq!(v, [(1, "abc"), (4, "abc")]);
+
+let v: Vec<_> = "ababa".match_indices("aba").collect();
+assert_eq!(v, [(0, "aba")]); // only the first `aba`
An iterator over the disjoint matches of a pattern within self
,
+yielded in reverse order along with the index of the match.
For matches of pat
within self
that overlap, only the indices
+corresponding to the last match are returned.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
The returned iterator requires that the pattern supports a reverse
+search, and it will be a DoubleEndedIterator
if a forward/reverse
+search yields the same elements.
For iterating from the front, the match_indices
method can be used.
let v: Vec<_> = "abcXXXabcYYYabc".rmatch_indices("abc").collect();
+assert_eq!(v, [(12, "abc"), (6, "abc"), (0, "abc")]);
+
+let v: Vec<_> = "1abcabc2".rmatch_indices("abc").collect();
+assert_eq!(v, [(4, "abc"), (1, "abc")]);
+
+let v: Vec<_> = "ababa".rmatch_indices("aba").collect();
+assert_eq!(v, [(2, "aba")]); // only the last `aba`
Returns a string slice with leading and trailing whitespace removed.
+‘Whitespace’ is defined according to the terms of the Unicode Derived
+Core Property White_Space
, which includes newlines.
let s = "\n Hello\tworld\t\n";
+
+assert_eq!("Hello\tworld", s.trim());
Returns a string slice with leading whitespace removed.
+‘Whitespace’ is defined according to the terms of the Unicode Derived
+Core Property White_Space
, which includes newlines.
A string is a sequence of bytes. start
in this context means the first
+position of that byte string; for a left-to-right language like English or
+Russian, this will be left side, and for right-to-left languages like
+Arabic or Hebrew, this will be the right side.
Basic usage:
+ +let s = "\n Hello\tworld\t\n";
+assert_eq!("Hello\tworld\t\n", s.trim_start());
Directionality:
+ +let s = " English ";
+assert!(Some('E') == s.trim_start().chars().next());
+
+let s = " עברית ";
+assert!(Some('ע') == s.trim_start().chars().next());
Returns a string slice with trailing whitespace removed.
+‘Whitespace’ is defined according to the terms of the Unicode Derived
+Core Property White_Space
, which includes newlines.
A string is a sequence of bytes. end
in this context means the last
+position of that byte string; for a left-to-right language like English or
+Russian, this will be right side, and for right-to-left languages like
+Arabic or Hebrew, this will be the left side.
Basic usage:
+ +let s = "\n Hello\tworld\t\n";
+assert_eq!("\n Hello\tworld", s.trim_end());
Directionality:
+ +let s = " English ";
+assert!(Some('h') == s.trim_end().chars().rev().next());
+
+let s = " עברית ";
+assert!(Some('ת') == s.trim_end().chars().rev().next());
trim_start
Returns a string slice with leading whitespace removed.
+‘Whitespace’ is defined according to the terms of the Unicode Derived
+Core Property White_Space
.
A string is a sequence of bytes. ‘Left’ in this context means the first +position of that byte string; for a language like Arabic or Hebrew +which are ‘right to left’ rather than ‘left to right’, this will be +the right side, not the left.
+Basic usage:
+ +let s = " Hello\tworld\t";
+
+assert_eq!("Hello\tworld\t", s.trim_left());
Directionality:
+ +let s = " English";
+assert!(Some('E') == s.trim_left().chars().next());
+
+let s = " עברית";
+assert!(Some('ע') == s.trim_left().chars().next());
trim_end
Returns a string slice with trailing whitespace removed.
+‘Whitespace’ is defined according to the terms of the Unicode Derived
+Core Property White_Space
.
A string is a sequence of bytes. ‘Right’ in this context means the last +position of that byte string; for a language like Arabic or Hebrew +which are ‘right to left’ rather than ‘left to right’, this will be +the left side, not the right.
+Basic usage:
+ +let s = " Hello\tworld\t";
+
+assert_eq!(" Hello\tworld", s.trim_right());
Directionality:
+ +let s = "English ";
+assert!(Some('h') == s.trim_right().chars().rev().next());
+
+let s = "עברית ";
+assert!(Some('ת') == s.trim_right().chars().rev().next());
Returns a string slice with all prefixes and suffixes that match a +pattern repeatedly removed.
+The pattern can be a char
, a slice of char
s, or a function
+or closure that determines if a character matches.
Simple patterns:
+ +assert_eq!("11foo1bar11".trim_matches('1'), "foo1bar");
+assert_eq!("123foo1bar123".trim_matches(char::is_numeric), "foo1bar");
+
+let x: &[_] = &['1', '2'];
+assert_eq!("12foo1bar12".trim_matches(x), "foo1bar");
A more complex pattern, using a closure:
+ +assert_eq!("1foo1barXX".trim_matches(|c| c == '1' || c == 'X'), "foo1bar");
Returns a string slice with all prefixes that match a pattern +repeatedly removed.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
A string is a sequence of bytes. start
in this context means the first
+position of that byte string; for a left-to-right language like English or
+Russian, this will be left side, and for right-to-left languages like
+Arabic or Hebrew, this will be the right side.
assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11");
+assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123");
+
+let x: &[_] = &['1', '2'];
+assert_eq!("12foo1bar12".trim_start_matches(x), "foo1bar12");
Returns a string slice with the prefix removed.
+If the string starts with the pattern prefix
, returns the substring after the prefix,
+wrapped in Some
. Unlike trim_start_matches
, this method removes the prefix exactly once.
If the string does not start with prefix
, returns None
.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
assert_eq!("foo:bar".strip_prefix("foo:"), Some("bar"));
+assert_eq!("foo:bar".strip_prefix("bar"), None);
+assert_eq!("foofoo".strip_prefix("foo"), Some("foo"));
Returns a string slice with the suffix removed.
+If the string ends with the pattern suffix
, returns the substring before the suffix,
+wrapped in Some
. Unlike trim_end_matches
, this method removes the suffix exactly once.
If the string does not end with suffix
, returns None
.
The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
assert_eq!("bar:foo".strip_suffix(":foo"), Some("bar"));
+assert_eq!("bar:foo".strip_suffix("bar"), None);
+assert_eq!("foofoo".strip_suffix("foo"), Some("foo"));
Returns a string slice with all suffixes that match a pattern +repeatedly removed.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
A string is a sequence of bytes. end
in this context means the last
+position of that byte string; for a left-to-right language like English or
+Russian, this will be right side, and for right-to-left languages like
+Arabic or Hebrew, this will be the left side.
Simple patterns:
+ +assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar");
+assert_eq!("123foo1bar123".trim_end_matches(char::is_numeric), "123foo1bar");
+
+let x: &[_] = &['1', '2'];
+assert_eq!("12foo1bar12".trim_end_matches(x), "12foo1bar");
A more complex pattern, using a closure:
+ +assert_eq!("1fooX".trim_end_matches(|c| c == '1' || c == 'X'), "1foo");
trim_start_matches
Returns a string slice with all prefixes that match a pattern +repeatedly removed.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
A string is a sequence of bytes. ‘Left’ in this context means the first +position of that byte string; for a language like Arabic or Hebrew +which are ‘right to left’ rather than ‘left to right’, this will be +the right side, not the left.
+assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11");
+assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123");
+
+let x: &[_] = &['1', '2'];
+assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12");
trim_end_matches
Returns a string slice with all suffixes that match a pattern +repeatedly removed.
+The pattern can be a &str
, char
, a slice of char
s, or a
+function or closure that determines if a character matches.
A string is a sequence of bytes. ‘Right’ in this context means the last +position of that byte string; for a language like Arabic or Hebrew +which are ‘right to left’ rather than ‘left to right’, this will be +the left side, not the right.
+Simple patterns:
+ +assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar");
+assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar");
+
+let x: &[_] = &['1', '2'];
+assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar");
A more complex pattern, using a closure:
+ +assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo");
Parses this string slice into another type.
+Because parse
is so general, it can cause problems with type
+inference. As such, parse
is one of the few times you’ll see
+the syntax affectionately known as the ‘turbofish’: ::<>
. This
+helps the inference algorithm understand specifically which type
+you’re trying to parse into.
parse
can parse into any type that implements the FromStr
trait.
Will return Err
if it’s not possible to parse this string slice into
+the desired type.
Basic usage
+ +let four: u32 = "4".parse().unwrap();
+
+assert_eq!(4, four);
Using the ‘turbofish’ instead of annotating four
:
let four = "4".parse::<u32>();
+
+assert_eq!(Ok(4), four);
Failing to parse:
+ +let nope = "j".parse::<u32>();
+
+assert!(nope.is_err());
Checks if all characters in this string are within the ASCII range.
+let ascii = "hello!\n";
+let non_ascii = "Grüße, Jürgen ❤";
+
+assert!(ascii.is_ascii());
+assert!(!non_ascii.is_ascii());
ascii_char
)If this string slice is_ascii
, returns it as a slice
+of ASCII characters, otherwise returns None
.
Checks that two strings are an ASCII case-insensitive match.
+Same as to_ascii_lowercase(a) == to_ascii_lowercase(b)
,
+but without allocating and copying temporaries.
assert!("Ferris".eq_ignore_ascii_case("FERRIS"));
+assert!("Ferrös".eq_ignore_ascii_case("FERRöS"));
+assert!(!"Ferrös".eq_ignore_ascii_case("FERRÖS"));
Returns a string slice with leading ASCII whitespace removed.
+‘Whitespace’ refers to the definition used by
+u8::is_ascii_whitespace
.
assert_eq!(" \t \u{3000}hello world\n".trim_ascii_start(), "\u{3000}hello world\n");
+assert_eq!(" ".trim_ascii_start(), "");
+assert_eq!("".trim_ascii_start(), "");
Returns a string slice with trailing ASCII whitespace removed.
+‘Whitespace’ refers to the definition used by
+u8::is_ascii_whitespace
.
assert_eq!("\r hello world\u{3000}\n ".trim_ascii_end(), "\r hello world\u{3000}");
+assert_eq!(" ".trim_ascii_end(), "");
+assert_eq!("".trim_ascii_end(), "");
Returns a string slice with leading and trailing ASCII whitespace +removed.
+‘Whitespace’ refers to the definition used by
+u8::is_ascii_whitespace
.
assert_eq!("\r hello world\n ".trim_ascii(), "hello world");
+assert_eq!(" ".trim_ascii(), "");
+assert_eq!("".trim_ascii(), "");
Return an iterator that escapes each char in self
with char::escape_debug
.
Note: only extended grapheme codepoints that begin the string will be +escaped.
+As an iterator:
+ +for c in "❤\n!".escape_debug() {
+ print!("{c}");
+}
+println!();
Using println!
directly:
println!("{}", "❤\n!".escape_debug());
Both are equivalent to:
+ +println!("❤\\n!");
Using to_string
:
assert_eq!("❤\n!".escape_debug().to_string(), "❤\\n!");
Return an iterator that escapes each char in self
with char::escape_default
.
As an iterator:
+ +for c in "❤\n!".escape_default() {
+ print!("{c}");
+}
+println!();
Using println!
directly:
println!("{}", "❤\n!".escape_default());
Both are equivalent to:
+ +println!("\\u{{2764}}\\n!");
Using to_string
:
assert_eq!("❤\n!".escape_default().to_string(), "\\u{2764}\\n!");
Return an iterator that escapes each char in self
with char::escape_unicode
.
As an iterator:
+ +for c in "❤\n!".escape_unicode() {
+ print!("{c}");
+}
+println!();
Using println!
directly:
println!("{}", "❤\n!".escape_unicode());
Both are equivalent to:
+ +println!("\\u{{2764}}\\u{{a}}\\u{{21}}");
Using to_string
:
assert_eq!("❤\n!".escape_unicode().to_string(), "\\u{2764}\\u{a}\\u{21}");
Replaces all matches of a pattern with another string.
+replace
creates a new String
, and copies the data from this string slice into it.
+While doing so, it attempts to find matches of a pattern. If it finds any, it
+replaces them with the replacement string slice.
Basic usage:
+ +let s = "this is old";
+
+assert_eq!("this is new", s.replace("old", "new"));
+assert_eq!("than an old", s.replace("is", "an"));
When the pattern doesn’t match, it returns this string slice as String
:
let s = "this is old";
+assert_eq!(s, s.replace("cookie monster", "little lamb"));
Replaces first N matches of a pattern with another string.
+replacen
creates a new String
, and copies the data from this string slice into it.
+While doing so, it attempts to find matches of a pattern. If it finds any, it
+replaces them with the replacement string slice at most count
times.
Basic usage:
+ +let s = "foo foo 123 foo";
+assert_eq!("new new 123 foo", s.replacen("foo", "new", 2));
+assert_eq!("faa fao 123 foo", s.replacen('o', "a", 3));
+assert_eq!("foo foo new23 foo", s.replacen(char::is_numeric, "new", 1));
When the pattern doesn’t match, it returns this string slice as String
:
let s = "this is old";
+assert_eq!(s, s.replacen("cookie monster", "little lamb", 10));
Returns the lowercase equivalent of this string slice, as a new String
.
‘Lowercase’ is defined according to the terms of the Unicode Derived Core Property
+Lowercase
.
Since some characters can expand into multiple characters when changing
+the case, this function returns a String
instead of modifying the
+parameter in-place.
Basic usage:
+ +let s = "HELLO";
+
+assert_eq!("hello", s.to_lowercase());
A tricky example, with sigma:
+ +let sigma = "Σ";
+
+assert_eq!("σ", sigma.to_lowercase());
+
+// but at the end of a word, it's ς, not σ:
+let odysseus = "ὈΔΥΣΣΕΎΣ";
+
+assert_eq!("ὀδυσσεύς", odysseus.to_lowercase());
Languages without case are not changed:
+ +let new_year = "农历新年";
+
+assert_eq!(new_year, new_year.to_lowercase());
Returns the uppercase equivalent of this string slice, as a new String
.
‘Uppercase’ is defined according to the terms of the Unicode Derived Core Property
+Uppercase
.
Since some characters can expand into multiple characters when changing
+the case, this function returns a String
instead of modifying the
+parameter in-place.
Basic usage:
+ +let s = "hello";
+
+assert_eq!("HELLO", s.to_uppercase());
Scripts without case are not changed:
+ +let new_year = "农历新年";
+
+assert_eq!(new_year, new_year.to_uppercase());
One character can become multiple:
+ +let s = "tschüß";
+
+assert_eq!("TSCHÜSS", s.to_uppercase());
Creates a new String
by repeating a string n
times.
This function will panic if the capacity would overflow.
+Basic usage:
+ +assert_eq!("abc".repeat(4), String::from("abcabcabcabc"));
A panic upon overflow:
+ +// this will panic at runtime
+let huge = "0123456789abcdef".repeat(usize::MAX);
Returns a copy of this string where each character is mapped to its +ASCII upper case equivalent.
+ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.
+To uppercase the value in-place, use make_ascii_uppercase
.
To uppercase ASCII characters in addition to non-ASCII characters, use
+to_uppercase
.
let s = "Grüße, Jürgen ❤";
+
+assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase());
Returns a copy of this string where each character is mapped to its +ASCII lower case equivalent.
+ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.
+To lowercase the value in-place, use make_ascii_lowercase
.
To lowercase ASCII characters in addition to non-ASCII characters, use
+to_lowercase
.
let s = "Grüße, Jürgen ❤";
+
+assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase());
source
. Read moreself
and other
) and is used by the <=
+operator. Read moreNon-thread-safe channels.
+See docs for mpsc::channel()
.
A non-thread-safe multi-producer, single-consumer, futures-aware, FIFO queue.
+Stream
trait.pub struct Receiver<T> { /* private fields */ }
The receiving end of a channel which implements the Stream
trait.
This is created by the channel
function.
pub struct SendError<T>(pub T);
Error returned when attempting to send after the channels’ Receiver is dropped or closed.
+Allows access to message that failed to send with into_inner
.
0: T
Returns the message that was attempted to be sent but failed.
+pub struct Sender<T> { /* private fields */ }
The transmission end of a channel.
+This is created by the channel
function.
Sink
to receive a value. Read morepoll_ready
which returned Poll::Ready(Ok(()))
. Read moreA synchronization primitive for thread-local task wakeup.
+See docs for LocalWaker
.
pub struct LocalWaker { /* private fields */ }
A synchronization primitive for task wakeup.
+Sometimes the task interested in a given event will change over time. A LocalWaker
can
+coordinate concurrent notifications with the consumer, potentially “updating” the underlying
+task to wake up. This is useful in scenarios where a computation completes in another task and
+wants to notify the consumer, but the consumer is in the process of being migrated to a new
+logical task.
Consumers should call register
before checking the result of a computation and producers
+should call wake
after producing the computation (this differs from the usual thread::park
+pattern). It is also permitted for wake
to be called before register
. This results in
+a no-op.
A single LocalWaker
may be reused for any number of calls to register
or wake
.
Stream
and Sink
interface to an underlying I/O …\nFramedParts
contains an export of the data of a Framed …\nThe type of decoded frames.\nLines codec. Reads/writes line delimited strings.\nA wrapper around a byte buffer that is incrementally …\nAdvances the size of the filled region of the buffer.\nAsserts that the first n
unfilled bytes of the buffer are …\nReturns the total capacity of the buffer.\nClears the buffer, resetting the filled region to empty.\nFlush write buffer and shutdown underlying I/O stream.\nThe codec object.\nReturns a mutable reference to the underlying codec.\nReturns a reference to the underlying codec.\nAttempts to decode a frame from the provided buffer of …\nA default method available to be called when there are no …\nEncodes a frame into the buffer provided.\nReturns a shared reference to the filled portion of the …\nReturns a mutable reference to the filled portion of the …\nFlush write buffer to underlying I/O stream.\nProvides a Stream
and Sink
interface for reading and …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nThis function returns a single object that is both Stream
…\nReturns a mutable reference to the unfilled part of the …\nReturns a mutable reference to the first n
bytes of the …\nReturns a shared reference to the initialized portion of …\nReturns a mutable reference to the initialized portion of …\nReturns a mutable reference to the entire buffer, without …\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConsume the Frame
, returning Frame
with different codec.\nConsume the Frame
, returning Frame
with different io.\nConsumes the Frame
, returning its underlying I/O stream, …\nThe inner transport used to read bytes to and write bytes …\nReturns a mutable reference to the underlying I/O stream.\nReturns a Pin
of a mutable reference to the underlying I/O …\nReturns a reference to the underlying I/O stream wrapped …\nCheck if read buffer is empty.\nCheck if write buffer is empty.\nCheck if write buffer is full.\nCheck if framed is able to write more data.\nDetermines if this writer has an efficient …\nCreates a new ReadBuf
from a fully initialized buffer.\nThis function returns a single object that is both Stream
…\nCreates a new default FramedParts
.\nTry to read underlying I/O stream and decode item.\nAttempts to flush the object, ensuring that any buffered …\nAttempts to read from the AsyncRead
into buf
.\nTry to read data from an AsyncRead
into an implementer of …\nInitiates or attempts to shut down this writer, returning …\nAttempt to write bytes from buf
into the object.\nLike poll_write
, except that it writes from a slice of …\nAppends data to the buffer, advancing the written position …\nThe buffer with read but unprocessed data.\nReturns the number of bytes at the end of the slice that …\nConsume the Frame
, returning Frame
with different codec.\nSets the size of the filled region of the buffer.\nReturns a new ReadBuf
comprised of the unfilled section up …\nReturns a mutable reference to the unfilled part of the …\nCreates a new ReadBuf
from a fully uninitialized buffer.\nCreates a new FramedParts
with read buffer.\nSerialize item and write to the inner buffer\nA buffer with unprocessed data which are not written yet.")
\ No newline at end of file
diff --git a/search.desc/actix_macros/actix_macros-desc-0-.js b/search.desc/actix_macros/actix_macros-desc-0-.js
new file mode 100644
index 0000000000..7369b384fe
--- /dev/null
+++ b/search.desc/actix_macros/actix_macros-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("actix_macros", 0, "Macros for Actix system and runtime.\nMarks async entry-point function to be executed by Actix …\nMarks async test function to be executed in an Actix …")
\ No newline at end of file
diff --git a/search.desc/actix_rt/actix_rt-desc-0-.js b/search.desc/actix_rt/actix_rt-desc-0-.js
new file mode 100644
index 0000000000..23c629e500
--- /dev/null
+++ b/search.desc/actix_rt/actix_rt-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("actix_rt", 0, "Tokio-based single-threaded async runtime for the Actix …\nAn Arbiter represents a thread that provides an …\nA handle for sending spawn and stop messages to an Arbiter.\nA Tokio-based runtime proxy.\nA manager for a per-thread distributed async runtime.\nRunner that keeps a System’s event loop alive until stop …\nGet handle to a the System’s initial Arbiter.\nRuns the provided future, blocking the current thread …\nRuns the provided future, blocking the current thread …\nReturn a handle to the current thread’s Arbiter’s …\nGet current running system.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturn a handle to the this Arbiter’s message sender.\nNumeric system identifier.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCheck if there is a System registered on the current …\nWait for Arbiter’s event loop to complete.\nMarks async entry-point function to be executed by Actix …\nTCP/UDP/Unix bindings (mostly Tokio re-exports).\nSpawn a new Arbiter thread and start its event loop with …\nReturns a new runtime initialized with default …\nCreate a new system.\nPins a value on the stack.\nStarts event loop and will return once System is stopped.\nRuns the event loop until stopped, returning the exit code.\nAsynchronous signal handling (Tokio re-exports).\nSpawns a future on the current thread as a new task.\nSend a future to the Arbiter’s thread and spawn it.\nSend a future to the Arbiter’s thread and spawn it.\nOffload a future onto the single-threaded runtime.\nSend a function to the Arbiter’s thread and execute it.\nSend a function to the Arbiter’s thread and execute it.\nInstruct Arbiter to stop processing it’s event loop.\nStop Arbiter from continuing it’s event loop.\nStop the system (with code 0).\nStop the system with a given exit code.\nTask management (Tokio re-exports).\nMarks async test function to be executed in an Actix …\nUtilities for tracking time (Tokio re-exports).\nRetrieves a reference to the underlying Tokio runtime …\nTry to get current running arbiter handle.\nTry to get current running system.\nReturns a Ready
representing readiness for all operations.\nReturns the empty Ready
set.\nReturns a Ready
representing error readiness.\nReturns a Ready
representing priority readiness.\nReturns a Ready
representing readable readiness.\nReturns a Ready
representing read closed readiness.\nDescribes the readiness state of an I/O resources.\nA TCP socket server, listening for connections.\nA TCP socket that has not yet been converted to a TcpStream
…\nA TCP stream between a local and a remote socket.\nA UDP socket.\nAn I/O object representing a Unix datagram socket.\nA Unix socket which can accept connections from other Unix …\nA structure representing a connected Unix socket.\nReturns a Ready
representing writable readiness.\nReturns a Ready
representing write closed readiness.\nAccepts a new incoming connection from this listener.\nAccepts a new incoming connection to this listener.\nReads or writes from the socket using a user-provided IO …\nReads or writes from the socket using a user-provided IO …\nReads or writes from the socket using a user-provided IO …\nReads or writes from the socket using a user-provided IO …\nCreates a new TcpListener
, which will be bound to the …\nBinds the socket to the given address.\nThis function will create a new UDP socket and attempt to …\nCreates a new UnixDatagram
bound to the specified path.\nCreates a new UnixListener
bound to the specified path.\nSets the value for the SO_BINDTODEVICE
option on this …\nSets the value for the SO_BINDTODEVICE
option on this …\nGets the value of the SO_BROADCAST
option for this socket.\nEstablishes a TCP connection with a peer at the specified …\nOpens a TCP connection to a remote host.\nConnects the UDP socket setting the default destination …\nConnects the socket to the specified address.\nConnects to the socket named by path
.\nGets the value for the SO_BINDTODEVICE
option on this …\nGets the value for the SO_BINDTODEVICE
option on this …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nConverts a RawFd
to a TcpSocket
.\nCreates new TcpListener
from a std::net::TcpListener
.\nCreates new TcpStream
from a std::net::TcpStream
.\nCreates new UdpSocket
from a previously bound …\nCreates new UnixDatagram
from a …\nCreates new UnixListener
from a …\nCreates new UnixStream
from a …\nConverts a std::net::TcpStream
into a TcpSocket
. The …\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nSplits a TcpStream
into a read half and a write half, …\nSplits a UnixStream
into a read half and a write half, …\nTurns a tokio::net::TcpListener
into a …\nTurns a tokio::net::TcpStream
into a std::net::TcpStream
.\nTurns a tokio::net::UdpSocket
into a std::net::UdpSocket
.\nTurns a tokio::net::UnixDatagram
into a …\nTurns a tokio::net::UnixListener
into a …\nTurns a tokio::net::UnixStream
into a …\nReturns true if Ready
is the empty set.\nReturns true
if the value includes error readiness
.\nReturns true
if the value includes priority readiness
.\nReturns true
if the value includes read-closed readiness
.\nReturns true
if the value includes readable
.\nReturns true
if the value includes writable readiness
.\nReturns true
if the value includes write-closed readiness
.\nExecutes an operation of the IP_ADD_MEMBERSHIP
type.\nExecutes an operation of the IPV6_ADD_MEMBERSHIP
type.\nGets the value of the SO_KEEPALIVE
option on this socket.\nExecutes an operation of the IP_DROP_MEMBERSHIP
type.\nExecutes an operation of the IPV6_DROP_MEMBERSHIP
type.\nReads the linger duration for this socket by getting the …\nReads the linger duration for this socket by getting the …\nConverts the socket into a TcpListener
.\nReturns the local address that this listener is bound to.\nGets the local address of this socket.\nReturns the local address that this stream is bound to.\nReturns the local address that this socket is bound to.\nReturns the local address that this socket is bound to.\nReturns the local socket address of this listener.\nReturns the socket address of the local half of this …\nGets the value of the IP_MULTICAST_LOOP
option for this …\nGets the value of the IPV6_MULTICAST_LOOP
option for this …\nGets the value of the IP_MULTICAST_TTL
option for this …\nCreates a new socket configured for IPv4.\nCreates a new socket configured for IPv6.\nGets the value of the TCP_NODELAY
option on this socket.\nGets the value of the TCP_NODELAY
option on this socket.\nCreates an unnamed pair of connected sockets.\nCreates an unnamed pair of connected sockets.\nReceives data on the socket from the remote address to …\nReceives data from the socket, without removing it from …\nRetrieve the sender of the data at the head of the input …\nReturns the remote address that this stream is connected …\nReturns the socket address of the remote peer this socket …\nReturns the address of this socket’s peer.\nReturns the socket address of the remote half of this …\nReturns effective credentials of the process which called …\nPolls to accept a new incoming connection to this listener.\nPolls to accept a new incoming connection to this listener.\nAttempts to receive data on the socket, without removing …\nReceives data from the socket, without removing it from …\nRetrieve the sender of the data at the head of the input …\nPolls for read readiness.\nPolls for read readiness.\nAttempts to receive a single datagram message on the …\nAttempts to receive a single datagram message on the …\nAttempts to receive a single datagram on the socket.\nAttempts to receive a single datagram on the specified …\nPolls for read/receive readiness.\nPolls for read/receive readiness.\nAttempts to send data on the socket to the remote address …\nAttempts to send data on the socket to the remote address …\nPolls for write/send readiness.\nPolls for write/send readiness.\nAttempts to send data on the socket to a given address.\nAttempts to send data to the specified address.\nPolls for write readiness.\nPolls for write readiness.\nWaits for the socket to become readable.\nWaits for the socket to become readable.\nWaits for the socket to become readable.\nWaits for the socket to become readable.\nWaits for any of the requested ready states.\nWaits for any of the requested ready states.\nWaits for any of the requested ready states.\nWaits for any of the requested ready states.\nReceives a single datagram message on the socket from the …\nReceives data from the socket.\nReturns the size of the TCP receive buffer for this socket.\nReceives a single datagram message on the socket. On …\nReceives data from the socket.\nRetrieves the value set for SO_REUSEADDR
on this socket.\nAllows the socket to bind to an in-use port. Only …\nSends data on the socket to the remote address that the …\nSends data on the socket to the socket’s peer.\nReturns the size of the TCP send buffer for this socket.\nSends data on the socket to the given address. On success, …\nSends data on the socket to the specified address.\nSets the value of the SO_BROADCAST
option for this socket.\nSets value for the SO_KEEPALIVE
option on this socket.\nSets the linger duration of this socket by setting the …\nSets the linger duration of this socket by setting the …\nSets the value of the IP_MULTICAST_LOOP
option for this …\nSets the value of the IPV6_MULTICAST_LOOP
option for this …\nSets the value of the IP_MULTICAST_TTL
option for this …\nSets the value of the TCP_NODELAY
option on this socket.\nSets the value of the TCP_NODELAY
option on this socket.\nSets the size of the TCP receive buffer on this socket.\nAllows the socket to bind to an in-use address.\nAllows the socket to bind to an in-use port. Only …\nSets the size of the TCP send buffer on this socket.\nSets the value for the IP_TOS
option on this socket.\nSets the value for the IP_TOS
option on this socket.\nSets the value for the IP_TTL
option on this socket.\nSets the value for the IP_TTL
option on this socket.\nSets the value for the IP_TTL
option on this socket.\nShuts down the read, write, or both halves of this …\nSplits a TcpStream
into a read half and a write half, …\nSplits a UnixStream
into a read half and a write half, …\nReturns the value of the SO_ERROR
option.\nReturns the value of the SO_ERROR
option.\nReturns the value of the SO_ERROR
option.\nReturns the value of the SO_ERROR
option.\nReturns the value of the SO_ERROR
option.\nReturns the value of the SO_ERROR
option.\nGets the value of the IP_TOS
option for this socket.\nGets the value of the IP_TOS
option for this socket.\nConsumes stream, returning the tokio I/O object.\nConsumes stream, returning the tokio I/O object.\nConsumes stream, returning the tokio I/O object.\nConsumes stream, returning the Tokio I/O object.\nConsumes stream, returning the tokio I/O object.\nConsumes stream, returning the tokio I/O object.\nTries to read or write from the socket using a …\nTries to read or write from the socket using a …\nTries to read or write from the socket using a …\nTries to read or write from the socket using a …\nTries to receive data on the socket without removing it …\nTry to retrieve the sender of the data at the head of the …\nTries to read data from the stream into the provided …\nTry to read data from the stream into the provided buffer, …\nTries to read data from the stream into the provided …\nTries to read data from the stream into the provided …\nTries to receive a single datagram message on the socket …\nTries to receive a datagram from the peer without waiting.\nTries to receive a single datagram message on the socket. …\nTries to receive data from the socket without waiting.\nTries to send data on the socket to the remote address to …\nTries to send a datagram to the peer without waiting.\nTries to send data on the socket to the given address, but …\nTries to send a datagram to the peer without waiting.\nTry to write a buffer to the stream, returning how many …\nTries to write a buffer to the stream, returning how many …\nTries to write several buffers to the stream, returning …\nTries to write several buffers to the stream, returning …\nGets the value of the IP_TTL
option for this socket.\nGets the value of the IP_TTL
option for this socket.\nGets the value of the IP_TTL
option for this socket.\nCreates a new UnixDatagram
which is not bound to any …\nWaits for the socket to become writable.\nWaits for the socket to become writable.\nWaits for the socket to become writable.\nWaits for the socket to become writable.\nCompletes when a “ctrl-c” notification is sent to the …\nUnix specific signals (Tokio re-exports).\nAn listener for receiving a particular type of OS signal.\nRepresents the specific kind of signal to listen for.\nRepresents the SIGALRM
signal.\nGet the signal’s numeric value.\nRepresents the SIGCHLD
signal.\nReturns the argument unchanged.\nReturns the argument unchanged.\nAllows for listening to any valid OS signal.\nRepresents the SIGHUP
signal.\nRepresents the SIGINT
signal.\nCalls U::from(self)
.\nCalls U::from(self)
.\nRepresents the SIGIO
signal.\nRepresents the SIGPIPE
signal.\nPolls to receive the next signal notification event, …\nRepresents the SIGQUIT
signal.\nReceives the next signal notification event.\nCreates a new listener which will receive notifications …\nRepresents the SIGTERM
signal.\nRepresents the SIGUSR1
signal.\nRepresents the SIGUSR2
signal.\nRepresents the SIGWINCH
signal.\nTask failed to execute to completion.\nAn owned permission to join on a task (await its …\nAbort the task associated with the handle.\nReturns a new AbortHandle
that can be used to remotely …\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConsumes the join error, returning the object with which …\nReturns true if the error was caused by the task being …\nChecks if the task associated with this JoinHandle
has …\nReturns true if the error was caused by the task panicking.\nRuns the provided closure on a thread where blocking is …\nConsumes the join error, returning the object with which …\nYields execution back to the Tokio runtime.\nA measurement of a monotonically nondecreasing clock. …\nInterval returned by interval
and interval_at
.\nFuture returned by sleep
and sleep_until
.\nFuture returned by timeout
and timeout_at
.\nReturns Some(t)
where t
is the time self + duration
if t
…\nReturns the amount of time elapsed from another instant to …\nReturns Some(t)
where t
is the time self - duration
if t
…\nReturns the instant at which the future will complete.\nReturns the amount of time elapsed from another instant to …\nReturns the amount of time elapsed since this instant was …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCreate a tokio::time::Instant
from a std::time::Instant
.\nGets a mutable reference to the underlying value in this …\nGets a reference to the underlying value in this timeout.\nCreates new Interval
that yields with interval of period
. …\nCreates new Interval
that yields with interval of period
…\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConsumes this timeout, returning the underlying value.\nConvert the value into a std::time::Instant
.\nReturns true
if Sleep
has elapsed.\nReturns the MissedTickBehavior
strategy currently being …\nReturns an instant corresponding to “now”.\nReturns the period of the interval.\nPolls for the next instant in the interval to be reached.\nResets the interval to complete one period after the …\nResets the Sleep
instance to a new deadline.\nResets the interval after the specified std::time::Duration
…\nResets the interval to a crate::time::Instant
deadline.\nResets the interval immediately.\nReturns the amount of time elapsed from another instant to …\nSets the MissedTickBehavior
strategy that should be used.\nWaits until duration
has elapsed.\nWaits until deadline
is reached.\nCompletes when the next instant in the interval has been …\nRequires a Future
to complete before the specified …")
\ No newline at end of file
diff --git a/search.desc/actix_server/actix_server-desc-0-.js b/search.desc/actix_server/actix_server-desc-0-.js
new file mode 100644
index 0000000000..6831a683b6
--- /dev/null
+++ b/search.desc/actix_server/actix_server-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("actix_server", 0, "General purpose TCP server.\nMPTCP will not be used when binding sockets.\nMultipath TCP (MPTCP) preference.\nMPTCP will be used when binding sockets (with no fallback).\nGeneral purpose TCP server that runs services receiving …\nServer builder.\nServer handle.\nMPTCP will be attempted when binding sockets. If errors …\nA testing server.\nSet the maximum number of pending connections.\nAdds new service to the server.\nAdds new service to the server using a UDS (unix domain …\nCreate server build.\nDisables OS signal handling.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nGet a Server
handle that can be used issue commands and …\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nAdds service to the server using a socket listener already …\nAdds new service to the server using a UDS (unix domain …\nSets the maximum per-worker number of concurrent …\nSets MultiPath TCP (MPTCP) preference on bound sockets.\nCreate new Server builder instance\nPause accepting incoming connections.\nResume accepting incoming connections.\nStarts processing incoming connections and return server …\nTimeout for graceful workers shutdown in seconds.\nStart new TestServer
using application factory and default …\nStart new TestServer
using application factory and server …\nStop incoming connection processing, stop all workers and …\nSets flag to stop Actix System
after server shutdown.\nGet first available unused local address.\nSet max number of threads for each worker’s blocking …\nSets number of workers to start.")
\ No newline at end of file
diff --git a/search.desc/actix_service/actix_service-desc-0-.js b/search.desc/actix_service/actix_service-desc-0-.js
new file mode 100644
index 0000000000..52925fd13d
--- /dev/null
+++ b/search.desc/actix_service/actix_service-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("actix_service", 0, "See Service
docs for information on this crate’s …\nApply a Transform
to a Service
.\nService factory configuration.\nErrors produced by the service.\nErrors produced by the service when polling readiness or …\nErrors produced by the created services.\nThe future response value.\nThe future response value.\nThe future of the Service
instance.g\nErrors produced while building a transform service.\nErrors potentially raised while building a service.\nTrait for types that can be converted to a Service
\nTrait for types that can be converted to a ServiceFactory
\nResponses produced by the service.\nResponses given by the service.\nResponses given by the created services.\nAn asynchronous operation from Request
to a Response
.\nThe kind of Service
created by this factory.\nAn extension trait for Service
s that provides a variety of …\nFactory for creating Service
s.\nAn extension trait for ServiceFactory
s that provides a …\nDefines the interface of a service factory that wraps …\nThe TransformService
value created by this factory\nAn extension trait for Transform
s that provides a variety …\nAn implementation of poll_ready
that always signals …\nCall another service after call to this one has resolved …\nCall another service after call to this one has resolved …\nCall another service after call to this one has resolved …\nCall another service after call to this one has resolved …\nApply a Transform
to a Service
.\nConvert Fn(Config, &Service1) -> Future<Service2>
fn to a …\nConvert …\nApply transform function to a service.\nService factory that produces apply_fn
service.\nTrait object forms of services and service factories.\nProcess the request and return the response asynchronously.\nCreate ServiceFactory
for function that can produce …\nCreate ServiceFactory
for function that accepts config …\nCreate ServiceFactory
for function that can act as a …\nAn implementation of poll_ready
that forwards readiness …\nReturns the argument unchanged.\nCalls U::from(self)
.\nConvert Self
to a ServiceFactory
\nConvert object of type U
to a service S
\nConvert to a Service
\nMap this service’s output to a different type, returning …\nMap this service’s output to a different type, returning …\nMap this service’s output to a different type, returning …\nMap this service’s output to a different type, returning …\nAdapt external config argument to a config for provided …\nMap this service’s error to a different error, returning …\nMap this service’s error to a different error, returning …\nMap this service’s error to a different error, returning …\nMap this service’s error to a different error, returning …\nMap this factory’s init error to a different error, …\nMap this factory’s init error to a different error, …\nReturn a new Transform
whose init error is mapped to to a …\nReturn a new Transform
whose init error is mapped to to a …\nCreate and return a new service asynchronously.\nCreates and returns a new Transform component, …\nReturns Ready
when the service is able to process requests.\nReplace config with unit.\nA boxed future with no send bound or lifetime parameters.\nType alias for service trait object using Box
.\nWrapper for a service factory that will map it’s …\nType alias for service trait object using Rc
.\nWraps a service factory that returns service trait objects.\nReturns the argument unchanged.\nCalls U::from(self)
.\nWraps service as a trait object using RcService
.\nWraps service as a trait object using BoxService
.")
\ No newline at end of file
diff --git a/search.desc/actix_tls/actix_tls-desc-0-.js b/search.desc/actix_tls/actix_tls-desc-0-.js
new file mode 100644
index 0000000000..26e8d5aa54
--- /dev/null
+++ b/search.desc/actix_tls/actix_tls-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("actix_tls", 0, "TLS acceptor and connector services for the Actix …\nTLS connection acceptor services.\nTCP and TLS connector services.\nWraps service errors.\nTLS handshake has timed-out.\nWraps TLS service errors.\nTLS handshake error, TLS timeout, or inner service error.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCasts the infallible service error type returned from …\nSets the maximum per-worker concurrent TLS connection …\nnative-tls
based TLS connection acceptor service.\nopenssl
based TLS acceptor service.\nrustls
v0.20 based TLS connection acceptor service.\nrustls
v0.21 based TLS connection acceptor service.\nrustls
v0.22 based TLS connection acceptor service.\nrustls
v0.23 based TLS connection acceptor service.\nAccept TLS connections via the native-tls
crate.\nNative-TLS based acceptor service.\nWraps a native-tls
based async TLS stream in order to …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConstructs native-tls
based acceptor service factory.\nRe-exports from native-tls
that are useful for acceptors.\nLimit the amount of time that the acceptor will wait for a …\nAn error returned from the TLS implementation.\nA wrapper around a native_tls::TlsAcceptor
, providing an …\nAccepts a new client connection with the provided stream.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nAccept TLS connections via the openssl
crate.\nOpenSSL based acceptor service.\nWraps an openssl
based async TLS stream in order to …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCreate openssl
based acceptor service factory.\nRe-exports from openssl
that are useful for acceptors.\nLimit the amount of time that the acceptor will wait for a …\nTerminate the handshake with a fatal alert.\nAn error returned from an ALPN selection callback.\nAn SSL error.\nThe handshake failed.\nAn error or intermediate state after a TLS handshake …\nDo not select a protocol, but continue the handshake.\nSetup failed.\nThe state of an SSL/TLS session.\nA type which wraps server-side streams in a TLS session.\nA builder for SslAcceptor
s.\nThe handshake encountered a WouldBlock
error midway …\nInitiates a server-side TLS session on a stream.\nInitiates a server-side TLS handshake.\nConsumes the builder, returning a SslAcceptor
.\nInitiates a client-side TLS handshake.\nReturns a shared reference to the inner raw SslContext
.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConsumes the SslAcceptor
, returning the inner raw …\nCreates a new builder configured to connect to non-legacy …\nCreates a new builder configured to connect to non-legacy …\nCreates a new builder configured to connect to modern …\nCreates a new builder configured to connect to modern …\nCreates a new Ssl
.\nReturns a new extra data index.\nAccept TLS connections via the rustls
crate.\nRustls based acceptor service.\nWraps a rustls
based async TLS stream in order to …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConstructs rustls
based acceptor service factory.\nRe-exports from rustls
that are useful for acceptors.\nLimit the amount of time that the acceptor will wait for a …\nCommon configuration for a set of server sessions.\nProtocol names we support, most preferred first. If empty …\nCreate builder to build up the server configuration.\nHow to choose a server cert and key.\nReturns the argument unchanged.\nIgnore the client’s ciphersuite order. Instead, choose …\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nAmount of early data to accept for sessions created by …\nThe maximum size of TLS message we’ll emit. If None, we …\nWhether the server should send “0.5RTT” data. This …\nHow to store client sessions.\nHow to produce tickets.\nAccept TLS connections via the rustls
crate.\nRustls based acceptor service.\nWraps a rustls
based async TLS stream in order to …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConstructs rustls
based acceptor service factory.\nRe-exports from rustls
that are useful for acceptors.\nLimit the amount of time that the acceptor will wait for a …\nCommon configuration for a set of server sessions.\nProtocol names we support, most preferred first. If empty …\nCreate builder to build up the server configuration.\nHow to choose a server cert and key.\nReturns the argument unchanged.\nIgnore the client’s ciphersuite order. Instead, choose …\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nAmount of early data to accept for sessions created by …\nThe maximum size of TLS message we’ll emit. If None, we …\nWhether the server should send “0.5RTT” data. This …\nHow many TLS1.3 tickets to send immediately after a …\nHow to store client sessions.\nHow to produce tickets.\nAccept TLS connections via the rustls
crate.\nRustls based acceptor service.\nWraps a rustls
based async TLS stream in order to …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConstructs rustls
based acceptor service factory.\nRe-exports from rustls
that are useful for acceptors.\nLimit the amount of time that the acceptor will wait for a …\nCommon configuration for a set of server sessions.\nProtocol names we support, most preferred first. If empty …\nCreate a builder for a server configuration with the …\nCreate a builder for a server configuration with the …\nCreate a builder for a server configuration with a …\nHow to choose a server cert and key. This is usually set by\nAllows traffic secrets to be extracted after the handshake,\nReturns the argument unchanged.\nIgnore the client’s ciphersuite order. Instead, choose …\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nAmount of early data to accept for sessions created by …\nThe maximum size of plaintext input to be emitted in a …\nWhether the server should send “0.5RTT” data. This …\nHow many TLS1.3 tickets to send immediately after a …\nHow to store client sessions.\nHow to produce tickets.\nAccept TLS connections via the rustls
crate.\nRustls based acceptor service.\nWraps a rustls
based async TLS stream in order to …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConstructs rustls
based acceptor service factory.\nRe-exports from rustls
that are useful for acceptors.\nLimit the amount of time that the acceptor will wait for a …\nCommon configuration for a set of server sessions.\nProtocol names we support, most preferred first. If empty …\nCreate a builder for a server configuration with the …\nCreate a builder for a server configuration with no …\nCreate a builder for a server configuration with the …\nCreate a builder for a server configuration with a …\nCaching for compressed certificates.\nHow to compress the server’s certificate chain.\nHow to decompress the clients’s certificate chain.\nHow to choose a server cert and key. This is usually set by\nReturn the crypto provider used to construct this client …\nAllows traffic secrets to be extracted after the handshake,\nReturn true
if connections made with this ServerConfig
will\nReturns the argument unchanged.\nIgnore the client’s ciphersuite order. Instead, choose …\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nAmount of early data to accept for sessions created by …\nThe maximum size of plaintext input to be emitted in a …\nWhether the server should send “0.5RTT” data. This …\nHow many TLS1.3 tickets to send immediately after a …\nHow to store client sessions.\nHow to produce tickets.\nProvides the current system time\nErrors that can result from using a connector service.\nConnection request information.\nWraps underlying I/O and the connection request that …\nCombined resolver and TCP connector service factory.\nCombined resolver and TCP connector service.\nAn interface for types where host parts (hostname and …\nInvalid input.\nConnection IO error.\nNo DNS records.\nCustom async DNS resolvers.\nDNS resolver service factory.\nFailed to resolve the hostname.\nDNS resolver service.\nUnresolved host name.\nGet borrowed iterator of resolved request addresses.\nConstructs a new resolver factory with a custom resolver.\nConstructor for custom Resolve trait object and use it as …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nExtract hostname.\nReturns hostname.\nReturns request hostname.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nDeconstructs into IO and request parts.\nReturns a mutable reference to the underlying IO.\nReturns a shared reference to the underlying IO.\nGiven DNS lookup information, returns a future that …\nNative-TLS based connector service.\nConstruct new Connection
from request and IO parts.\nConstructs new connector factory with the given resolver.\nConstructs new connection info using a request.\nOpenSSL based connector service.\nExtract optional port.\nExtract optional port.\nReturns request port.\nReplaces underlying IO, returning old IO and new Connection
…\nReturns a reference to the connection request.\nReturns a reference to the connection request.\nRustls based connector service.\nRustls based connector service.\nRustls based connector service.\nRustls based connector service.\nBuild connector service.\nReturns a new resolver service.\nSet connection socket address.\nSet list of addresses.\nSet local address to connection with.\nSet connection port.\nTake owned iterator resolved request addresses.\nTCP connector service.\nConstructs new connection info from request and known …\nConnector service and factory using native-tls
.\nReturns the argument unchanged.\nCalls U::from(self)
.\nConstructs new connector service from a native-tls
…\nRe-exports from native-tls
and tokio-native-tls
that are …\nA wrapper around an underlying raw stream which implements …\nA builder for client-side TLS connections.\nReturns a new builder for a TlsConnector
.\nInitiates a TLS handshake.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns a mutable reference to the inner stream.\nReturns a shared reference to the inner stream.\nCalls U::from(self)
.\nCalls U::from(self)
.\nReturns a new connector with default settings.\nConnector service factory using openssl
.\nConnector service using openssl
.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConstructs new connector service factory from an openssl
…\nRe-exports from openssl
and tokio-openssl
that are useful …\nConstructs new connector service from an openssl
connector.\nAn asynchronous version of openssl::ssl::SslStream
.\nAn SSL error.\nThe handshake failed.\nAn error or intermediate state after a TLS handshake …\nSetup failed.\nA type which wraps client-side streams in a TLS session.\nA builder for SslConnector
s.\nA type specifying the kind of protocol an SslContext
will …\nThe handshake encountered a WouldBlock
error midway …\nA convenience method wrapping poll_accept
.\nReturns a pointer to the underlying OpenSSL value.\nConsumes the builder, returning an SslConnector
.\nCreates a new builder for TLS connections.\nReturns a structure allowing for configuration of a single …\nA convenience method wrapping poll_connect
.\nInitiates a client-side TLS session on a stream.\nReturns a shared reference to the inner raw SslContext
.\nA convenience method wrapping poll_do_handshake
.\nSupport all versions of the DTLS protocol.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nConstructs an SslMethod
from a pointer to the underlying …\nReturns a mutable reference to the underlying stream.\nReturns a pinned mutable reference to the underlying …\nReturns a shared reference to the underlying stream.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nConsumes the SslConnector
, returning the inner raw …\nLike SslStream::new
.\nLike SslStream::accept
.\nLike SslStream::connect
.\nLike SslStream::do_handshake
.\nLike SslStream::read_early_data
.\nLike SslStream::write_early_data
.\nA convenience method wrapping poll_read_early_data
.\nReturns a shared reference to the Ssl
object associated …\nSupport all versions of the TLS protocol.\nSupport all versions of the TLS protocol, explicitly as a …\nSupport all versions of the TLS protocol, explicitly as a …\nA convenience method wrapping poll_write_early_data
.\nConnector service factory using rustls
.\nConnector service using rustls
.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nReturns root certificates via rustls-native-certs
crate as …\nConstructs new connector service factory from a rustls
…\nRe-exports from the rustls
v0.20 ecosystem that are useful …\nConstructs new connector service from a rustls
client …\nReturns standard root certificates from webpki-roots
crate …\nA wrapper around an underlying raw stream which implements …\nCommon configuration for (typically) all connections made …\nWhich ALPN protocols we include in our client hello. If …\nCreate a builder to build up the client configuration.\nHow to decide what client auth certificate/keys to use.\nWhether to send data on the first flight (“early data”…\nWhether to send the Server Name Indication (SNI) extension …\nWhether to support RFC5077 tickets. You must provide a …\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nThe maximum size of TLS message we’ll emit. If None, we …\nNote: that it does not guarantee the final data to be sent.\nHow we store session data or tickets.\nConnector service factory using rustls
.\nConnector service using rustls
.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nReturns root certificates via rustls-native-certs
crate as …\nConstructs new connector service factory from a rustls
…\nRe-exports from the rustls
v0.21 ecosystem that are useful …\nConstructs new connector service from a rustls
client …\nReturns standard root certificates from webpki-roots
crate …\nA wrapper around an underlying raw stream which implements …\nCommon configuration for (typically) all connections made …\nWhich ALPN protocols we include in our client hello. If …\nCreate a builder to build up the client configuration.\nHow to decide what client auth certificate/keys to use.\nWhether to send data on the first flight (“early data”…\nWhether to send the Server Name Indication (SNI) extension …\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nThe maximum size of TLS message we’ll emit. If None, we …\nNote: that it does not guarantee the final data to be sent.\nHow and when the client can resume a previous session.\nConnector service factory using rustls
.\nConnector service using rustls
.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nReturns root certificates via rustls-native-certs
crate as …\nConstructs new connector service factory from a rustls
…\nRe-exports from the rustls
v0.22 ecosystem that are useful …\nConstructs new connector service from a rustls
client …\nReturns standard root certificates from webpki-roots
crate …\nA wrapper around an underlying raw stream which implements …\nCommon configuration for (typically) all connections made …\nWhich ALPN protocols we include in our client hello. If …\nCreate a builder for a client configuration with the …\nCreate a builder for a client configuration with the …\nCreate a builder for a client configuration with a …\nHow to decide what client auth certificate/keys to use.\nAccess configuration options whose use is dangerous and …\nWhether to send data on the first flight (“early data”…\nAllows traffic secrets to be extracted after the handshake,\nWhether to send the Server Name Indication (SNI) extension …\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nThe maximum size of plaintext input to be emitted in a …\nNote: that it does not guarantee the final data to be sent.\nHow and when the client can resume a previous session.\nConnector service factory using rustls
.\nConnector service using rustls
.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nReturns root certificates via rustls-native-certs
crate as …\nConstructs new connector service factory from a rustls
…\nRe-exports from the rustls
v0.23 ecosystem that are useful …\nConstructs new connector service from a rustls
client …\nReturns standard root certificates from webpki-roots
crate …\nA wrapper around an underlying raw stream which implements …\nCommon configuration for (typically) all connections made …\nWhich ALPN protocols we include in our client hello. If …\nCreate a builder for a client configuration with the …\nCreate a builder for a client configuration with no …\nCreate a builder for a client configuration with the …\nCreate a builder for a client configuration with a …\nCaching for compressed certificates.\nHow to compress the client’s certificate chain.\nHow to decompress the server’s certificate chain.\nHow to decide what client auth certificate/keys to use.\nReturn the crypto provider used to construct this client …\nAccess configuration options whose use is dangerous and …\nWhether to send data on the first flight (“early data”…\nAllows traffic secrets to be extracted after the handshake,\nWhether to send the Server Name Indication (SNI) extension …\nReturn true if connections made with this ClientConfig
will\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nHow to output key material for debugging. The default …\nThe maximum size of plaintext input to be emitted in a …\nNote: that it does not guarantee the final data to be sent.\nNote: that it does not guarantee the final data to be sent.\nHow and when the client can resume a previous session.\nProvides the current system time\nTCP connector service factory.\nTCP connector service.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nReturns a new TCP connector service.")
\ No newline at end of file
diff --git a/search.desc/actix_tracing/actix_tracing-desc-0-.js b/search.desc/actix_tracing/actix_tracing-desc-0-.js
new file mode 100644
index 0000000000..047ddb258a
--- /dev/null
+++ b/search.desc/actix_tracing/actix_tracing-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("actix_tracing", 0, "Actix tracing - support for tokio tracing with Actix …\nA Service
implementation that automatically enters/exits …\nA Transform
implementation that wraps services with a …\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nWraps the provided service factory with a transform that …")
\ No newline at end of file
diff --git a/search.desc/actix_utils/actix_utils-desc-0-.js b/search.desc/actix_utils/actix_utils-desc-0-.js
new file mode 100644
index 0000000000..3ca2bb2f98
--- /dev/null
+++ b/search.desc/actix_utils/actix_utils-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("actix_utils", 0, "Various utilities used in the Actix ecosystem.\nTask-notifying counter.\nHelpers for constructing futures.\nSimple counter with ability to notify task on reaching …\nAn RAII structure that keeps the underlying counter …\nReturns true if counter is below capacity. Otherwise, …\nReturns the argument unchanged.\nReturns the argument unchanged.\nCreate new counter guard, incrementing the counter.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCreate Counter
instance with max value.\nGet total number of acquired guards.\nCombines two different futures that have the same output …\nA value of type L
.\nFuture for the poll_fn
function.\nFuture for the ready
function.\nA value of type R
.\nCreates a future that is immediately ready with an error …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nUnwraps into inner value when left and right have a common …\nUnwraps the value from this immediately ready future.\nCreates new Either
using left variant.\nCreates a future that is immediately ready with a success …\nCreates a future driven by the provided function that …\nCreates a future that is immediately ready with a value.\nCreates new Either
using right variant.")
\ No newline at end of file
diff --git a/search.desc/bytestring/bytestring-desc-0-.js b/search.desc/bytestring/bytestring-desc-0-.js
new file mode 100644
index 0000000000..dae1c3afb6
--- /dev/null
+++ b/search.desc/bytestring/bytestring-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("bytestring", 0, "A UTF-8 encoded read-only string using Bytes
as storage.\nAn immutable UTF-8 encoded string with Bytes
as a storage.\nGet a reference to the underlying Bytes
object.\nReturns the argument unchanged.\nCreates a new ByteString
from a Bytes.\nCreates a new ByteString
from a &'static str
.\nCalls U::from(self)
.\nUnwraps this ByteString
into the underlying Bytes
object.\nCreates a new empty ByteString
.\nReturns a new byte string that is equivalent to the given …")
\ No newline at end of file
diff --git a/search.desc/local_channel/local_channel-desc-0-.js b/search.desc/local_channel/local_channel-desc-0-.js
new file mode 100644
index 0000000000..dde61ec3e0
--- /dev/null
+++ b/search.desc/local_channel/local_channel-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("local_channel", 0, "Non-thread-safe channels.\nA non-thread-safe multi-producer, single-consumer, …\nThe receiving end of a channel which implements the Stream
…\nError returned when attempting to send after the channels…\nThe transmission end of a channel.\nCreates a unbounded in-memory channel with buffered …\nCloses the sender half.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nReturns the message that was attempted to be sent but …\nReceive the next value.\nSends the provided message along this channel.\nCreate an associated Sender.")
\ No newline at end of file
diff --git a/search.desc/local_waker/local_waker-desc-0-.js b/search.desc/local_waker/local_waker-desc-0-.js
new file mode 100644
index 0000000000..e2c3414a51
--- /dev/null
+++ b/search.desc/local_waker/local_waker-desc-0-.js
@@ -0,0 +1 @@
+searchState.loadedDescShard("local_waker", 0, "A synchronization primitive for thread-local task wakeup.\nA synchronization primitive for task wakeup.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCreates a new, empty LocalWaker
.\nRegisters the waker to be notified on calls to wake
.\nReturns the last Waker
passed to register
, so that the …\nCalls wake
on the last Waker
passed to register
.")
\ No newline at end of file
diff --git a/settings.html b/settings.html
new file mode 100644
index 0000000000..319685cf9a
--- /dev/null
+++ b/settings.html
@@ -0,0 +1 @@
+1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +
use std::io;
+
+use bytes::{Buf, Bytes, BytesMut};
+
+use super::{Decoder, Encoder};
+
+/// Bytes codec. Reads/writes chunks of bytes from a stream.
+#[derive(Debug, Copy, Clone)]
+pub struct BytesCodec;
+
+impl Encoder<Bytes> for BytesCodec {
+ type Error = io::Error;
+
+ #[inline]
+ fn encode(&mut self, item: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> {
+ dst.extend_from_slice(item.chunk());
+ Ok(())
+ }
+}
+
+impl Decoder for BytesCodec {
+ type Item = BytesMut;
+ type Error = io::Error;
+
+ fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+ if src.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(src.split()))
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +
use std::{
+ fmt, io,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use bitflags::bitflags;
+use bytes::{Buf, BytesMut};
+use futures_core::{ready, Stream};
+use futures_sink::Sink;
+use pin_project_lite::pin_project;
+
+use crate::{AsyncRead, AsyncWrite, Decoder, Encoder};
+
+/// Low-water mark
+const LW: usize = 1024;
+/// High-water mark
+const HW: usize = 8 * 1024;
+
+bitflags! {
+ #[derive(Debug, Clone, Copy)]
+ struct Flags: u8 {
+ const EOF = 0b0001;
+ const READABLE = 0b0010;
+ }
+}
+
+pin_project! {
+ /// A unified `Stream` and `Sink` interface to an underlying I/O object, using the `Encoder` and
+ /// `Decoder` traits to encode and decode frames.
+ ///
+ /// Raw I/O objects work with byte sequences, but higher-level code usually wants to batch these
+ /// into meaningful chunks, called "frames". This method layers framing on top of an I/O object,
+ /// by using the `Encoder`/`Decoder` traits to handle encoding and decoding of message frames.
+ /// Note that the incoming and outgoing frame types may be distinct.
+ pub struct Framed<T, U> {
+ #[pin]
+ io: T,
+ codec: U,
+ flags: Flags,
+ read_buf: BytesMut,
+ write_buf: BytesMut,
+ }
+}
+
+impl<T, U> Framed<T, U>
+where
+ T: AsyncRead + AsyncWrite,
+ U: Decoder,
+{
+ /// This function returns a *single* object that is both `Stream` and `Sink`; grouping this into
+ /// a single object is often useful for layering things like gzip or TLS, which require both
+ /// read and write access to the underlying object.
+ pub fn new(io: T, codec: U) -> Framed<T, U> {
+ Framed {
+ io,
+ codec,
+ flags: Flags::empty(),
+ read_buf: BytesMut::with_capacity(HW),
+ write_buf: BytesMut::with_capacity(HW),
+ }
+ }
+}
+
+impl<T, U> Framed<T, U> {
+ /// Returns a reference to the underlying codec.
+ pub fn codec_ref(&self) -> &U {
+ &self.codec
+ }
+
+ /// Returns a mutable reference to the underlying codec.
+ pub fn codec_mut(&mut self) -> &mut U {
+ &mut self.codec
+ }
+
+ /// Returns a reference to the underlying I/O stream wrapped by `Frame`.
+ ///
+ /// Note that care should be taken to not tamper with the underlying stream of data coming in as
+ /// it may corrupt the stream of frames otherwise being worked with.
+ pub fn io_ref(&self) -> &T {
+ &self.io
+ }
+
+ /// Returns a mutable reference to the underlying I/O stream.
+ ///
+ /// Note that care should be taken to not tamper with the underlying stream of data coming in as
+ /// it may corrupt the stream of frames otherwise being worked with.
+ pub fn io_mut(&mut self) -> &mut T {
+ &mut self.io
+ }
+
+ /// Returns a `Pin` of a mutable reference to the underlying I/O stream.
+ pub fn io_pin(self: Pin<&mut Self>) -> Pin<&mut T> {
+ self.project().io
+ }
+
+ /// Check if read buffer is empty.
+ pub fn is_read_buf_empty(&self) -> bool {
+ self.read_buf.is_empty()
+ }
+
+ /// Check if write buffer is empty.
+ pub fn is_write_buf_empty(&self) -> bool {
+ self.write_buf.is_empty()
+ }
+
+ /// Check if write buffer is full.
+ pub fn is_write_buf_full(&self) -> bool {
+ self.write_buf.len() >= HW
+ }
+
+ /// Check if framed is able to write more data.
+ ///
+ /// `Framed` object considers ready if there is free space in write buffer.
+ pub fn is_write_ready(&self) -> bool {
+ self.write_buf.len() < HW
+ }
+
+ /// Consume the `Frame`, returning `Frame` with different codec.
+ pub fn replace_codec<U2>(self, codec: U2) -> Framed<T, U2> {
+ Framed {
+ codec,
+ io: self.io,
+ flags: self.flags,
+ read_buf: self.read_buf,
+ write_buf: self.write_buf,
+ }
+ }
+
+ /// Consume the `Frame`, returning `Frame` with different io.
+ pub fn into_map_io<F, T2>(self, f: F) -> Framed<T2, U>
+ where
+ F: Fn(T) -> T2,
+ {
+ Framed {
+ io: f(self.io),
+ codec: self.codec,
+ flags: self.flags,
+ read_buf: self.read_buf,
+ write_buf: self.write_buf,
+ }
+ }
+
+ /// Consume the `Frame`, returning `Frame` with different codec.
+ pub fn into_map_codec<F, U2>(self, f: F) -> Framed<T, U2>
+ where
+ F: Fn(U) -> U2,
+ {
+ Framed {
+ io: self.io,
+ codec: f(self.codec),
+ flags: self.flags,
+ read_buf: self.read_buf,
+ write_buf: self.write_buf,
+ }
+ }
+}
+
+impl<T, U> Framed<T, U> {
+ /// Serialize item and write to the inner buffer
+ pub fn write<I>(mut self: Pin<&mut Self>, item: I) -> Result<(), <U as Encoder<I>>::Error>
+ where
+ T: AsyncWrite,
+ U: Encoder<I>,
+ {
+ let this = self.as_mut().project();
+ let remaining = this.write_buf.capacity() - this.write_buf.len();
+ if remaining < LW {
+ this.write_buf.reserve(HW - remaining);
+ }
+
+ this.codec.encode(item, this.write_buf)?;
+ Ok(())
+ }
+
+ /// Try to read underlying I/O stream and decode item.
+ pub fn next_item(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ ) -> Poll<Option<Result<<U as Decoder>::Item, U::Error>>>
+ where
+ T: AsyncRead,
+ U: Decoder,
+ {
+ loop {
+ let this = self.as_mut().project();
+ // Repeatedly call `decode` or `decode_eof` as long as it is "readable". Readable is
+ // defined as not having returned `None`. If the upstream has returned EOF, and the
+ // decoder is no longer readable, it can be assumed that the decoder will never become
+ // readable again, at which point the stream is terminated.
+
+ if this.flags.contains(Flags::READABLE) {
+ if this.flags.contains(Flags::EOF) {
+ match this.codec.decode_eof(this.read_buf) {
+ Ok(Some(frame)) => return Poll::Ready(Some(Ok(frame))),
+ Ok(None) => return Poll::Ready(None),
+ Err(err) => return Poll::Ready(Some(Err(err))),
+ }
+ }
+
+ tracing::trace!("attempting to decode a frame");
+
+ match this.codec.decode(this.read_buf) {
+ Ok(Some(frame)) => {
+ tracing::trace!("frame decoded from buffer");
+ return Poll::Ready(Some(Ok(frame)));
+ }
+ Err(err) => return Poll::Ready(Some(Err(err))),
+ _ => (), // Need more data
+ }
+
+ this.flags.remove(Flags::READABLE);
+ }
+
+ debug_assert!(!this.flags.contains(Flags::EOF));
+
+ // Otherwise, try to read more data and try again. Make sure we've got room.
+ let remaining = this.read_buf.capacity() - this.read_buf.len();
+ if remaining < LW {
+ this.read_buf.reserve(HW - remaining)
+ }
+
+ let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) {
+ Poll::Pending => return Poll::Pending,
+ Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
+ Poll::Ready(Ok(cnt)) => cnt,
+ };
+
+ if cnt == 0 {
+ this.flags.insert(Flags::EOF);
+ }
+ this.flags.insert(Flags::READABLE);
+ }
+ }
+
+ /// Flush write buffer to underlying I/O stream.
+ pub fn flush<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
+ where
+ T: AsyncWrite,
+ U: Encoder<I>,
+ {
+ let mut this = self.as_mut().project();
+ tracing::trace!("flushing framed transport");
+
+ while !this.write_buf.is_empty() {
+ tracing::trace!("writing; remaining={}", this.write_buf.len());
+
+ let n = ready!(this.io.as_mut().poll_write(cx, this.write_buf))?;
+
+ if n == 0 {
+ return Poll::Ready(Err(io::Error::new(
+ io::ErrorKind::WriteZero,
+ "failed to write frame to transport",
+ )
+ .into()));
+ }
+
+ // remove written data
+ this.write_buf.advance(n);
+ }
+
+ // Try flushing the underlying IO
+ ready!(this.io.poll_flush(cx))?;
+
+ tracing::trace!("framed transport flushed");
+ Poll::Ready(Ok(()))
+ }
+
+ /// Flush write buffer and shutdown underlying I/O stream.
+ pub fn close<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
+ where
+ T: AsyncWrite,
+ U: Encoder<I>,
+ {
+ let mut this = self.as_mut().project();
+ ready!(this.io.as_mut().poll_flush(cx))?;
+ ready!(this.io.as_mut().poll_shutdown(cx))?;
+ Poll::Ready(Ok(()))
+ }
+}
+
+impl<T, U> Stream for Framed<T, U>
+where
+ T: AsyncRead,
+ U: Decoder,
+{
+ type Item = Result<U::Item, U::Error>;
+
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ self.next_item(cx)
+ }
+}
+
+impl<T, U, I> Sink<I> for Framed<T, U>
+where
+ T: AsyncWrite,
+ U: Encoder<I>,
+ U::Error: From<io::Error>,
+{
+ type Error = U::Error;
+
+ fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ if self.is_write_ready() {
+ Poll::Ready(Ok(()))
+ } else {
+ self.flush(cx)
+ }
+ }
+
+ fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
+ self.write(item)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.flush(cx)
+ }
+
+ fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.close(cx)
+ }
+}
+
+impl<T, U> fmt::Debug for Framed<T, U>
+where
+ T: fmt::Debug,
+ U: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Framed")
+ .field("io", &self.io)
+ .field("codec", &self.codec)
+ .finish()
+ }
+}
+
+impl<T, U> Framed<T, U> {
+ /// This function returns a *single* object that is both `Stream` and `Sink`; grouping this into
+ /// a single object is often useful for layering things like gzip or TLS, which require both
+ /// read and write access to the underlying object.
+ ///
+ /// These objects take a stream, a read buffer and a write buffer. These fields can be obtained
+ /// from an existing `Framed` with the `into_parts` method.
+ pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
+ Framed {
+ io: parts.io,
+ codec: parts.codec,
+ flags: parts.flags,
+ write_buf: parts.write_buf,
+ read_buf: parts.read_buf,
+ }
+ }
+
+ /// Consumes the `Frame`, returning its underlying I/O stream, the buffer with unprocessed data,
+ /// and the codec.
+ ///
+ /// Note that care should be taken to not tamper with the underlying stream of data coming in as
+ /// it may corrupt the stream of frames otherwise being worked with.
+ pub fn into_parts(self) -> FramedParts<T, U> {
+ FramedParts {
+ io: self.io,
+ codec: self.codec,
+ flags: self.flags,
+ read_buf: self.read_buf,
+ write_buf: self.write_buf,
+ }
+ }
+}
+
+/// `FramedParts` contains an export of the data of a Framed transport.
+///
+/// It can be used to construct a new `Framed` with a different codec. It contains all current
+/// buffers and the inner transport.
+#[derive(Debug)]
+pub struct FramedParts<T, U> {
+ /// The inner transport used to read bytes to and write bytes to.
+ pub io: T,
+
+ /// The codec object.
+ pub codec: U,
+
+ /// The buffer with read but unprocessed data.
+ pub read_buf: BytesMut,
+
+ /// A buffer with unprocessed data which are not written yet.
+ pub write_buf: BytesMut,
+
+ flags: Flags,
+}
+
+impl<T, U> FramedParts<T, U> {
+ /// Creates a new default `FramedParts`.
+ pub fn new(io: T, codec: U) -> FramedParts<T, U> {
+ FramedParts {
+ io,
+ codec,
+ flags: Flags::empty(),
+ read_buf: BytesMut::new(),
+ write_buf: BytesMut::new(),
+ }
+ }
+
+ /// Creates a new `FramedParts` with read buffer.
+ pub fn with_read_buf(io: T, codec: U, read_buf: BytesMut) -> FramedParts<T, U> {
+ FramedParts {
+ io,
+ codec,
+ read_buf,
+ flags: Flags::empty(),
+ write_buf: BytesMut::new(),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +
//! Codec utilities for working with framed protocols.
+//!
+//! Contains adapters to go from streams of bytes, [`AsyncRead`] and [`AsyncWrite`], to framed
+//! streams implementing [`Sink`] and [`Stream`]. Framed streams are also known as `transports`.
+//!
+//! [`Sink`]: futures_sink::Sink
+//! [`Stream`]: futures_core::Stream
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible, missing_docs)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+
+pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+pub use tokio_util::{
+ codec::{Decoder, Encoder},
+ io::poll_read_buf,
+};
+
+mod bcodec;
+mod framed;
+mod lines;
+
+pub use self::{
+ bcodec::BytesCodec,
+ framed::{Framed, FramedParts},
+ lines::LinesCodec,
+};
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +
use std::io;
+
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+use memchr::memchr;
+
+use super::{Decoder, Encoder};
+
+/// Lines codec. Reads/writes line delimited strings.
+///
+/// Will split input up by LF or CRLF delimiters. Carriage return characters at the end of lines are
+/// not preserved.
+#[derive(Debug, Copy, Clone, Default)]
+#[non_exhaustive]
+pub struct LinesCodec;
+
+impl<T: AsRef<str>> Encoder<T> for LinesCodec {
+ type Error = io::Error;
+
+ #[inline]
+ fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> {
+ let item = item.as_ref();
+ dst.reserve(item.len() + 1);
+ dst.put_slice(item.as_bytes());
+ dst.put_u8(b'\n');
+ Ok(())
+ }
+}
+
+impl Decoder for LinesCodec {
+ type Item = String;
+ type Error = io::Error;
+
+ fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+ if src.is_empty() {
+ return Ok(None);
+ }
+
+ let len = match memchr(b'\n', src) {
+ Some(n) => n,
+ None => {
+ return Ok(None);
+ }
+ };
+
+ // split up to new line char
+ let mut buf = src.split_to(len);
+ debug_assert_eq!(len, buf.len());
+
+ // remove new line char from source
+ src.advance(1);
+
+ match buf.last() {
+ // remove carriage returns at the end of buf
+ Some(b'\r') => buf.truncate(len - 1),
+
+ // line is empty
+ None => return Ok(Some(String::new())),
+
+ _ => {}
+ }
+
+ try_into_utf8(buf.freeze())
+ }
+
+ fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+ match self.decode(src)? {
+ Some(frame) => Ok(Some(frame)),
+ None if src.is_empty() => Ok(None),
+ None => {
+ let buf = match src.last() {
+ // if last line ends in a CR then take everything up to it
+ Some(b'\r') => src.split_to(src.len() - 1),
+
+ // take all bytes from source
+ _ => src.split(),
+ };
+
+ if buf.is_empty() {
+ return Ok(None);
+ }
+
+ try_into_utf8(buf.freeze())
+ }
+ }
+ }
+}
+
+// Attempts to convert bytes into a `String`.
+fn try_into_utf8(buf: Bytes) -> io::Result<Option<String>> {
+ String::from_utf8(buf.to_vec())
+ .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
+ .map(Some)
+}
+
+#[cfg(test)]
+mod tests {
+ use bytes::BufMut as _;
+
+ use super::*;
+
+ #[test]
+ fn lines_decoder() {
+ let mut codec = LinesCodec::default();
+ let mut buf = BytesMut::from("\nline 1\nline 2\r\nline 3\n\r\n\r");
+
+ assert_eq!("", codec.decode(&mut buf).unwrap().unwrap());
+ assert_eq!("line 1", codec.decode(&mut buf).unwrap().unwrap());
+ assert_eq!("line 2", codec.decode(&mut buf).unwrap().unwrap());
+ assert_eq!("line 3", codec.decode(&mut buf).unwrap().unwrap());
+ assert_eq!("", codec.decode(&mut buf).unwrap().unwrap());
+ assert!(codec.decode(&mut buf).unwrap().is_none());
+ assert!(codec.decode_eof(&mut buf).unwrap().is_none());
+
+ buf.put_slice(b"k");
+ assert!(codec.decode(&mut buf).unwrap().is_none());
+ assert_eq!("\rk", codec.decode_eof(&mut buf).unwrap().unwrap());
+
+ assert!(codec.decode(&mut buf).unwrap().is_none());
+ assert!(codec.decode_eof(&mut buf).unwrap().is_none());
+ }
+
+ #[test]
+ fn lines_encoder() {
+ let mut codec = LinesCodec::default();
+
+ let mut buf = BytesMut::new();
+
+ codec.encode("", &mut buf).unwrap();
+ assert_eq!(&buf[..], b"\n");
+
+ codec.encode("test", &mut buf).unwrap();
+ assert_eq!(&buf[..], b"\ntest\n");
+
+ codec.encode("a\nb", &mut buf).unwrap();
+ assert_eq!(&buf[..], b"\ntest\na\nb\n");
+ }
+
+ #[test]
+ fn lines_encoder_no_overflow() {
+ let mut codec = LinesCodec::default();
+
+ let mut buf = BytesMut::new();
+ codec.encode("1234567", &mut buf).unwrap();
+ assert_eq!(&buf[..], b"1234567\n");
+
+ let mut buf = BytesMut::new();
+ codec.encode("12345678", &mut buf).unwrap();
+ assert_eq!(&buf[..], b"12345678\n");
+
+ let mut buf = BytesMut::new();
+ codec.encode("123456789111213", &mut buf).unwrap();
+ assert_eq!(&buf[..], b"123456789111213\n");
+
+ let mut buf = BytesMut::new();
+ codec.encode("1234567891112131", &mut buf).unwrap();
+ assert_eq!(&buf[..], b"1234567891112131\n");
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +
//! Macros for Actix system and runtime.
+//!
+//! The [`actix-rt`](https://docs.rs/actix-rt) crate must be available for macro output to compile.
+//!
+//! # Entry-point
+//! See docs for the [`#[main]`](macro@main) macro.
+//!
+//! # Tests
+//! See docs for the [`#[test]`](macro@test) macro.
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::parse::Parser as _;
+
+type AttributeArgs = syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>;
+
+/// Marks async entry-point function to be executed by Actix system.
+///
+/// # Examples
+/// ```
+/// #[actix_rt::main]
+/// async fn main() {
+/// println!("Hello world");
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
+ let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
+ Ok(input) => input,
+ // on parse err, make IDEs happy; see fn docs
+ Err(err) => return input_and_compile_error(item, err),
+ };
+
+ let parser = AttributeArgs::parse_terminated;
+ let args = match parser.parse(args.clone()) {
+ Ok(args) => args,
+ Err(err) => return input_and_compile_error(args, err),
+ };
+
+ let attrs = &input.attrs;
+ let vis = &input.vis;
+ let sig = &mut input.sig;
+ let body = &input.block;
+
+ if sig.asyncness.is_none() {
+ return syn::Error::new_spanned(
+ sig.fn_token,
+ "the async keyword is missing from the function declaration",
+ )
+ .to_compile_error()
+ .into();
+ }
+
+ let mut system = syn::parse_str::<syn::Path>("::actix_rt::System").unwrap();
+
+ for arg in &args {
+ match arg {
+ syn::Meta::NameValue(syn::MetaNameValue {
+ path,
+ value:
+ syn::Expr::Lit(syn::ExprLit {
+ lit: syn::Lit::Str(lit),
+ ..
+ }),
+ ..
+ }) => match path
+ .get_ident()
+ .map(|i| i.to_string().to_lowercase())
+ .as_deref()
+ {
+ Some("system") => match lit.parse() {
+ Ok(path) => system = path,
+ Err(_) => {
+ return syn::Error::new_spanned(lit, "Expected path")
+ .to_compile_error()
+ .into();
+ }
+ },
+ _ => {
+ return syn::Error::new_spanned(arg, "Unknown attribute specified")
+ .to_compile_error()
+ .into();
+ }
+ },
+
+ _ => {
+ return syn::Error::new_spanned(arg, "Unknown attribute specified")
+ .to_compile_error()
+ .into();
+ }
+ }
+ }
+
+ sig.asyncness = None;
+
+ (quote! {
+ #(#attrs)*
+ #vis #sig {
+ <#system>::new().block_on(async move { #body })
+ }
+ })
+ .into()
+}
+
+/// Marks async test function to be executed in an Actix system.
+///
+/// # Examples
+/// ```
+/// #[actix_rt::test]
+/// async fn my_test() {
+/// assert!(true);
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
+ let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
+ Ok(input) => input,
+ // on parse err, make IDEs happy; see fn docs
+ Err(err) => return input_and_compile_error(item, err),
+ };
+
+ let parser = AttributeArgs::parse_terminated;
+ let args = match parser.parse(args.clone()) {
+ Ok(args) => args,
+ Err(err) => return input_and_compile_error(args, err),
+ };
+
+ let attrs = &input.attrs;
+ let vis = &input.vis;
+ let sig = &mut input.sig;
+ let body = &input.block;
+ let mut has_test_attr = false;
+
+ for attr in attrs {
+ if attr.path().is_ident("test") {
+ has_test_attr = true;
+ }
+ }
+
+ if sig.asyncness.is_none() {
+ return syn::Error::new_spanned(
+ input.sig.fn_token,
+ "the async keyword is missing from the function declaration",
+ )
+ .to_compile_error()
+ .into();
+ }
+
+ sig.asyncness = None;
+
+ let missing_test_attr = if has_test_attr {
+ quote! {}
+ } else {
+ quote! { #[::core::prelude::v1::test] }
+ };
+
+ let mut system = syn::parse_str::<syn::Path>("::actix_rt::System").unwrap();
+
+ for arg in &args {
+ match arg {
+ syn::Meta::NameValue(syn::MetaNameValue {
+ path,
+ value:
+ syn::Expr::Lit(syn::ExprLit {
+ lit: syn::Lit::Str(lit),
+ ..
+ }),
+ ..
+ }) => match path
+ .get_ident()
+ .map(|i| i.to_string().to_lowercase())
+ .as_deref()
+ {
+ Some("system") => match lit.parse() {
+ Ok(path) => system = path,
+ Err(_) => {
+ return syn::Error::new_spanned(lit, "Expected path")
+ .to_compile_error()
+ .into();
+ }
+ },
+ _ => {
+ return syn::Error::new_spanned(arg, "Unknown attribute specified")
+ .to_compile_error()
+ .into();
+ }
+ },
+ _ => {
+ return syn::Error::new_spanned(arg, "Unknown attribute specified")
+ .to_compile_error()
+ .into();
+ }
+ }
+ }
+
+ (quote! {
+ #missing_test_attr
+ #(#attrs)*
+ #vis #sig {
+ <#system>::new().block_on(async { #body })
+ }
+ })
+ .into()
+}
+
+/// Converts the error to a token stream and appends it to the original input.
+///
+/// Returning the original input in addition to the error is good for IDEs which can gracefully
+/// recover and show more precise errors within the macro body.
+///
+/// See <https://github.com/rust-analyzer/rust-analyzer/issues/10468> for more info.
+fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream {
+ let compile_err = TokenStream::from(err.to_compile_error());
+ item.extend(compile_err);
+ item
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +
use std::{
+ cell::RefCell,
+ fmt,
+ future::Future,
+ pin::Pin,
+ sync::atomic::{AtomicUsize, Ordering},
+ task::{Context, Poll},
+ thread,
+};
+
+use futures_core::ready;
+use tokio::sync::mpsc;
+
+use crate::system::{System, SystemCommand};
+
+pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0);
+
+thread_local!(
+ static HANDLE: RefCell<Option<ArbiterHandle>> = const { RefCell::new(None) };
+);
+
+pub(crate) enum ArbiterCommand {
+ Stop,
+ Execute(Pin<Box<dyn Future<Output = ()> + Send>>),
+}
+
+impl fmt::Debug for ArbiterCommand {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ArbiterCommand::Stop => write!(f, "ArbiterCommand::Stop"),
+ ArbiterCommand::Execute(_) => write!(f, "ArbiterCommand::Execute"),
+ }
+ }
+}
+
+/// A handle for sending spawn and stop messages to an [Arbiter].
+#[derive(Debug, Clone)]
+pub struct ArbiterHandle {
+ tx: mpsc::UnboundedSender<ArbiterCommand>,
+}
+
+impl ArbiterHandle {
+ pub(crate) fn new(tx: mpsc::UnboundedSender<ArbiterCommand>) -> Self {
+ Self { tx }
+ }
+
+ /// Send a future to the [Arbiter]'s thread and spawn it.
+ ///
+ /// If you require a result, include a response channel in the future.
+ ///
+ /// Returns true if future was sent successfully and false if the [Arbiter] has died.
+ pub fn spawn<Fut>(&self, future: Fut) -> bool
+ where
+ Fut: Future<Output = ()> + Send + 'static,
+ {
+ self.tx
+ .send(ArbiterCommand::Execute(Box::pin(future)))
+ .is_ok()
+ }
+
+ /// Send a function to the [Arbiter]'s thread and execute it.
+ ///
+ /// Any result from the function is discarded. If you require a result, include a response
+ /// channel in the function.
+ ///
+ /// Returns true if function was sent successfully and false if the [Arbiter] has died.
+ pub fn spawn_fn<F>(&self, f: F) -> bool
+ where
+ F: FnOnce() + Send + 'static,
+ {
+ self.spawn(async { f() })
+ }
+
+ /// Instruct [Arbiter] to stop processing it's event loop.
+ ///
+ /// Returns true if stop message was sent successfully and false if the [Arbiter] has
+ /// been dropped.
+ pub fn stop(&self) -> bool {
+ self.tx.send(ArbiterCommand::Stop).is_ok()
+ }
+}
+
+/// An Arbiter represents a thread that provides an asynchronous execution environment for futures
+/// and functions.
+///
+/// When an arbiter is created, it spawns a new [OS thread](thread), and hosts an event loop.
+#[derive(Debug)]
+pub struct Arbiter {
+ tx: mpsc::UnboundedSender<ArbiterCommand>,
+ thread_handle: thread::JoinHandle<()>,
+}
+
+impl Arbiter {
+ /// Spawn a new Arbiter thread and start its event loop.
+ ///
+ /// # Panics
+ /// Panics if a [System] is not registered on the current thread.
+ #[cfg(not(all(target_os = "linux", feature = "io-uring")))]
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Arbiter {
+ Self::with_tokio_rt(|| {
+ crate::runtime::default_tokio_runtime().expect("Cannot create new Arbiter's Runtime.")
+ })
+ }
+
+ /// Spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure.
+ ///
+ /// [tokio-runtime]: tokio::runtime::Runtime
+ #[cfg(not(all(target_os = "linux", feature = "io-uring")))]
+ pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
+ where
+ F: Fn() -> tokio::runtime::Runtime + Send + 'static,
+ {
+ let sys = System::current();
+ let system_id = sys.id();
+ let arb_id = COUNT.fetch_add(1, Ordering::Relaxed);
+
+ let name = format!("actix-rt|system:{}|arbiter:{}", system_id, arb_id);
+ let (tx, rx) = mpsc::unbounded_channel();
+
+ let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>();
+
+ let thread_handle = thread::Builder::new()
+ .name(name.clone())
+ .spawn({
+ let tx = tx.clone();
+ move || {
+ let rt = crate::runtime::Runtime::from(runtime_factory());
+ let hnd = ArbiterHandle::new(tx);
+
+ System::set_current(sys);
+
+ HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone()));
+
+ // register arbiter
+ let _ = System::current()
+ .tx()
+ .send(SystemCommand::RegisterArbiter(arb_id, hnd));
+
+ ready_tx.send(()).unwrap();
+
+ // run arbiter event processing loop
+ rt.block_on(ArbiterRunner { rx });
+
+ // deregister arbiter
+ let _ = System::current()
+ .tx()
+ .send(SystemCommand::DeregisterArbiter(arb_id));
+ }
+ })
+ .unwrap_or_else(|err| panic!("Cannot spawn Arbiter's thread: {name:?}: {err:?}"));
+
+ ready_rx.recv().unwrap();
+
+ Arbiter { tx, thread_handle }
+ }
+
+ /// Spawn a new Arbiter thread and start its event loop with `tokio-uring` runtime.
+ ///
+ /// # Panics
+ /// Panics if a [System] is not registered on the current thread.
+ #[cfg(all(target_os = "linux", feature = "io-uring"))]
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Arbiter {
+ let sys = System::current();
+ let system_id = sys.id();
+ let arb_id = COUNT.fetch_add(1, Ordering::Relaxed);
+
+ let name = format!("actix-rt|system:{}|arbiter:{}", system_id, arb_id);
+ let (tx, rx) = mpsc::unbounded_channel();
+
+ let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>();
+
+ let thread_handle = thread::Builder::new()
+ .name(name.clone())
+ .spawn({
+ let tx = tx.clone();
+ move || {
+ let hnd = ArbiterHandle::new(tx);
+
+ System::set_current(sys);
+
+ HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone()));
+
+ // register arbiter
+ let _ = System::current()
+ .tx()
+ .send(SystemCommand::RegisterArbiter(arb_id, hnd));
+
+ ready_tx.send(()).unwrap();
+
+ // run arbiter event processing loop
+ tokio_uring::start(ArbiterRunner { rx });
+
+ // deregister arbiter
+ let _ = System::current()
+ .tx()
+ .send(SystemCommand::DeregisterArbiter(arb_id));
+ }
+ })
+ .unwrap_or_else(|err| panic!("Cannot spawn Arbiter's thread: {name:?}: {err:?}"));
+
+ ready_rx.recv().unwrap();
+
+ Arbiter { tx, thread_handle }
+ }
+
+ /// Sets up an Arbiter runner in a new System using the environment's local set.
+ pub(crate) fn in_new_system() -> ArbiterHandle {
+ let (tx, rx) = mpsc::unbounded_channel();
+
+ let hnd = ArbiterHandle::new(tx);
+
+ HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone()));
+
+ crate::spawn(ArbiterRunner { rx });
+
+ hnd
+ }
+
+ /// Return a handle to the this Arbiter's message sender.
+ pub fn handle(&self) -> ArbiterHandle {
+ ArbiterHandle::new(self.tx.clone())
+ }
+
+ /// Return a handle to the current thread's Arbiter's message sender.
+ ///
+ /// # Panics
+ /// Panics if no Arbiter is running on the current thread.
+ pub fn current() -> ArbiterHandle {
+ HANDLE.with(|cell| match *cell.borrow() {
+ Some(ref hnd) => hnd.clone(),
+ None => panic!("Arbiter is not running."),
+ })
+ }
+
+ /// Try to get current running arbiter handle.
+ ///
+ /// Returns `None` if no Arbiter has been started.
+ ///
+ /// Unlike [`current`](Self::current), this never panics.
+ pub fn try_current() -> Option<ArbiterHandle> {
+ HANDLE.with(|cell| cell.borrow().clone())
+ }
+
+ /// Stop Arbiter from continuing it's event loop.
+ ///
+ /// Returns true if stop message was sent successfully and false if the Arbiter has been dropped.
+ pub fn stop(&self) -> bool {
+ self.tx.send(ArbiterCommand::Stop).is_ok()
+ }
+
+ /// Send a future to the Arbiter's thread and spawn it.
+ ///
+ /// If you require a result, include a response channel in the future.
+ ///
+ /// Returns true if future was sent successfully and false if the Arbiter has died.
+ #[track_caller]
+ pub fn spawn<Fut>(&self, future: Fut) -> bool
+ where
+ Fut: Future<Output = ()> + Send + 'static,
+ {
+ self.tx
+ .send(ArbiterCommand::Execute(Box::pin(future)))
+ .is_ok()
+ }
+
+ /// Send a function to the Arbiter's thread and execute it.
+ ///
+ /// Any result from the function is discarded. If you require a result, include a response
+ /// channel in the function.
+ ///
+ /// Returns true if function was sent successfully and false if the Arbiter has died.
+ #[track_caller]
+ pub fn spawn_fn<F>(&self, f: F) -> bool
+ where
+ F: FnOnce() + Send + 'static,
+ {
+ self.spawn(async { f() })
+ }
+
+ /// Wait for Arbiter's event loop to complete.
+ ///
+ /// Joins the underlying OS thread handle. See [`JoinHandle::join`](thread::JoinHandle::join).
+ pub fn join(self) -> thread::Result<()> {
+ self.thread_handle.join()
+ }
+}
+
+/// A persistent future that processes [Arbiter] commands.
+struct ArbiterRunner {
+ rx: mpsc::UnboundedReceiver<ArbiterCommand>,
+}
+
+impl Future for ArbiterRunner {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ // process all items currently buffered in channel
+ loop {
+ match ready!(self.rx.poll_recv(cx)) {
+ // channel closed; no more messages can be received
+ None => return Poll::Ready(()),
+
+ // process arbiter command
+ Some(item) => match item {
+ ArbiterCommand::Stop => {
+ return Poll::Ready(());
+ }
+ ArbiterCommand::Execute(task_fut) => {
+ tokio::task::spawn_local(task_fut);
+ }
+ },
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +
//! Tokio-based single-threaded async runtime for the Actix ecosystem.
+//!
+//! In most parts of the the Actix ecosystem, it has been chosen to use !Send futures. For this
+//! reason, a single-threaded runtime is appropriate since it is guaranteed that futures will not
+//! be moved between threads. This can result in small performance improvements over cases where
+//! atomics would otherwise be needed.
+//!
+//! To achieve similar performance to multi-threaded, work-stealing runtimes, applications
+//! using `actix-rt` will create multiple, mostly disconnected, single-threaded runtimes.
+//! This approach has good performance characteristics for workloads where the majority of tasks
+//! have similar runtime expense.
+//!
+//! The disadvantage is that idle threads will not steal work from very busy, stuck or otherwise
+//! backlogged threads. Tasks that are disproportionately expensive should be offloaded to the
+//! blocking task thread-pool using [`task::spawn_blocking`].
+//!
+//! # Examples
+//! ```no_run
+//! use std::sync::mpsc;
+//! use actix_rt::{Arbiter, System};
+//!
+//! let _ = System::new();
+//!
+//! let (tx, rx) = mpsc::channel::<u32>();
+//!
+//! let arbiter = Arbiter::new();
+//! arbiter.spawn_fn(move || tx.send(42).unwrap());
+//!
+//! let num = rx.recv().unwrap();
+//! assert_eq!(num, 42);
+//!
+//! arbiter.stop();
+//! arbiter.join().unwrap();
+//! ```
+//!
+//! # `io-uring` Support
+//!
+//! There is experimental support for using io-uring with this crate by enabling the
+//! `io-uring` feature. For now, it is semver exempt.
+//!
+//! Note that there are currently some unimplemented parts of using `actix-rt` with `io-uring`.
+//! In particular, when running a `System`, only `System::block_on` is supported.
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible, missing_docs)]
+#![allow(clippy::type_complexity)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+
+#[cfg(all(not(target_os = "linux"), feature = "io-uring"))]
+compile_error!("io_uring is a linux only feature.");
+
+use std::future::Future;
+
+// Cannot define a main macro when compiled into test harness.
+// Workaround for https://github.com/rust-lang/rust/issues/62127.
+#[cfg(all(feature = "macros", not(test)))]
+pub use actix_macros::main;
+#[cfg(feature = "macros")]
+pub use actix_macros::test;
+
+mod arbiter;
+mod runtime;
+mod system;
+
+pub use tokio::pin;
+use tokio::task::JoinHandle;
+
+pub use self::{
+ arbiter::{Arbiter, ArbiterHandle},
+ runtime::Runtime,
+ system::{System, SystemRunner},
+};
+
+pub mod signal {
+ //! Asynchronous signal handling (Tokio re-exports).
+
+ #[cfg(unix)]
+ pub mod unix {
+ //! Unix specific signals (Tokio re-exports).
+ pub use tokio::signal::unix::*;
+ }
+ pub use tokio::signal::ctrl_c;
+}
+
+pub mod net {
+ //! TCP/UDP/Unix bindings (mostly Tokio re-exports).
+
+ use std::{
+ future::Future,
+ io,
+ task::{Context, Poll},
+ };
+
+ use tokio::io::{AsyncRead, AsyncWrite, Interest};
+ #[cfg(unix)]
+ pub use tokio::net::{UnixDatagram, UnixListener, UnixStream};
+ pub use tokio::{
+ io::Ready,
+ net::{TcpListener, TcpSocket, TcpStream, UdpSocket},
+ };
+
+ /// Extension trait over async read+write types that can also signal readiness.
+ #[doc(hidden)]
+ pub trait ActixStream: AsyncRead + AsyncWrite + Unpin {
+ /// Poll stream and check read readiness of Self.
+ ///
+ /// See [tokio::net::TcpStream::poll_read_ready] for detail on intended use.
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>>;
+
+ /// Poll stream and check write readiness of Self.
+ ///
+ /// See [tokio::net::TcpStream::poll_write_ready] for detail on intended use.
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>>;
+ }
+
+ impl ActixStream for TcpStream {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ let ready = self.ready(Interest::READABLE);
+ tokio::pin!(ready);
+ ready.poll(cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ let ready = self.ready(Interest::WRITABLE);
+ tokio::pin!(ready);
+ ready.poll(cx)
+ }
+ }
+
+ #[cfg(unix)]
+ impl ActixStream for UnixStream {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ let ready = self.ready(Interest::READABLE);
+ tokio::pin!(ready);
+ ready.poll(cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ let ready = self.ready(Interest::WRITABLE);
+ tokio::pin!(ready);
+ ready.poll(cx)
+ }
+ }
+
+ impl<Io: ActixStream + ?Sized> ActixStream for Box<Io> {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ (**self).poll_read_ready(cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ (**self).poll_write_ready(cx)
+ }
+ }
+}
+
+pub mod time {
+ //! Utilities for tracking time (Tokio re-exports).
+
+ pub use tokio::time::{
+ interval, interval_at, sleep, sleep_until, timeout, Instant, Interval, Sleep, Timeout,
+ };
+}
+
+pub mod task {
+ //! Task management (Tokio re-exports).
+
+ pub use tokio::task::{spawn_blocking, yield_now, JoinError, JoinHandle};
+}
+
+/// Spawns a future on the current thread as a new task.
+///
+/// If not immediately awaited, the task can be cancelled using [`JoinHandle::abort`].
+///
+/// The provided future is spawned as a new task; therefore, panics are caught.
+///
+/// # Panics
+/// Panics if Actix system is not running.
+///
+/// # Examples
+/// ```
+/// # use std::time::Duration;
+/// # actix_rt::Runtime::new().unwrap().block_on(async {
+/// // task resolves successfully
+/// assert_eq!(actix_rt::spawn(async { 1 }).await.unwrap(), 1);
+///
+/// // task panics
+/// assert!(actix_rt::spawn(async {
+/// panic!("panic is caught at task boundary");
+/// })
+/// .await
+/// .unwrap_err()
+/// .is_panic());
+///
+/// // task is cancelled before completion
+/// let handle = actix_rt::spawn(actix_rt::time::sleep(Duration::from_secs(100)));
+/// handle.abort();
+/// assert!(handle.await.unwrap_err().is_cancelled());
+/// # });
+/// ```
+#[track_caller]
+#[inline]
+pub fn spawn<Fut>(f: Fut) -> JoinHandle<Fut::Output>
+where
+ Fut: Future + 'static,
+ Fut::Output: 'static,
+{
+ tokio::task::spawn_local(f)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +
use std::{future::Future, io};
+
+use tokio::task::{JoinHandle, LocalSet};
+
+/// A Tokio-based runtime proxy.
+///
+/// All spawned futures will be executed on the current thread. Therefore, there is no `Send` bound
+/// on submitted futures.
+#[derive(Debug)]
+pub struct Runtime {
+ local: LocalSet,
+ rt: tokio::runtime::Runtime,
+}
+
+pub(crate) fn default_tokio_runtime() -> io::Result<tokio::runtime::Runtime> {
+ tokio::runtime::Builder::new_current_thread()
+ .enable_io()
+ .enable_time()
+ .build()
+}
+
+impl Runtime {
+ /// Returns a new runtime initialized with default configuration values.
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new() -> io::Result<Self> {
+ let rt = default_tokio_runtime()?;
+
+ Ok(Runtime {
+ rt,
+ local: LocalSet::new(),
+ })
+ }
+
+ /// Offload a future onto the single-threaded runtime.
+ ///
+ /// The returned join handle can be used to await the future's result.
+ ///
+ /// See [crate root][crate] documentation for more details.
+ ///
+ /// # Examples
+ /// ```
+ /// let rt = actix_rt::Runtime::new().unwrap();
+ ///
+ /// // Spawn a future onto the runtime
+ /// let handle = rt.spawn(async {
+ /// println!("running on the runtime");
+ /// 42
+ /// });
+ ///
+ /// assert_eq!(rt.block_on(handle).unwrap(), 42);
+ /// ```
+ ///
+ /// # Panics
+ /// This function panics if the spawn fails. Failure occurs if the executor is currently at
+ /// capacity and is unable to spawn a new future.
+ #[track_caller]
+ pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
+ where
+ F: Future + 'static,
+ {
+ self.local.spawn_local(future)
+ }
+
+ /// Retrieves a reference to the underlying Tokio runtime associated with this instance.
+ ///
+ /// The Tokio runtime is responsible for executing asynchronous tasks and managing
+ /// the event loop for an asynchronous Rust program. This method allows accessing
+ /// the runtime to interact with its features directly.
+ ///
+ /// In a typical use case, you might need to share the same runtime between different
+ /// modules of your project. For example, a module might require a `tokio::runtime::Handle`
+ /// to spawn tasks on the same runtime, or the runtime itself to configure more complex
+ /// behaviours.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use actix_rt::Runtime;
+ ///
+ /// mod module_a {
+ /// pub fn do_something(handle: tokio::runtime::Handle) {
+ /// handle.spawn(async {
+ /// // Some asynchronous task here
+ /// });
+ /// }
+ /// }
+ ///
+ /// mod module_b {
+ /// pub fn do_something_else(rt: &tokio::runtime::Runtime) {
+ /// rt.spawn(async {
+ /// // Another asynchronous task here
+ /// });
+ /// }
+ /// }
+ ///
+ /// let actix_runtime = actix_rt::Runtime::new().unwrap();
+ /// let tokio_runtime = actix_runtime.tokio_runtime();
+ ///
+ /// let handle = tokio_runtime.handle().clone();
+ ///
+ /// module_a::do_something(handle);
+ /// module_b::do_something_else(tokio_runtime);
+ /// ```
+ ///
+ /// # Returns
+ ///
+ /// An immutable reference to the `tokio::runtime::Runtime` instance associated with this
+ /// `Runtime` instance.
+ ///
+ /// # Note
+ ///
+ /// While this method provides an immutable reference to the Tokio runtime, which is safe to share across threads,
+ /// be aware that spawning blocking tasks on the Tokio runtime could potentially impact the execution
+ /// of the Actix runtime. This is because Tokio is responsible for driving the Actix system,
+ /// and blocking tasks could delay or deadlock other tasks in run loop.
+ pub fn tokio_runtime(&self) -> &tokio::runtime::Runtime {
+ &self.rt
+ }
+
+ /// Runs the provided future, blocking the current thread until the future completes.
+ ///
+ /// This function can be used to synchronously block the current thread until the provided
+ /// `future` has resolved either successfully or with an error. The result of the future is
+ /// then returned from this function call.
+ ///
+ /// Note that this function will also execute any spawned futures on the current thread, but
+ /// will not block until these other spawned futures have completed. Once the function returns,
+ /// any uncompleted futures remain pending in the `Runtime` instance. These futures will not run
+ /// until `block_on` or `run` is called again.
+ ///
+ /// The caller is responsible for ensuring that other spawned futures complete execution by
+ /// calling `block_on` or `run`.
+ #[track_caller]
+ pub fn block_on<F>(&self, f: F) -> F::Output
+ where
+ F: Future,
+ {
+ self.local.block_on(&self.rt, f)
+ }
+}
+
+impl From<tokio::runtime::Runtime> for Runtime {
+ fn from(rt: tokio::runtime::Runtime) -> Self {
+ Self {
+ local: LocalSet::new(),
+ rt,
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +
use std::{
+ cell::RefCell,
+ collections::HashMap,
+ future::Future,
+ io,
+ pin::Pin,
+ sync::atomic::{AtomicUsize, Ordering},
+ task::{Context, Poll},
+};
+
+use futures_core::ready;
+use tokio::sync::{mpsc, oneshot};
+
+use crate::{arbiter::ArbiterHandle, Arbiter};
+
+static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
+
+thread_local!(
+ static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
+);
+
+/// A manager for a per-thread distributed async runtime.
+#[derive(Clone, Debug)]
+pub struct System {
+ id: usize,
+ sys_tx: mpsc::UnboundedSender<SystemCommand>,
+
+ /// Handle to the first [Arbiter] that is created with the System.
+ arbiter_handle: ArbiterHandle,
+}
+
+#[cfg(not(feature = "io-uring"))]
+impl System {
+ /// Create a new system.
+ ///
+ /// # Panics
+ /// Panics if underlying Tokio runtime can not be created.
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new() -> SystemRunner {
+ Self::with_tokio_rt(|| {
+ crate::runtime::default_tokio_runtime()
+ .expect("Default Actix (Tokio) runtime could not be created.")
+ })
+ }
+
+ /// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure.
+ ///
+ /// [tokio-runtime]: tokio::runtime::Runtime
+ pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
+ where
+ F: Fn() -> tokio::runtime::Runtime,
+ {
+ let (stop_tx, stop_rx) = oneshot::channel();
+ let (sys_tx, sys_rx) = mpsc::unbounded_channel();
+
+ let rt = crate::runtime::Runtime::from(runtime_factory());
+ let sys_arbiter = rt.block_on(async { Arbiter::in_new_system() });
+ let system = System::construct(sys_tx, sys_arbiter.clone());
+
+ system
+ .tx()
+ .send(SystemCommand::RegisterArbiter(usize::MAX, sys_arbiter))
+ .unwrap();
+
+ // init background system arbiter
+ let sys_ctrl = SystemController::new(sys_rx, stop_tx);
+ rt.spawn(sys_ctrl);
+
+ SystemRunner { rt, stop_rx }
+ }
+}
+
+#[cfg(feature = "io-uring")]
+impl System {
+ /// Create a new system.
+ ///
+ /// # Panics
+ /// Panics if underlying Tokio runtime can not be created.
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new() -> SystemRunner {
+ SystemRunner
+ }
+
+ /// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure.
+ ///
+ /// [tokio-runtime]: tokio::runtime::Runtime
+ #[doc(hidden)]
+ pub fn with_tokio_rt<F>(_: F) -> SystemRunner
+ where
+ F: Fn() -> tokio::runtime::Runtime,
+ {
+ unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
+ }
+}
+
+impl System {
+ /// Constructs new system and registers it on the current thread.
+ pub(crate) fn construct(
+ sys_tx: mpsc::UnboundedSender<SystemCommand>,
+ arbiter_handle: ArbiterHandle,
+ ) -> Self {
+ let sys = System {
+ sys_tx,
+ arbiter_handle,
+ id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst),
+ };
+
+ System::set_current(sys.clone());
+
+ sys
+ }
+
+ /// Get current running system.
+ ///
+ /// # Panics
+ /// Panics if no system is registered on the current thread.
+ pub fn current() -> System {
+ CURRENT.with(|cell| match *cell.borrow() {
+ Some(ref sys) => sys.clone(),
+ None => panic!("System is not running"),
+ })
+ }
+
+ /// Try to get current running system.
+ ///
+ /// Returns `None` if no System has been started.
+ ///
+ /// Unlike [`current`](Self::current), this never panics.
+ pub fn try_current() -> Option<System> {
+ CURRENT.with(|cell| cell.borrow().clone())
+ }
+
+ /// Get handle to a the System's initial [Arbiter].
+ pub fn arbiter(&self) -> &ArbiterHandle {
+ &self.arbiter_handle
+ }
+
+ /// Check if there is a System registered on the current thread.
+ pub fn is_registered() -> bool {
+ CURRENT.with(|sys| sys.borrow().is_some())
+ }
+
+ /// Register given system on current thread.
+ #[doc(hidden)]
+ pub fn set_current(sys: System) {
+ CURRENT.with(|cell| {
+ *cell.borrow_mut() = Some(sys);
+ })
+ }
+
+ /// Numeric system identifier.
+ ///
+ /// Useful when using multiple Systems.
+ pub fn id(&self) -> usize {
+ self.id
+ }
+
+ /// Stop the system (with code 0).
+ pub fn stop(&self) {
+ self.stop_with_code(0)
+ }
+
+ /// Stop the system with a given exit code.
+ pub fn stop_with_code(&self, code: i32) {
+ let _ = self.sys_tx.send(SystemCommand::Exit(code));
+ }
+
+ pub(crate) fn tx(&self) -> &mpsc::UnboundedSender<SystemCommand> {
+ &self.sys_tx
+ }
+}
+
+/// Runner that keeps a [System]'s event loop alive until stop message is received.
+#[cfg(not(feature = "io-uring"))]
+#[must_use = "A SystemRunner does nothing unless `run` is called."]
+#[derive(Debug)]
+pub struct SystemRunner {
+ rt: crate::runtime::Runtime,
+ stop_rx: oneshot::Receiver<i32>,
+}
+
+#[cfg(not(feature = "io-uring"))]
+impl SystemRunner {
+ /// Starts event loop and will return once [System] is [stopped](System::stop).
+ pub fn run(self) -> io::Result<()> {
+ let exit_code = self.run_with_code()?;
+
+ match exit_code {
+ 0 => Ok(()),
+ nonzero => Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("Non-zero exit code: {}", nonzero),
+ )),
+ }
+ }
+
+ /// Runs the event loop until [stopped](System::stop_with_code), returning the exit code.
+ pub fn run_with_code(self) -> io::Result<i32> {
+ let SystemRunner { rt, stop_rx, .. } = self;
+
+ // run loop
+ rt.block_on(stop_rx)
+ .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
+ }
+
+ /// Retrieves a reference to the underlying [Actix runtime](crate::Runtime) associated with this
+ /// `SystemRunner` instance.
+ ///
+ /// The Actix runtime is responsible for managing the event loop for an Actix system and
+ /// executing asynchronous tasks. This method provides access to the runtime, allowing direct
+ /// interaction with its features.
+ ///
+ /// In a typical use case, you might need to share the same runtime between different
+ /// parts of your project. For example, some components might require a [`Runtime`] to spawn
+ /// tasks on the same runtime.
+ ///
+ /// Read more in the documentation for [`Runtime`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let system_runner = actix_rt::System::new();
+ /// let actix_runtime = system_runner.runtime();
+ ///
+ /// // Use the runtime to spawn an async task or perform other operations
+ /// ```
+ ///
+ /// # Note
+ ///
+ /// While this method provides an immutable reference to the Actix runtime, which is safe to
+ /// share across threads, be aware that spawning blocking tasks on the Actix runtime could
+ /// potentially impact system performance. This is because the Actix runtime is responsible for
+ /// driving the system, and blocking tasks could delay other tasks in the run loop.
+ ///
+ /// [`Runtime`]: crate::Runtime
+ pub fn runtime(&self) -> &crate::runtime::Runtime {
+ &self.rt
+ }
+
+ /// Runs the provided future, blocking the current thread until the future completes.
+ #[track_caller]
+ #[inline]
+ pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
+ self.rt.block_on(fut)
+ }
+}
+
+/// Runner that keeps a [System]'s event loop alive until stop message is received.
+#[cfg(feature = "io-uring")]
+#[must_use = "A SystemRunner does nothing unless `run` is called."]
+#[derive(Debug)]
+pub struct SystemRunner;
+
+#[cfg(feature = "io-uring")]
+impl SystemRunner {
+ /// Starts event loop and will return once [System] is [stopped](System::stop).
+ pub fn run(self) -> io::Result<()> {
+ unimplemented!("SystemRunner::run is not implemented for io-uring feature yet");
+ }
+
+ /// Runs the event loop until [stopped](System::stop_with_code), returning the exit code.
+ pub fn run_with_code(self) -> io::Result<i32> {
+ unimplemented!("SystemRunner::run_with_code is not implemented for io-uring feature yet");
+ }
+
+ /// Runs the provided future, blocking the current thread until the future completes.
+ #[inline]
+ pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
+ tokio_uring::start(async move {
+ let (stop_tx, stop_rx) = oneshot::channel();
+ let (sys_tx, sys_rx) = mpsc::unbounded_channel();
+
+ let sys_arbiter = Arbiter::in_new_system();
+ let system = System::construct(sys_tx, sys_arbiter.clone());
+
+ system
+ .tx()
+ .send(SystemCommand::RegisterArbiter(usize::MAX, sys_arbiter))
+ .unwrap();
+
+ // init background system arbiter
+ let sys_ctrl = SystemController::new(sys_rx, stop_tx);
+ tokio_uring::spawn(sys_ctrl);
+
+ let res = fut.await;
+ drop(stop_rx);
+ res
+ })
+ }
+}
+
+#[derive(Debug)]
+pub(crate) enum SystemCommand {
+ Exit(i32),
+ RegisterArbiter(usize, ArbiterHandle),
+ DeregisterArbiter(usize),
+}
+
+/// There is one `SystemController` per [System]. It runs in the background, keeping track of
+/// [Arbiter]s and is able to distribute a system-wide stop command.
+#[derive(Debug)]
+pub(crate) struct SystemController {
+ stop_tx: Option<oneshot::Sender<i32>>,
+ cmd_rx: mpsc::UnboundedReceiver<SystemCommand>,
+ arbiters: HashMap<usize, ArbiterHandle>,
+}
+
+impl SystemController {
+ pub(crate) fn new(
+ cmd_rx: mpsc::UnboundedReceiver<SystemCommand>,
+ stop_tx: oneshot::Sender<i32>,
+ ) -> Self {
+ SystemController {
+ cmd_rx,
+ stop_tx: Some(stop_tx),
+ arbiters: HashMap::with_capacity(4),
+ }
+ }
+}
+
+impl Future for SystemController {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ // process all items currently buffered in channel
+ loop {
+ match ready!(self.cmd_rx.poll_recv(cx)) {
+ // channel closed; no more messages can be received
+ None => return Poll::Ready(()),
+
+ // process system command
+ Some(cmd) => match cmd {
+ SystemCommand::Exit(code) => {
+ // stop all arbiters
+ for arb in self.arbiters.values() {
+ arb.stop();
+ }
+
+ // stop event loop
+ // will only fire once
+ if let Some(stop_tx) = self.stop_tx.take() {
+ let _ = stop_tx.send(code);
+ }
+ }
+
+ SystemCommand::RegisterArbiter(id, arb) => {
+ self.arbiters.insert(id, arb);
+ }
+
+ SystemCommand::DeregisterArbiter(id) => {
+ self.arbiters.remove(&id);
+ }
+ },
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +
use std::{io, thread, time::Duration};
+
+use actix_rt::time::Instant;
+use mio::{Interest, Poll, Token as MioToken};
+use tracing::{debug, error, info};
+
+use crate::{
+ availability::Availability,
+ socket::MioListener,
+ waker_queue::{WakerInterest, WakerQueue, WAKER_TOKEN},
+ worker::{Conn, ServerWorker, WorkerHandleAccept, WorkerHandleServer},
+ ServerBuilder, ServerHandle,
+};
+
+const TIMEOUT_DURATION_ON_ERROR: Duration = Duration::from_millis(510);
+
+struct ServerSocketInfo {
+ token: usize,
+
+ lst: MioListener,
+
+ /// Timeout is used to mark the deadline when this socket's listener should be registered again
+ /// after an error.
+ timeout: Option<actix_rt::time::Instant>,
+}
+
+/// Poll instance of the server.
+pub(crate) struct Accept {
+ poll: Poll,
+ waker_queue: WakerQueue,
+ handles: Vec<WorkerHandleAccept>,
+ srv: ServerHandle,
+ next: usize,
+ avail: Availability,
+ /// use the smallest duration from sockets timeout.
+ timeout: Option<Duration>,
+ paused: bool,
+}
+
+impl Accept {
+ pub(crate) fn start(
+ sockets: Vec<(usize, MioListener)>,
+ builder: &ServerBuilder,
+ ) -> io::Result<(WakerQueue, Vec<WorkerHandleServer>, thread::JoinHandle<()>)> {
+ let handle_server = ServerHandle::new(builder.cmd_tx.clone());
+
+ // construct poll instance and its waker
+ let poll = Poll::new()?;
+ let waker_queue = WakerQueue::new(poll.registry())?;
+
+ // start workers and collect handles
+ let (handles_accept, handles_server) = (0..builder.threads)
+ .map(|idx| {
+ // clone service factories
+ let factories = builder
+ .factories
+ .iter()
+ .map(|f| f.clone_factory())
+ .collect::<Vec<_>>();
+
+ // start worker using service factories
+ ServerWorker::start(idx, factories, waker_queue.clone(), builder.worker_config)
+ })
+ .collect::<io::Result<Vec<_>>>()?
+ .into_iter()
+ .unzip();
+
+ let (mut accept, mut sockets) = Accept::new_with_sockets(
+ poll,
+ waker_queue.clone(),
+ sockets,
+ handles_accept,
+ handle_server,
+ )?;
+
+ let accept_handle = thread::Builder::new()
+ .name("actix-server acceptor".to_owned())
+ .spawn(move || accept.poll_with(&mut sockets))
+ .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
+
+ Ok((waker_queue, handles_server, accept_handle))
+ }
+
+ fn new_with_sockets(
+ poll: Poll,
+ waker_queue: WakerQueue,
+ sockets: Vec<(usize, MioListener)>,
+ accept_handles: Vec<WorkerHandleAccept>,
+ server_handle: ServerHandle,
+ ) -> io::Result<(Accept, Box<[ServerSocketInfo]>)> {
+ let sockets = sockets
+ .into_iter()
+ .map(|(token, mut lst)| {
+ // Start listening for incoming connections
+ poll.registry()
+ .register(&mut lst, MioToken(token), Interest::READABLE)?;
+
+ Ok(ServerSocketInfo {
+ token,
+ lst,
+ timeout: None,
+ })
+ })
+ .collect::<io::Result<_>>()?;
+
+ let mut avail = Availability::default();
+
+ // Assume all handles are avail at construct time.
+ avail.set_available_all(&accept_handles);
+
+ let accept = Accept {
+ poll,
+ waker_queue,
+ handles: accept_handles,
+ srv: server_handle,
+ next: 0,
+ avail,
+ timeout: None,
+ paused: false,
+ };
+
+ Ok((accept, sockets))
+ }
+
+ /// blocking wait for readiness events triggered by mio
+ fn poll_with(&mut self, sockets: &mut [ServerSocketInfo]) {
+ let mut events = mio::Events::with_capacity(256);
+
+ loop {
+ if let Err(err) = self.poll.poll(&mut events, self.timeout) {
+ match err.kind() {
+ io::ErrorKind::Interrupted => {}
+ _ => panic!("Poll error: {}", err),
+ }
+ }
+
+ for event in events.iter() {
+ let token = event.token();
+ match token {
+ WAKER_TOKEN => {
+ let exit = self.handle_waker(sockets);
+ if exit {
+ info!("accept thread stopped");
+ return;
+ }
+ }
+ _ => {
+ let token = usize::from(token);
+ self.accept(sockets, token);
+ }
+ }
+ }
+
+ // check for timeout and re-register sockets
+ self.process_timeout(sockets);
+ }
+ }
+
+ fn handle_waker(&mut self, sockets: &mut [ServerSocketInfo]) -> bool {
+ // This is a loop because interests for command from previous version was
+ // a loop that would try to drain the command channel. It's yet unknown
+ // if it's necessary/good practice to actively drain the waker queue.
+ loop {
+ // Take guard with every iteration so no new interests can be added until the current
+ // task is done. Take care not to take the guard again inside this loop.
+ let mut guard = self.waker_queue.guard();
+
+ #[allow(clippy::significant_drop_in_scrutinee)]
+ match guard.pop_front() {
+ // Worker notified it became available.
+ Some(WakerInterest::WorkerAvailable(idx)) => {
+ drop(guard);
+
+ self.avail.set_available(idx, true);
+
+ if !self.paused {
+ self.accept_all(sockets);
+ }
+ }
+
+ // A new worker thread has been created so store its handle.
+ Some(WakerInterest::Worker(handle)) => {
+ drop(guard);
+
+ self.avail.set_available(handle.idx(), true);
+ self.handles.push(handle);
+
+ if !self.paused {
+ self.accept_all(sockets);
+ }
+ }
+
+ Some(WakerInterest::Pause) => {
+ drop(guard);
+
+ if !self.paused {
+ self.paused = true;
+
+ self.deregister_all(sockets);
+ }
+ }
+
+ Some(WakerInterest::Resume) => {
+ drop(guard);
+
+ if self.paused {
+ self.paused = false;
+
+ sockets.iter_mut().for_each(|info| {
+ self.register_logged(info);
+ });
+
+ self.accept_all(sockets);
+ }
+ }
+
+ Some(WakerInterest::Stop) => {
+ if !self.paused {
+ self.deregister_all(sockets);
+ }
+
+ return true;
+ }
+
+ // waker queue is drained
+ None => {
+ // Reset the WakerQueue before break so it does not grow infinitely
+ WakerQueue::reset(&mut guard);
+
+ return false;
+ }
+ }
+ }
+ }
+
+ fn process_timeout(&mut self, sockets: &mut [ServerSocketInfo]) {
+ // always remove old timeouts
+ if self.timeout.take().is_some() {
+ let now = Instant::now();
+
+ sockets
+ .iter_mut()
+ // Only sockets that had an associated timeout were deregistered.
+ .filter(|info| info.timeout.is_some())
+ .for_each(|info| {
+ let inst = info.timeout.take().unwrap();
+
+ if now < inst {
+ // still timed out; try to set new timeout
+ info.timeout = Some(inst);
+ self.set_timeout(inst - now);
+ } else if !self.paused {
+ // timeout expired; register socket again
+ self.register_logged(info);
+ }
+
+ // Drop the timeout if server is paused and socket timeout is expired.
+ // When server recovers from pause it will register all sockets without
+ // a timeout value so this socket register will be delayed till then.
+ });
+ }
+ }
+
+ /// Update accept timeout with `duration` if it is shorter than current timeout.
+ fn set_timeout(&mut self, duration: Duration) {
+ match self.timeout {
+ Some(ref mut timeout) => {
+ if *timeout > duration {
+ *timeout = duration;
+ }
+ }
+ None => self.timeout = Some(duration),
+ }
+ }
+
+ #[cfg(not(target_os = "windows"))]
+ fn register(&self, info: &mut ServerSocketInfo) -> io::Result<()> {
+ let token = MioToken(info.token);
+ self.poll
+ .registry()
+ .register(&mut info.lst, token, Interest::READABLE)
+ }
+
+ #[cfg(target_os = "windows")]
+ fn register(&self, info: &mut ServerSocketInfo) -> io::Result<()> {
+ // On windows, calling register without deregister cause an error.
+ // See https://github.com/actix/actix-web/issues/905
+ // Calling reregister seems to fix the issue.
+ let token = MioToken(info.token);
+ self.poll
+ .registry()
+ .register(&mut info.lst, token, Interest::READABLE)
+ .or_else(|_| {
+ self.poll
+ .registry()
+ .reregister(&mut info.lst, token, Interest::READABLE)
+ })
+ }
+
+ fn register_logged(&self, info: &mut ServerSocketInfo) {
+ match self.register(info) {
+ Ok(_) => debug!("resume accepting connections on {}", info.lst.local_addr()),
+ Err(err) => error!("can not register server socket {}", err),
+ }
+ }
+
+ fn deregister_logged(&self, info: &mut ServerSocketInfo) {
+ match self.poll.registry().deregister(&mut info.lst) {
+ Ok(_) => debug!("paused accepting connections on {}", info.lst.local_addr()),
+ Err(err) => {
+ error!("can not deregister server socket {}", err)
+ }
+ }
+ }
+
+ fn deregister_all(&self, sockets: &mut [ServerSocketInfo]) {
+ // This is a best effort implementation with following limitation:
+ //
+ // Every ServerSocketInfo with associated timeout will be skipped and it's timeout is
+ // removed in the process.
+ //
+ // Therefore WakerInterest::Pause followed by WakerInterest::Resume in a very short gap
+ // (less than 500ms) would cause all timing out ServerSocketInfos be re-registered before
+ // expected timing.
+ sockets
+ .iter_mut()
+ // Take all timeout.
+ // This is to prevent Accept::process_timer method re-register a socket afterwards.
+ .map(|info| (info.timeout.take(), info))
+ // Socket info with a timeout is already deregistered so skip them.
+ .filter(|(timeout, _)| timeout.is_none())
+ .for_each(|(_, info)| self.deregister_logged(info));
+ }
+
+ // Send connection to worker and handle error.
+ fn send_connection(&mut self, conn: Conn) -> Result<(), Conn> {
+ let next = self.next();
+ match next.send(conn) {
+ Ok(_) => {
+ // Increment counter of WorkerHandle.
+ // Set worker to unavailable with it hit max (Return false).
+ if !next.inc_counter() {
+ let idx = next.idx();
+ self.avail.set_available(idx, false);
+ }
+ self.set_next();
+ Ok(())
+ }
+ Err(conn) => {
+ // Worker thread is error and could be gone.
+ // Remove worker handle and notify `ServerBuilder`.
+ self.remove_next();
+
+ if self.handles.is_empty() {
+ error!("no workers");
+ // All workers are gone and Conn is nowhere to be sent.
+ // Treat this situation as Ok and drop Conn.
+ return Ok(());
+ } else if self.handles.len() <= self.next {
+ self.next = 0;
+ }
+
+ Err(conn)
+ }
+ }
+ }
+
+ fn accept_one(&mut self, mut conn: Conn) {
+ loop {
+ let next = self.next();
+ let idx = next.idx();
+
+ if self.avail.get_available(idx) {
+ match self.send_connection(conn) {
+ Ok(_) => return,
+ Err(c) => conn = c,
+ }
+ } else {
+ self.avail.set_available(idx, false);
+ self.set_next();
+
+ if !self.avail.available() {
+ while let Err(c) = self.send_connection(conn) {
+ conn = c;
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ fn accept(&mut self, sockets: &mut [ServerSocketInfo], token: usize) {
+ while self.avail.available() {
+ let info = &mut sockets[token];
+
+ match info.lst.accept() {
+ Ok(io) => {
+ let conn = Conn { io, token };
+ self.accept_one(conn);
+ }
+ Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return,
+ Err(ref err) if connection_error(err) => continue,
+ Err(err) => {
+ error!("error accepting connection: {}", err);
+
+ // deregister listener temporary
+ self.deregister_logged(info);
+
+ // sleep after error. write the timeout to socket info as later
+ // the poll would need it mark which socket and when it's
+ // listener should be registered
+ info.timeout = Some(Instant::now() + Duration::from_millis(500));
+ self.set_timeout(TIMEOUT_DURATION_ON_ERROR);
+
+ return;
+ }
+ };
+ }
+ }
+
+ fn accept_all(&mut self, sockets: &mut [ServerSocketInfo]) {
+ sockets
+ .iter_mut()
+ .map(|info| info.token)
+ .collect::<Vec<_>>()
+ .into_iter()
+ .for_each(|idx| self.accept(sockets, idx))
+ }
+
+ #[inline(always)]
+ fn next(&self) -> &WorkerHandleAccept {
+ &self.handles[self.next]
+ }
+
+ /// Set next worker handle that would accept connection.
+ #[inline(always)]
+ fn set_next(&mut self) {
+ self.next = (self.next + 1) % self.handles.len();
+ }
+
+ /// Remove next worker handle that fail to accept connection.
+ fn remove_next(&mut self) {
+ let handle = self.handles.swap_remove(self.next);
+ let idx = handle.idx();
+ // A message is sent to `ServerBuilder` future to notify it a new worker
+ // should be made.
+ self.srv.worker_faulted(idx);
+ self.avail.set_available(idx, false);
+ }
+}
+
+/// This function defines errors that are per-connection; if we get this error from the `accept()`
+/// system call it means the next connection might be ready to be accepted.
+///
+/// All other errors will incur a timeout before next `accept()` call is attempted. The timeout is
+/// useful to handle resource exhaustion errors like `ENFILE` and `EMFILE`. Otherwise, it could
+/// enter into a temporary spin loop.
+fn connection_error(e: &io::Error) -> bool {
+ e.kind() == io::ErrorKind::ConnectionRefused
+ || e.kind() == io::ErrorKind::ConnectionAborted
+ || e.kind() == io::ErrorKind::ConnectionReset
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +
use crate::worker::WorkerHandleAccept;
+
+/// Array of u128 with every bit as marker for a worker handle's availability.
+#[derive(Debug, Default)]
+pub(crate) struct Availability([u128; 4]);
+
+impl Availability {
+ /// Check if any worker handle is available
+ #[inline(always)]
+ pub(crate) fn available(&self) -> bool {
+ self.0.iter().any(|a| *a != 0)
+ }
+
+ /// Check if worker handle is available by index
+ #[inline(always)]
+ pub(crate) fn get_available(&self, idx: usize) -> bool {
+ let (offset, idx) = Self::offset(idx);
+
+ self.0[offset] & (1 << idx as u128) != 0
+ }
+
+ /// Set worker handle available state by index.
+ pub(crate) fn set_available(&mut self, idx: usize, avail: bool) {
+ let (offset, idx) = Self::offset(idx);
+
+ let off = 1 << idx as u128;
+ if avail {
+ self.0[offset] |= off;
+ } else {
+ self.0[offset] &= !off
+ }
+ }
+
+ /// Set all worker handle to available state.
+ /// This would result in a re-check on all workers' availability.
+ pub(crate) fn set_available_all(&mut self, handles: &[WorkerHandleAccept]) {
+ handles.iter().for_each(|handle| {
+ self.set_available(handle.idx(), true);
+ })
+ }
+
+ /// Get offset and adjusted index of given worker handle index.
+ pub(crate) fn offset(idx: usize) -> (usize, usize) {
+ if idx < 128 {
+ (0, idx)
+ } else if idx < 128 * 2 {
+ (1, idx - 128)
+ } else if idx < 128 * 3 {
+ (2, idx - 128 * 2)
+ } else if idx < 128 * 4 {
+ (3, idx - 128 * 3)
+ } else {
+ panic!("Max WorkerHandle count is 512")
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn single(aval: &mut Availability, idx: usize) {
+ aval.set_available(idx, true);
+ assert!(aval.available());
+
+ aval.set_available(idx, true);
+
+ aval.set_available(idx, false);
+ assert!(!aval.available());
+
+ aval.set_available(idx, false);
+ assert!(!aval.available());
+ }
+
+ fn multi(aval: &mut Availability, mut idx: Vec<usize>) {
+ idx.iter().for_each(|idx| aval.set_available(*idx, true));
+
+ assert!(aval.available());
+
+ while let Some(idx) = idx.pop() {
+ assert!(aval.available());
+ aval.set_available(idx, false);
+ }
+
+ assert!(!aval.available());
+ }
+
+ #[test]
+ fn availability() {
+ let mut aval = Availability::default();
+
+ single(&mut aval, 1);
+ single(&mut aval, 128);
+ single(&mut aval, 256);
+ single(&mut aval, 511);
+
+ let idx = (0..511).filter(|i| i % 3 == 0 && i % 5 == 0).collect();
+
+ multi(&mut aval, idx);
+
+ multi(&mut aval, (0..511).collect())
+ }
+
+ #[test]
+ #[should_panic]
+ fn overflow() {
+ let mut aval = Availability::default();
+ single(&mut aval, 512);
+ }
+
+ #[test]
+ fn pin_point() {
+ let mut aval = Availability::default();
+
+ aval.set_available(438, true);
+
+ aval.set_available(479, true);
+
+ assert_eq!(aval.0[3], 1 << (438 - 384) | 1 << (479 - 384));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +
use std::{io, num::NonZeroUsize, time::Duration};
+
+use actix_rt::net::TcpStream;
+use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
+
+use crate::{
+ server::ServerCommand,
+ service::{InternalServiceFactory, ServerServiceFactory, StreamNewService},
+ socket::{create_mio_tcp_listener, MioListener, MioTcpListener, StdTcpListener, ToSocketAddrs},
+ worker::ServerWorkerConfig,
+ Server,
+};
+
+/// Multipath TCP (MPTCP) preference.
+///
+/// Currently only useful on Linux.
+///
+#[cfg_attr(target_os = "linux", doc = "Also see [`ServerBuilder::mptcp()`].")]
+#[derive(Debug, Clone)]
+pub enum MpTcp {
+ /// MPTCP will not be used when binding sockets.
+ Disabled,
+
+ /// MPTCP will be attempted when binding sockets. If errors occur, regular TCP will be
+ /// attempted, too.
+ TcpFallback,
+
+ /// MPTCP will be used when binding sockets (with no fallback).
+ NoFallback,
+}
+
+/// [Server] builder.
+pub struct ServerBuilder {
+ pub(crate) threads: usize,
+ pub(crate) token: usize,
+ pub(crate) backlog: u32,
+ pub(crate) factories: Vec<Box<dyn InternalServiceFactory>>,
+ pub(crate) sockets: Vec<(usize, String, MioListener)>,
+ pub(crate) mptcp: MpTcp,
+ pub(crate) exit: bool,
+ pub(crate) listen_os_signals: bool,
+ pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
+ pub(crate) cmd_rx: UnboundedReceiver<ServerCommand>,
+ pub(crate) worker_config: ServerWorkerConfig,
+}
+
+impl Default for ServerBuilder {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl ServerBuilder {
+ /// Create new Server builder instance
+ pub fn new() -> ServerBuilder {
+ let (cmd_tx, cmd_rx) = unbounded_channel();
+
+ ServerBuilder {
+ threads: std::thread::available_parallelism().map_or(2, NonZeroUsize::get),
+ token: 0,
+ factories: Vec::new(),
+ sockets: Vec::new(),
+ backlog: 2048,
+ mptcp: MpTcp::Disabled,
+ exit: false,
+ listen_os_signals: true,
+ cmd_tx,
+ cmd_rx,
+ worker_config: ServerWorkerConfig::default(),
+ }
+ }
+
+ /// Sets number of workers to start.
+ ///
+ /// See [`bind()`](Self::bind()) for more details on how worker count affects the number of
+ /// server factory instantiations.
+ ///
+ /// The default worker count is the determined by [`std::thread::available_parallelism()`]. See
+ /// its documentation to determine what behavior you should expect when server is run.
+ ///
+ /// `num` must be greater than 0.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `num` is 0.
+ pub fn workers(mut self, num: usize) -> Self {
+ assert_ne!(num, 0, "workers must be greater than 0");
+ self.threads = num;
+ self
+ }
+
+ /// Set max number of threads for each worker's blocking task thread pool.
+ ///
+ /// One thread pool is set up **per worker**; not shared across workers.
+ ///
+ /// # Examples:
+ /// ```
+ /// # use actix_server::ServerBuilder;
+ /// let builder = ServerBuilder::new()
+ /// .workers(4) // server has 4 worker thread.
+ /// .worker_max_blocking_threads(4); // every worker has 4 max blocking threads.
+ /// ```
+ ///
+ /// See [tokio::runtime::Builder::max_blocking_threads] for behavior reference.
+ pub fn worker_max_blocking_threads(mut self, num: usize) -> Self {
+ self.worker_config.max_blocking_threads(num);
+ self
+ }
+
+ /// Set the maximum number of pending connections.
+ ///
+ /// This refers to the number of clients that can be waiting to be served. Exceeding this number
+ /// results in the client getting an error when attempting to connect. It should only affect
+ /// servers under significant load.
+ ///
+ /// Generally set in the 64-2048 range. Default value is 2048.
+ ///
+ /// This method should be called before `bind()` method call.
+ pub fn backlog(mut self, num: u32) -> Self {
+ self.backlog = num;
+ self
+ }
+
+ /// Sets MultiPath TCP (MPTCP) preference on bound sockets.
+ ///
+ /// Multipath TCP (MPTCP) builds on top of TCP to improve connection redundancy and performance
+ /// by sharing a network data stream across multiple underlying TCP sessions. See [mptcp.dev]
+ /// for more info about MPTCP itself.
+ ///
+ /// MPTCP is available on Linux kernel version 5.6 and higher. In addition, you'll also need to
+ /// ensure the kernel option is enabled using `sysctl net.mptcp.enabled=1`.
+ ///
+ /// This method will have no effect if called after a `bind()`.
+ ///
+ /// [mptcp.dev]: https://www.mptcp.dev
+ #[cfg(target_os = "linux")]
+ pub fn mptcp(mut self, mptcp_enabled: MpTcp) -> Self {
+ self.mptcp = mptcp_enabled;
+ self
+ }
+
+ /// Sets the maximum per-worker number of concurrent connections.
+ ///
+ /// All socket listeners will stop accepting connections when this limit is reached for
+ /// each worker.
+ ///
+ /// By default max connections is set to a 25k per worker.
+ pub fn max_concurrent_connections(mut self, num: usize) -> Self {
+ self.worker_config.max_concurrent_connections(num);
+ self
+ }
+
+ #[doc(hidden)]
+ #[deprecated(since = "2.0.0", note = "Renamed to `max_concurrent_connections`.")]
+ pub fn maxconn(self, num: usize) -> Self {
+ self.max_concurrent_connections(num)
+ }
+
+ /// Sets flag to stop Actix `System` after server shutdown.
+ ///
+ /// This has no effect when server is running in a Tokio-only runtime.
+ pub fn system_exit(mut self) -> Self {
+ self.exit = true;
+ self
+ }
+
+ /// Disables OS signal handling.
+ pub fn disable_signals(mut self) -> Self {
+ self.listen_os_signals = false;
+ self
+ }
+
+ /// Timeout for graceful workers shutdown in seconds.
+ ///
+ /// After receiving a stop signal, workers have this much time to finish serving requests.
+ /// Workers still alive after the timeout are force dropped.
+ ///
+ /// By default shutdown timeout sets to 30 seconds.
+ pub fn shutdown_timeout(mut self, sec: u64) -> Self {
+ self.worker_config
+ .shutdown_timeout(Duration::from_secs(sec));
+ self
+ }
+
+ /// Adds new service to the server.
+ ///
+ /// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.
+ ///
+ /// # Worker Count
+ ///
+ /// The `factory` will be instantiated multiple times in most scenarios. The number of
+ /// instantiations is number of [`workers`](Self::workers()) × number of sockets resolved by
+ /// `addrs`.
+ ///
+ /// For example, if you've manually set [`workers`](Self::workers()) to 2, and use `127.0.0.1`
+ /// as the bind `addrs`, then `factory` will be instantiated twice. However, using `localhost`
+ /// as the bind `addrs` can often resolve to both `127.0.0.1` (IPv4) _and_ `::1` (IPv6), causing
+ /// the `factory` to be instantiated 4 times (2 workers × 2 bind addresses).
+ ///
+ /// Using a bind address of `0.0.0.0`, which signals to use all interfaces, may also multiple
+ /// the number of instantiations in a similar way.
+ ///
+ /// # Errors
+ ///
+ /// Returns an `io::Error` if:
+ /// - `addrs` cannot be resolved into one or more socket addresses;
+ /// - all the resolved socket addresses are already bound.
+ pub fn bind<F, U, N>(mut self, name: N, addrs: U, factory: F) -> io::Result<Self>
+ where
+ F: ServerServiceFactory<TcpStream>,
+ U: ToSocketAddrs,
+ N: AsRef<str>,
+ {
+ let sockets = bind_addr(addrs, self.backlog, &self.mptcp)?;
+
+ tracing::trace!("binding server to: {sockets:?}");
+
+ for lst in sockets {
+ let token = self.next_token();
+
+ self.factories.push(StreamNewService::create(
+ name.as_ref().to_string(),
+ token,
+ factory.clone(),
+ lst.local_addr()?,
+ ));
+
+ self.sockets
+ .push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
+ }
+
+ Ok(self)
+ }
+
+ /// Adds service to the server using a socket listener already bound.
+ ///
+ /// # Worker Count
+ ///
+ /// The `factory` will be instantiated multiple times in most scenarios. The number of
+ /// instantiations is: number of [`workers`](Self::workers()).
+ pub fn listen<F, N: AsRef<str>>(
+ mut self,
+ name: N,
+ lst: StdTcpListener,
+ factory: F,
+ ) -> io::Result<Self>
+ where
+ F: ServerServiceFactory<TcpStream>,
+ {
+ lst.set_nonblocking(true)?;
+ let addr = lst.local_addr()?;
+
+ let token = self.next_token();
+ self.factories.push(StreamNewService::create(
+ name.as_ref().to_string(),
+ token,
+ factory,
+ addr,
+ ));
+
+ self.sockets
+ .push((token, name.as_ref().to_string(), MioListener::from(lst)));
+
+ Ok(self)
+ }
+
+ /// Starts processing incoming connections and return server controller.
+ pub fn run(self) -> Server {
+ if self.sockets.is_empty() {
+ panic!("Server should have at least one bound socket");
+ } else {
+ tracing::info!("starting {} workers", self.threads);
+ Server::new(self)
+ }
+ }
+
+ fn next_token(&mut self) -> usize {
+ let token = self.token;
+ self.token += 1;
+ token
+ }
+}
+
+#[cfg(unix)]
+impl ServerBuilder {
+ /// Adds new service to the server using a UDS (unix domain socket) address.
+ ///
+ /// # Worker Count
+ ///
+ /// The `factory` will be instantiated multiple times in most scenarios. The number of
+ /// instantiations is: number of [`workers`](Self::workers()).
+ pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
+ where
+ F: ServerServiceFactory<actix_rt::net::UnixStream>,
+ N: AsRef<str>,
+ U: AsRef<std::path::Path>,
+ {
+ // The path must not exist when we try to bind.
+ // Try to remove it to avoid bind error.
+ if let Err(err) = std::fs::remove_file(addr.as_ref()) {
+ // NotFound is expected and not an issue. Anything else is.
+ if err.kind() != std::io::ErrorKind::NotFound {
+ return Err(err);
+ }
+ }
+
+ let lst = crate::socket::StdUnixListener::bind(addr)?;
+ self.listen_uds(name, lst, factory)
+ }
+
+ /// Adds new service to the server using a UDS (unix domain socket) listener already bound.
+ ///
+ /// Useful when running as a systemd service and a socket FD is acquired externally.
+ ///
+ /// # Worker Count
+ ///
+ /// The `factory` will be instantiated multiple times in most scenarios. The number of
+ /// instantiations is: number of [`workers`](Self::workers()).
+ pub fn listen_uds<F, N: AsRef<str>>(
+ mut self,
+ name: N,
+ lst: crate::socket::StdUnixListener,
+ factory: F,
+ ) -> io::Result<Self>
+ where
+ F: ServerServiceFactory<actix_rt::net::UnixStream>,
+ {
+ use std::net::{IpAddr, Ipv4Addr};
+
+ lst.set_nonblocking(true)?;
+
+ let token = self.next_token();
+ let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+
+ self.factories.push(StreamNewService::create(
+ name.as_ref().to_string(),
+ token,
+ factory,
+ addr,
+ ));
+
+ self.sockets
+ .push((token, name.as_ref().to_string(), MioListener::from(lst)));
+
+ Ok(self)
+ }
+}
+
+pub(super) fn bind_addr<S: ToSocketAddrs>(
+ addr: S,
+ backlog: u32,
+ mptcp: &MpTcp,
+) -> io::Result<Vec<MioTcpListener>> {
+ let mut opt_err = None;
+ let mut success = false;
+ let mut sockets = Vec::new();
+
+ for addr in addr.to_socket_addrs()? {
+ match create_mio_tcp_listener(addr, backlog, mptcp) {
+ Ok(lst) => {
+ success = true;
+ sockets.push(lst);
+ }
+ Err(err) => opt_err = Some(err),
+ }
+ }
+
+ if success {
+ Ok(sockets)
+ } else if let Some(err) = opt_err.take() {
+ Err(err)
+ } else {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "Can not bind to address.",
+ ))
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +
use std::future::Future;
+
+use tokio::sync::{mpsc::UnboundedSender, oneshot};
+
+use crate::server::ServerCommand;
+
+/// Server handle.
+#[derive(Debug, Clone)]
+pub struct ServerHandle {
+ cmd_tx: UnboundedSender<ServerCommand>,
+}
+
+impl ServerHandle {
+ pub(crate) fn new(cmd_tx: UnboundedSender<ServerCommand>) -> Self {
+ ServerHandle { cmd_tx }
+ }
+
+ pub(crate) fn worker_faulted(&self, idx: usize) {
+ let _ = self.cmd_tx.send(ServerCommand::WorkerFaulted(idx));
+ }
+
+ /// Pause accepting incoming connections.
+ ///
+ /// May drop socket pending connection. All open connections remain active.
+ pub fn pause(&self) -> impl Future<Output = ()> {
+ let (tx, rx) = oneshot::channel();
+ let _ = self.cmd_tx.send(ServerCommand::Pause(tx));
+ async {
+ let _ = rx.await;
+ }
+ }
+
+ /// Resume accepting incoming connections.
+ pub fn resume(&self) -> impl Future<Output = ()> {
+ let (tx, rx) = oneshot::channel();
+ let _ = self.cmd_tx.send(ServerCommand::Resume(tx));
+ async {
+ let _ = rx.await;
+ }
+ }
+
+ /// Stop incoming connection processing, stop all workers and exit.
+ pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> {
+ let (tx, rx) = oneshot::channel();
+
+ let _ = self.cmd_tx.send(ServerCommand::Stop {
+ graceful,
+ completion: Some(tx),
+ force_system_stop: false,
+ });
+
+ async {
+ let _ = rx.await;
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +
use std::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use futures_core::future::BoxFuture;
+
+// a poor man's join future. joined future is only used when starting/stopping the server.
+// pin_project and pinned futures are overkill for this task.
+pub(crate) struct JoinAll<T> {
+ fut: Vec<JoinFuture<T>>,
+}
+
+pub(crate) fn join_all<T>(fut: Vec<impl Future<Output = T> + Send + 'static>) -> JoinAll<T> {
+ let fut = fut
+ .into_iter()
+ .map(|f| JoinFuture::Future(Box::pin(f)))
+ .collect();
+
+ JoinAll { fut }
+}
+
+enum JoinFuture<T> {
+ Future(BoxFuture<'static, T>),
+ Result(Option<T>),
+}
+
+impl<T> Unpin for JoinAll<T> {}
+
+impl<T> Future for JoinAll<T> {
+ type Output = Vec<T>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut ready = true;
+
+ let this = self.get_mut();
+ for fut in this.fut.iter_mut() {
+ if let JoinFuture::Future(f) = fut {
+ match f.as_mut().poll(cx) {
+ Poll::Ready(t) => {
+ *fut = JoinFuture::Result(Some(t));
+ }
+ Poll::Pending => ready = false,
+ }
+ }
+ }
+
+ if ready {
+ let mut res = Vec::new();
+ for fut in this.fut.iter_mut() {
+ if let JoinFuture::Result(f) = fut {
+ res.push(f.take().unwrap());
+ }
+ }
+
+ Poll::Ready(res)
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use actix_utils::future::ready;
+
+ use super::*;
+
+ #[actix_rt::test]
+ async fn test_join_all() {
+ let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))];
+ let mut res = join_all(futs).await.into_iter();
+ assert_eq!(Ok(1), res.next().unwrap());
+ assert_eq!(Err(3), res.next().unwrap());
+ assert_eq!(Ok(9), res.next().unwrap());
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +
//! General purpose TCP server.
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+
+mod accept;
+mod availability;
+mod builder;
+mod handle;
+mod join_all;
+mod server;
+mod service;
+mod signals;
+mod socket;
+mod test_server;
+mod waker_queue;
+mod worker;
+
+#[doc(hidden)]
+pub use self::socket::FromStream;
+pub use self::{
+ builder::{MpTcp, ServerBuilder},
+ handle::ServerHandle,
+ server::Server,
+ service::ServerServiceFactory,
+ test_server::TestServer,
+};
+
+/// Start server building process
+#[doc(hidden)]
+#[deprecated(since = "2.0.0", note = "Use `Server::build()`.")]
+pub fn new() -> ServerBuilder {
+ ServerBuilder::default()
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +
use std::{
+ future::Future,
+ io, mem,
+ pin::Pin,
+ task::{Context, Poll},
+ thread,
+ time::Duration,
+};
+
+use actix_rt::{time::sleep, System};
+use futures_core::{future::BoxFuture, Stream};
+use futures_util::stream::StreamExt as _;
+use tokio::sync::{mpsc::UnboundedReceiver, oneshot};
+use tracing::{error, info};
+
+use crate::{
+ accept::Accept,
+ builder::ServerBuilder,
+ join_all::join_all,
+ service::InternalServiceFactory,
+ signals::{SignalKind, Signals},
+ waker_queue::{WakerInterest, WakerQueue},
+ worker::{ServerWorker, ServerWorkerConfig, WorkerHandleServer},
+ ServerHandle,
+};
+
+#[derive(Debug)]
+pub(crate) enum ServerCommand {
+ /// Worker failed to accept connection, indicating a probable panic.
+ ///
+ /// Contains index of faulted worker.
+ WorkerFaulted(usize),
+
+ /// Pause accepting connections.
+ ///
+ /// Contains return channel to notify caller of successful state change.
+ Pause(oneshot::Sender<()>),
+
+ /// Resume accepting connections.
+ ///
+ /// Contains return channel to notify caller of successful state change.
+ Resume(oneshot::Sender<()>),
+
+ /// Stop accepting connections and begin shutdown procedure.
+ Stop {
+ /// True if shut down should be graceful.
+ graceful: bool,
+
+ /// Return channel to notify caller that shutdown is complete.
+ completion: Option<oneshot::Sender<()>>,
+
+ /// Force System exit when true, overriding `ServerBuilder::system_exit()` if it is false.
+ force_system_stop: bool,
+ },
+}
+
+/// General purpose TCP server that runs services receiving Tokio `TcpStream`s.
+///
+/// Handles creating worker threads, restarting faulted workers, connection accepting, and
+/// back-pressure logic.
+///
+/// Creates a worker per CPU core (or the number specified in [`ServerBuilder::workers`]) and
+/// distributes connections with a round-robin strategy.
+///
+/// The [Server] must be awaited or polled in order to start running. It will resolve when the
+/// server has fully shut down.
+///
+/// # Shutdown Signals
+/// On UNIX systems, `SIGTERM` will start a graceful shutdown and `SIGQUIT` or `SIGINT` will start a
+/// forced shutdown. On Windows, a Ctrl-C signal will start a forced shutdown.
+///
+/// A graceful shutdown will wait for all workers to stop first.
+///
+/// # Examples
+/// The following is a TCP echo server. Test using `telnet 127.0.0.1 8080`.
+///
+/// ```no_run
+/// use std::io;
+///
+/// use actix_rt::net::TcpStream;
+/// use actix_server::Server;
+/// use actix_service::{fn_service, ServiceFactoryExt as _};
+/// use bytes::BytesMut;
+/// use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
+///
+/// #[actix_rt::main]
+/// async fn main() -> io::Result<()> {
+/// let bind_addr = ("127.0.0.1", 8080);
+///
+/// Server::build()
+/// .bind("echo", bind_addr, move || {
+/// fn_service(move |mut stream: TcpStream| {
+/// async move {
+/// let mut size = 0;
+/// let mut buf = BytesMut::new();
+///
+/// loop {
+/// match stream.read_buf(&mut buf).await {
+/// // end of stream; bail from loop
+/// Ok(0) => break,
+///
+/// // write bytes back to stream
+/// Ok(bytes_read) => {
+/// stream.write_all(&buf[size..]).await.unwrap();
+/// size += bytes_read;
+/// }
+///
+/// Err(err) => {
+/// eprintln!("Stream Error: {:?}", err);
+/// return Err(());
+/// }
+/// }
+/// }
+///
+/// Ok(())
+/// }
+/// })
+/// .map_err(|err| eprintln!("Service Error: {:?}", err))
+/// })?
+/// .run()
+/// .await
+/// }
+/// ```
+#[must_use = "Server does nothing unless you `.await` or poll it"]
+pub struct Server {
+ handle: ServerHandle,
+ fut: BoxFuture<'static, io::Result<()>>,
+}
+
+impl Server {
+ /// Create server build.
+ pub fn build() -> ServerBuilder {
+ ServerBuilder::default()
+ }
+
+ pub(crate) fn new(builder: ServerBuilder) -> Self {
+ Server {
+ handle: ServerHandle::new(builder.cmd_tx.clone()),
+ fut: Box::pin(ServerInner::run(builder)),
+ }
+ }
+
+ /// Get a `Server` handle that can be used issue commands and change it's state.
+ ///
+ /// See [ServerHandle](ServerHandle) for usage.
+ pub fn handle(&self) -> ServerHandle {
+ self.handle.clone()
+ }
+}
+
+impl Future for Server {
+ type Output = io::Result<()>;
+
+ #[inline]
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ Pin::new(&mut Pin::into_inner(self).fut).poll(cx)
+ }
+}
+
+pub struct ServerInner {
+ worker_handles: Vec<WorkerHandleServer>,
+ accept_handle: Option<thread::JoinHandle<()>>,
+ worker_config: ServerWorkerConfig,
+ services: Vec<Box<dyn InternalServiceFactory>>,
+ waker_queue: WakerQueue,
+ system_stop: bool,
+ stopping: bool,
+}
+
+impl ServerInner {
+ async fn run(builder: ServerBuilder) -> io::Result<()> {
+ let (mut this, mut mux) = Self::run_sync(builder)?;
+
+ while let Some(cmd) = mux.next().await {
+ this.handle_cmd(cmd).await;
+
+ if this.stopping {
+ break;
+ }
+ }
+
+ Ok(())
+ }
+
+ fn run_sync(mut builder: ServerBuilder) -> io::Result<(Self, ServerEventMultiplexer)> {
+ let sockets = mem::take(&mut builder.sockets)
+ .into_iter()
+ .map(|t| (t.0, t.2))
+ .collect();
+
+ // Give log information on what runtime will be used.
+ let is_actix = actix_rt::System::try_current().is_some();
+ let is_tokio = tokio::runtime::Handle::try_current().is_ok();
+
+ match (is_actix, is_tokio) {
+ (true, _) => info!("Actix runtime found; starting in Actix runtime"),
+ (_, true) => info!("Tokio runtime found; starting in existing Tokio runtime"),
+ (_, false) => panic!("Actix or Tokio runtime not found; halting"),
+ }
+
+ for (_, name, lst) in &builder.sockets {
+ info!(
+ r#"starting service: "{}", workers: {}, listening on: {}"#,
+ name,
+ builder.threads,
+ lst.local_addr()
+ );
+ }
+
+ let (waker_queue, worker_handles, accept_handle) = Accept::start(sockets, &builder)?;
+
+ let mux = ServerEventMultiplexer {
+ signal_fut: (builder.listen_os_signals).then(Signals::new),
+ cmd_rx: builder.cmd_rx,
+ };
+
+ let server = ServerInner {
+ waker_queue,
+ accept_handle: Some(accept_handle),
+ worker_handles,
+ worker_config: builder.worker_config,
+ services: builder.factories,
+ system_stop: builder.exit,
+ stopping: false,
+ };
+
+ Ok((server, mux))
+ }
+
+ async fn handle_cmd(&mut self, item: ServerCommand) {
+ match item {
+ ServerCommand::Pause(tx) => {
+ self.waker_queue.wake(WakerInterest::Pause);
+ let _ = tx.send(());
+ }
+
+ ServerCommand::Resume(tx) => {
+ self.waker_queue.wake(WakerInterest::Resume);
+ let _ = tx.send(());
+ }
+
+ ServerCommand::Stop {
+ graceful,
+ completion,
+ force_system_stop,
+ } => {
+ self.stopping = true;
+
+ // Signal accept thread to stop.
+ // Signal is non-blocking; we wait for thread to stop later.
+ self.waker_queue.wake(WakerInterest::Stop);
+
+ // send stop signal to workers
+ let workers_stop = self
+ .worker_handles
+ .iter()
+ .map(|worker| worker.stop(graceful))
+ .collect::<Vec<_>>();
+
+ if graceful {
+ // wait for all workers to shut down
+ let _ = join_all(workers_stop).await;
+ }
+
+ // wait for accept thread stop
+ self.accept_handle
+ .take()
+ .unwrap()
+ .join()
+ .expect("Accept thread must not panic in any case");
+
+ if let Some(tx) = completion {
+ let _ = tx.send(());
+ }
+
+ if self.system_stop || force_system_stop {
+ sleep(Duration::from_millis(300)).await;
+ System::try_current().as_ref().map(System::stop);
+ }
+ }
+
+ ServerCommand::WorkerFaulted(idx) => {
+ // TODO: maybe just return with warning log if not found ?
+ assert!(self.worker_handles.iter().any(|wrk| wrk.idx == idx));
+
+ error!("worker {} has died; restarting", idx);
+
+ let factories = self
+ .services
+ .iter()
+ .map(|service| service.clone_factory())
+ .collect();
+
+ match ServerWorker::start(
+ idx,
+ factories,
+ self.waker_queue.clone(),
+ self.worker_config,
+ ) {
+ Ok((handle_accept, handle_server)) => {
+ *self
+ .worker_handles
+ .iter_mut()
+ .find(|wrk| wrk.idx == idx)
+ .unwrap() = handle_server;
+
+ self.waker_queue.wake(WakerInterest::Worker(handle_accept));
+ }
+
+ Err(err) => error!("can not restart worker {}: {}", idx, err),
+ };
+ }
+ }
+ }
+
+ fn map_signal(signal: SignalKind) -> ServerCommand {
+ match signal {
+ SignalKind::Int => {
+ info!("SIGINT received; starting forced shutdown");
+ ServerCommand::Stop {
+ graceful: false,
+ completion: None,
+ force_system_stop: true,
+ }
+ }
+
+ SignalKind::Term => {
+ info!("SIGTERM received; starting graceful shutdown");
+ ServerCommand::Stop {
+ graceful: true,
+ completion: None,
+ force_system_stop: true,
+ }
+ }
+
+ SignalKind::Quit => {
+ info!("SIGQUIT received; starting forced shutdown");
+ ServerCommand::Stop {
+ graceful: false,
+ completion: None,
+ force_system_stop: true,
+ }
+ }
+ }
+ }
+}
+
+struct ServerEventMultiplexer {
+ cmd_rx: UnboundedReceiver<ServerCommand>,
+ signal_fut: Option<Signals>,
+}
+
+impl Stream for ServerEventMultiplexer {
+ type Item = ServerCommand;
+
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ let this = Pin::into_inner(self);
+
+ if let Some(signal_fut) = &mut this.signal_fut {
+ if let Poll::Ready(signal) = Pin::new(signal_fut).poll(cx) {
+ this.signal_fut = None;
+ return Poll::Ready(Some(ServerInner::map_signal(signal)));
+ }
+ }
+
+ this.cmd_rx.poll_recv(cx)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +
use std::{
+ marker::PhantomData,
+ net::SocketAddr,
+ task::{Context, Poll},
+};
+
+use actix_service::{Service, ServiceFactory as BaseServiceFactory};
+use actix_utils::future::{ready, Ready};
+use futures_core::future::LocalBoxFuture;
+use tracing::error;
+
+use crate::{
+ socket::{FromStream, MioStream},
+ worker::WorkerCounterGuard,
+};
+
+#[doc(hidden)]
+pub trait ServerServiceFactory<Stream: FromStream>: Send + Clone + 'static {
+ type Factory: BaseServiceFactory<Stream, Config = ()>;
+
+ fn create(&self) -> Self::Factory;
+}
+
+pub(crate) trait InternalServiceFactory: Send {
+ fn name(&self, token: usize) -> &str;
+
+ fn clone_factory(&self) -> Box<dyn InternalServiceFactory>;
+
+ fn create(&self) -> LocalBoxFuture<'static, Result<(usize, BoxedServerService), ()>>;
+}
+
+pub(crate) type BoxedServerService = Box<
+ dyn Service<
+ (WorkerCounterGuard, MioStream),
+ Response = (),
+ Error = (),
+ Future = Ready<Result<(), ()>>,
+ >,
+>;
+
+pub(crate) struct StreamService<S, I> {
+ service: S,
+ _phantom: PhantomData<I>,
+}
+
+impl<S, I> StreamService<S, I> {
+ pub(crate) fn new(service: S) -> Self {
+ StreamService {
+ service,
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<S, I> Service<(WorkerCounterGuard, MioStream)> for StreamService<S, I>
+where
+ S: Service<I>,
+ S::Future: 'static,
+ S::Error: 'static,
+ I: FromStream,
+{
+ type Response = ();
+ type Error = ();
+ type Future = Ready<Result<(), ()>>;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.service.poll_ready(ctx).map_err(|_| ())
+ }
+
+ fn call(&self, (guard, req): (WorkerCounterGuard, MioStream)) -> Self::Future {
+ ready(match FromStream::from_mio(req) {
+ Ok(stream) => {
+ let f = self.service.call(stream);
+ actix_rt::spawn(async move {
+ let _ = f.await;
+ drop(guard);
+ });
+ Ok(())
+ }
+ Err(err) => {
+ error!("can not convert to an async TCP stream: {err}");
+ Err(())
+ }
+ })
+ }
+}
+
+pub(crate) struct StreamNewService<F: ServerServiceFactory<Io>, Io: FromStream> {
+ name: String,
+ inner: F,
+ token: usize,
+ addr: SocketAddr,
+ _t: PhantomData<Io>,
+}
+
+impl<F, Io> StreamNewService<F, Io>
+where
+ F: ServerServiceFactory<Io>,
+ Io: FromStream + Send + 'static,
+{
+ pub(crate) fn create(
+ name: String,
+ token: usize,
+ inner: F,
+ addr: SocketAddr,
+ ) -> Box<dyn InternalServiceFactory> {
+ Box::new(Self {
+ name,
+ token,
+ inner,
+ addr,
+ _t: PhantomData,
+ })
+ }
+}
+
+impl<F, Io> InternalServiceFactory for StreamNewService<F, Io>
+where
+ F: ServerServiceFactory<Io>,
+ Io: FromStream + Send + 'static,
+{
+ fn name(&self, _: usize) -> &str {
+ &self.name
+ }
+
+ fn clone_factory(&self) -> Box<dyn InternalServiceFactory> {
+ Box::new(Self {
+ name: self.name.clone(),
+ inner: self.inner.clone(),
+ token: self.token,
+ addr: self.addr,
+ _t: PhantomData,
+ })
+ }
+
+ fn create(&self) -> LocalBoxFuture<'static, Result<(usize, BoxedServerService), ()>> {
+ let token = self.token;
+ let fut = self.inner.create().new_service(());
+ Box::pin(async move {
+ match fut.await {
+ Ok(inner) => {
+ let service = Box::new(StreamService::new(inner)) as _;
+ Ok((token, service))
+ }
+ Err(_) => Err(()),
+ }
+ })
+ }
+}
+
+impl<F, T, I> ServerServiceFactory<I> for F
+where
+ F: Fn() -> T + Send + Clone + 'static,
+ T: BaseServiceFactory<I, Config = ()>,
+ I: FromStream,
+{
+ type Factory = T;
+
+ fn create(&self) -> T {
+ (self)()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +
use std::{
+ fmt,
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use tracing::trace;
+
+/// Types of process signals.
+// #[allow(dead_code)]
+#[derive(Debug, Clone, Copy, PartialEq)]
+#[allow(dead_code)] // variants are never constructed on non-unix
+pub(crate) enum SignalKind {
+ /// `SIGINT`
+ Int,
+
+ /// `SIGTERM`
+ Term,
+
+ /// `SIGQUIT`
+ Quit,
+}
+
+impl fmt::Display for SignalKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(match self {
+ SignalKind::Int => "SIGINT",
+ SignalKind::Term => "SIGTERM",
+ SignalKind::Quit => "SIGQUIT",
+ })
+ }
+}
+
+/// Process signal listener.
+pub(crate) struct Signals {
+ #[cfg(not(unix))]
+ signals: futures_core::future::BoxFuture<'static, std::io::Result<()>>,
+
+ #[cfg(unix)]
+ signals: Vec<(SignalKind, actix_rt::signal::unix::Signal)>,
+}
+
+impl Signals {
+ /// Constructs an OS signal listening future.
+ pub(crate) fn new() -> Self {
+ trace!("setting up OS signal listener");
+
+ #[cfg(not(unix))]
+ {
+ Signals {
+ signals: Box::pin(actix_rt::signal::ctrl_c()),
+ }
+ }
+
+ #[cfg(unix)]
+ {
+ use actix_rt::signal::unix;
+
+ let sig_map = [
+ (unix::SignalKind::interrupt(), SignalKind::Int),
+ (unix::SignalKind::terminate(), SignalKind::Term),
+ (unix::SignalKind::quit(), SignalKind::Quit),
+ ];
+
+ let signals = sig_map
+ .iter()
+ .filter_map(|(kind, sig)| {
+ unix::signal(*kind)
+ .map(|tokio_sig| (*sig, tokio_sig))
+ .map_err(|e| {
+ tracing::error!(
+ "can not initialize stream handler for {:?} err: {}",
+ sig,
+ e
+ )
+ })
+ .ok()
+ })
+ .collect::<Vec<_>>();
+
+ Signals { signals }
+ }
+ }
+}
+
+impl Future for Signals {
+ type Output = SignalKind;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ #[cfg(not(unix))]
+ {
+ self.signals.as_mut().poll(cx).map(|_| SignalKind::Int)
+ }
+
+ #[cfg(unix)]
+ {
+ for (sig, fut) in self.signals.iter_mut() {
+ if fut.poll_recv(cx).is_ready() {
+ trace!("{} received", sig);
+ return Poll::Ready(*sig);
+ }
+ }
+
+ Poll::Pending
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +
pub(crate) use std::net::{
+ SocketAddr as StdSocketAddr, TcpListener as StdTcpListener, ToSocketAddrs,
+};
+use std::{fmt, io};
+
+use actix_rt::net::TcpStream;
+pub(crate) use mio::net::TcpListener as MioTcpListener;
+use mio::{event::Source, Interest, Registry, Token};
+#[cfg(unix)]
+pub(crate) use {
+ mio::net::UnixListener as MioUnixListener, std::os::unix::net::UnixListener as StdUnixListener,
+};
+
+use crate::builder::MpTcp;
+
+pub(crate) enum MioListener {
+ Tcp(MioTcpListener),
+ #[cfg(unix)]
+ Uds(MioUnixListener),
+}
+
+impl MioListener {
+ pub(crate) fn local_addr(&self) -> SocketAddr {
+ match *self {
+ MioListener::Tcp(ref lst) => lst
+ .local_addr()
+ .map(SocketAddr::Tcp)
+ .unwrap_or(SocketAddr::Unknown),
+ #[cfg(unix)]
+ MioListener::Uds(ref lst) => lst
+ .local_addr()
+ .map(SocketAddr::Uds)
+ .unwrap_or(SocketAddr::Unknown),
+ }
+ }
+
+ pub(crate) fn accept(&self) -> io::Result<MioStream> {
+ match *self {
+ MioListener::Tcp(ref lst) => lst.accept().map(|(stream, _)| MioStream::Tcp(stream)),
+ #[cfg(unix)]
+ MioListener::Uds(ref lst) => lst.accept().map(|(stream, _)| MioStream::Uds(stream)),
+ }
+ }
+}
+
+impl Source for MioListener {
+ fn register(
+ &mut self,
+ registry: &Registry,
+ token: Token,
+ interests: Interest,
+ ) -> io::Result<()> {
+ match *self {
+ MioListener::Tcp(ref mut lst) => lst.register(registry, token, interests),
+ #[cfg(unix)]
+ MioListener::Uds(ref mut lst) => lst.register(registry, token, interests),
+ }
+ }
+
+ fn reregister(
+ &mut self,
+ registry: &Registry,
+ token: Token,
+ interests: Interest,
+ ) -> io::Result<()> {
+ match *self {
+ MioListener::Tcp(ref mut lst) => lst.reregister(registry, token, interests),
+ #[cfg(unix)]
+ MioListener::Uds(ref mut lst) => lst.reregister(registry, token, interests),
+ }
+ }
+
+ fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
+ match *self {
+ MioListener::Tcp(ref mut lst) => lst.deregister(registry),
+ #[cfg(unix)]
+ MioListener::Uds(ref mut lst) => {
+ let res = lst.deregister(registry);
+
+ // cleanup file path
+ if let Ok(addr) = lst.local_addr() {
+ if let Some(path) = addr.as_pathname() {
+ let _ = std::fs::remove_file(path);
+ }
+ }
+ res
+ }
+ }
+ }
+}
+
+impl From<StdTcpListener> for MioListener {
+ fn from(lst: StdTcpListener) -> Self {
+ MioListener::Tcp(MioTcpListener::from_std(lst))
+ }
+}
+
+#[cfg(unix)]
+impl From<StdUnixListener> for MioListener {
+ fn from(lst: StdUnixListener) -> Self {
+ MioListener::Uds(MioUnixListener::from_std(lst))
+ }
+}
+
+impl fmt::Debug for MioListener {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
+ #[cfg(unix)]
+ MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
+ }
+ }
+}
+
+impl fmt::Display for MioListener {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
+ #[cfg(unix)]
+ MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
+ }
+ }
+}
+
+pub(crate) enum SocketAddr {
+ Unknown,
+ Tcp(StdSocketAddr),
+ #[cfg(unix)]
+ Uds(mio::net::SocketAddr),
+}
+
+impl fmt::Display for SocketAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ Self::Unknown => write!(f, "Unknown SocketAddr"),
+ Self::Tcp(ref addr) => write!(f, "{}", addr),
+ #[cfg(unix)]
+ Self::Uds(ref addr) => write!(f, "{:?}", addr),
+ }
+ }
+}
+
+impl fmt::Debug for SocketAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ Self::Unknown => write!(f, "Unknown SocketAddr"),
+ Self::Tcp(ref addr) => write!(f, "{:?}", addr),
+ #[cfg(unix)]
+ Self::Uds(ref addr) => write!(f, "{:?}", addr),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum MioStream {
+ Tcp(mio::net::TcpStream),
+ #[cfg(unix)]
+ Uds(mio::net::UnixStream),
+}
+
+/// Helper trait for converting a Mio stream into a Tokio stream.
+pub trait FromStream: Sized {
+ fn from_mio(sock: MioStream) -> io::Result<Self>;
+}
+
+#[cfg(windows)]
+mod win_impl {
+ use std::os::windows::io::{FromRawSocket, IntoRawSocket};
+
+ use super::*;
+
+ // TODO: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
+ impl FromStream for TcpStream {
+ fn from_mio(sock: MioStream) -> io::Result<Self> {
+ match sock {
+ MioStream::Tcp(mio) => {
+ let raw = IntoRawSocket::into_raw_socket(mio);
+ // SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
+ TcpStream::from_std(unsafe { FromRawSocket::from_raw_socket(raw) })
+ }
+ }
+ }
+ }
+}
+
+#[cfg(unix)]
+mod unix_impl {
+ use std::os::unix::io::{FromRawFd, IntoRawFd};
+
+ use actix_rt::net::UnixStream;
+
+ use super::*;
+
+ // HACK: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
+ impl FromStream for TcpStream {
+ fn from_mio(sock: MioStream) -> io::Result<Self> {
+ match sock {
+ MioStream::Tcp(mio) => {
+ let raw = IntoRawFd::into_raw_fd(mio);
+ // SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
+ TcpStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
+ }
+ MioStream::Uds(_) => {
+ panic!("Should not happen, bug in server impl");
+ }
+ }
+ }
+ }
+
+ // HACK: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
+ impl FromStream for UnixStream {
+ fn from_mio(sock: MioStream) -> io::Result<Self> {
+ match sock {
+ MioStream::Tcp(_) => panic!("Should not happen, bug in server impl"),
+ MioStream::Uds(mio) => {
+ let raw = IntoRawFd::into_raw_fd(mio);
+ // SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
+ UnixStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
+ }
+ }
+ }
+ }
+}
+
+pub(crate) fn create_mio_tcp_listener(
+ addr: StdSocketAddr,
+ backlog: u32,
+ mptcp: &MpTcp,
+) -> io::Result<MioTcpListener> {
+ use socket2::{Domain, Protocol, Socket, Type};
+
+ #[cfg(not(target_os = "linux"))]
+ let protocol = Protocol::TCP;
+ #[cfg(target_os = "linux")]
+ let protocol = if matches!(mptcp, MpTcp::Disabled) {
+ Protocol::TCP
+ } else {
+ Protocol::MPTCP
+ };
+
+ let socket = match Socket::new(Domain::for_address(addr), Type::STREAM, Some(protocol)) {
+ Ok(sock) => sock,
+
+ Err(err) if matches!(mptcp, MpTcp::TcpFallback) => {
+ tracing::warn!("binding socket as MPTCP failed: {err}");
+ tracing::warn!("falling back to TCP");
+ Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?
+ }
+
+ Err(err) => return Err(err),
+ };
+
+ socket.set_reuse_address(true)?;
+ socket.set_nonblocking(true)?;
+ socket.bind(&addr.into())?;
+ socket.listen(backlog as i32)?;
+
+ Ok(MioTcpListener::from_std(StdTcpListener::from(socket)))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn socket_addr() {
+ let addr = SocketAddr::Tcp("127.0.0.1:8080".parse().unwrap());
+ assert!(format!("{:?}", addr).contains("127.0.0.1:8080"));
+ assert_eq!(format!("{}", addr), "127.0.0.1:8080");
+
+ let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
+ let lst = create_mio_tcp_listener(addr, 128, &MpTcp::Disabled).unwrap();
+ let lst = MioListener::Tcp(lst);
+ assert!(format!("{:?}", lst).contains("TcpListener"));
+ assert!(format!("{}", lst).contains("127.0.0.1"));
+ }
+
+ #[test]
+ #[cfg(unix)]
+ fn uds() {
+ let _ = std::fs::remove_file("/tmp/sock.xxxxx");
+ if let Ok(socket) = MioUnixListener::bind("/tmp/sock.xxxxx") {
+ let addr = socket.local_addr().expect("Couldn't get local address");
+ let a = SocketAddr::Uds(addr);
+ assert!(format!("{:?}", a).contains("/tmp/sock.xxxxx"));
+ assert!(format!("{}", a).contains("/tmp/sock.xxxxx"));
+
+ let lst = MioListener::Uds(socket);
+ assert!(format!("{:?}", lst).contains("/tmp/sock.xxxxx"));
+ assert!(format!("{}", lst).contains("/tmp/sock.xxxxx"));
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +
use std::{io, net, sync::mpsc, thread};
+
+use actix_rt::{net::TcpStream, System};
+
+use crate::{Server, ServerBuilder, ServerHandle, ServerServiceFactory};
+
+/// A testing server.
+///
+/// `TestServer` is very simple test server that simplify process of writing integration tests for
+/// network applications.
+///
+/// # Examples
+/// ```
+/// use actix_service::fn_service;
+/// use actix_server::TestServer;
+///
+/// #[actix_rt::main]
+/// async fn main() {
+/// let srv = TestServer::start(|| fn_service(
+/// |sock| async move {
+/// println!("New connection: {:?}", sock);
+/// Ok::<_, ()>(())
+/// }
+/// ));
+///
+/// println!("SOCKET: {:?}", srv.connect());
+/// }
+/// ```
+pub struct TestServer;
+
+/// Test server handle.
+pub struct TestServerHandle {
+ addr: net::SocketAddr,
+ host: String,
+ port: u16,
+ server_handle: ServerHandle,
+ thread_handle: Option<thread::JoinHandle<io::Result<()>>>,
+}
+
+impl TestServer {
+ /// Start new `TestServer` using application factory and default server config.
+ pub fn start(factory: impl ServerServiceFactory<TcpStream>) -> TestServerHandle {
+ Self::start_with_builder(Server::build(), factory)
+ }
+
+ /// Start new `TestServer` using application factory and server builder.
+ pub fn start_with_builder(
+ server_builder: ServerBuilder,
+ factory: impl ServerServiceFactory<TcpStream>,
+ ) -> TestServerHandle {
+ let (tx, rx) = mpsc::channel();
+
+ // run server in separate thread
+ let thread_handle = thread::spawn(move || {
+ let lst = net::TcpListener::bind("127.0.0.1:0").unwrap();
+ let local_addr = lst.local_addr().unwrap();
+
+ System::new().block_on(async {
+ let server = server_builder
+ .listen("test", lst, factory)
+ .unwrap()
+ .workers(1)
+ .disable_signals()
+ .run();
+
+ tx.send((server.handle(), local_addr)).unwrap();
+ server.await
+ })
+ });
+
+ let (server_handle, addr) = rx.recv().unwrap();
+
+ let host = format!("{}", addr.ip());
+ let port = addr.port();
+
+ TestServerHandle {
+ addr,
+ host,
+ port,
+ server_handle,
+ thread_handle: Some(thread_handle),
+ }
+ }
+
+ /// Get first available unused local address.
+ pub fn unused_addr() -> net::SocketAddr {
+ use socket2::{Domain, Protocol, Socket, Type};
+
+ let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
+ let domain = Domain::for_address(addr);
+ let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
+
+ socket.set_reuse_address(true).unwrap();
+ socket.set_nonblocking(true).unwrap();
+ socket.bind(&addr.into()).unwrap();
+ socket.listen(1024).unwrap();
+
+ net::TcpListener::from(socket).local_addr().unwrap()
+ }
+}
+
+impl TestServerHandle {
+ /// Test server host.
+ pub fn host(&self) -> &str {
+ &self.host
+ }
+
+ /// Test server port.
+ pub fn port(&self) -> u16 {
+ self.port
+ }
+
+ /// Get test server address.
+ pub fn addr(&self) -> net::SocketAddr {
+ self.addr
+ }
+
+ /// Stop server.
+ fn stop(&mut self) {
+ drop(self.server_handle.stop(false));
+ self.thread_handle.take().unwrap().join().unwrap().unwrap();
+ }
+
+ /// Connect to server, returning a Tokio `TcpStream`.
+ pub fn connect(&self) -> io::Result<TcpStream> {
+ TcpStream::from_std(net::TcpStream::connect(self.addr)?)
+ }
+}
+
+impl Drop for TestServerHandle {
+ fn drop(&mut self) {
+ self.stop()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use actix_service::fn_service;
+
+ use super::*;
+
+ #[tokio::test]
+ async fn connect_in_tokio_runtime() {
+ let srv = TestServer::start(|| fn_service(|_sock| async move { Ok::<_, ()>(()) }));
+ assert!(srv.connect().is_ok());
+ }
+
+ #[actix_rt::test]
+ async fn connect_in_actix_runtime() {
+ let srv = TestServer::start(|| fn_service(|_sock| async move { Ok::<_, ()>(()) }));
+ assert!(srv.connect().is_ok());
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +
use std::{
+ collections::VecDeque,
+ ops::Deref,
+ sync::{Arc, Mutex, MutexGuard},
+};
+
+use mio::{Registry, Token as MioToken, Waker};
+
+use crate::worker::WorkerHandleAccept;
+
+/// Waker token for `mio::Poll` instance.
+pub(crate) const WAKER_TOKEN: MioToken = MioToken(usize::MAX);
+
+/// `mio::Waker` with a queue for waking up the `Accept`'s `Poll` and contains the `WakerInterest`
+/// the `Poll` would want to look into.
+pub(crate) struct WakerQueue(Arc<(Waker, Mutex<VecDeque<WakerInterest>>)>);
+
+impl Clone for WakerQueue {
+ fn clone(&self) -> Self {
+ Self(self.0.clone())
+ }
+}
+
+impl Deref for WakerQueue {
+ type Target = (Waker, Mutex<VecDeque<WakerInterest>>);
+
+ fn deref(&self) -> &Self::Target {
+ self.0.deref()
+ }
+}
+
+impl WakerQueue {
+ /// Construct a waker queue with given `Poll`'s `Registry` and capacity.
+ ///
+ /// A fixed `WAKER_TOKEN` is used to identify the wake interest and the `Poll` needs to match
+ /// event's token for it to properly handle `WakerInterest`.
+ pub(crate) fn new(registry: &Registry) -> std::io::Result<Self> {
+ let waker = Waker::new(registry, WAKER_TOKEN)?;
+ let queue = Mutex::new(VecDeque::with_capacity(16));
+
+ Ok(Self(Arc::new((waker, queue))))
+ }
+
+ /// Push a new interest to the queue and wake up the accept poll afterwards.
+ pub(crate) fn wake(&self, interest: WakerInterest) {
+ let (waker, queue) = self.deref();
+
+ queue
+ .lock()
+ .expect("Failed to lock WakerQueue")
+ .push_back(interest);
+
+ waker
+ .wake()
+ .unwrap_or_else(|e| panic!("can not wake up Accept Poll: {}", e));
+ }
+
+ /// Get a MutexGuard of the waker queue.
+ pub(crate) fn guard(&self) -> MutexGuard<'_, VecDeque<WakerInterest>> {
+ self.deref().1.lock().expect("Failed to lock WakerQueue")
+ }
+
+ /// Reset the waker queue so it does not grow infinitely.
+ pub(crate) fn reset(queue: &mut VecDeque<WakerInterest>) {
+ std::mem::swap(&mut VecDeque::<WakerInterest>::with_capacity(16), queue);
+ }
+}
+
+/// Types of interests we would look into when `Accept`'s `Poll` is waked up by waker.
+///
+/// These interests should not be confused with `mio::Interest` and mostly not I/O related
+pub(crate) enum WakerInterest {
+ /// `WorkerAvailable` is an interest from `Worker` notifying `Accept` there is a worker
+ /// available and can accept new tasks.
+ WorkerAvailable(usize),
+ /// `Pause`, `Resume`, `Stop` Interest are from `ServerBuilder` future. It listens to
+ /// `ServerCommand` and notify `Accept` to do exactly these tasks.
+ Pause,
+ Resume,
+ Stop,
+ /// `Worker` is an interest that is triggered after a worker faults. This is determined by
+ /// trying to send work to it. `Accept` would be waked up and add the new `WorkerHandleAccept`.
+ Worker(WorkerHandleAccept),
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +
use std::{
+ future::Future,
+ io, mem,
+ num::NonZeroUsize,
+ pin::Pin,
+ rc::Rc,
+ sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+ },
+ task::{Context, Poll},
+ time::Duration,
+};
+
+use actix_rt::{
+ spawn,
+ time::{sleep, Instant, Sleep},
+ Arbiter, ArbiterHandle, System,
+};
+use futures_core::{future::LocalBoxFuture, ready};
+use tokio::sync::{
+ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
+ oneshot,
+};
+use tracing::{error, info, trace};
+
+use crate::{
+ service::{BoxedServerService, InternalServiceFactory},
+ socket::MioStream,
+ waker_queue::{WakerInterest, WakerQueue},
+};
+
+/// Stop worker message. Returns `true` on successful graceful shutdown
+/// and `false` if some connections still alive when shutdown execute.
+pub(crate) struct Stop {
+ graceful: bool,
+ tx: oneshot::Sender<bool>,
+}
+
+#[derive(Debug)]
+pub(crate) struct Conn {
+ pub io: MioStream,
+ pub token: usize,
+}
+
+/// Create accept and server worker handles.
+fn handle_pair(
+ idx: usize,
+ conn_tx: UnboundedSender<Conn>,
+ stop_tx: UnboundedSender<Stop>,
+ counter: Counter,
+) -> (WorkerHandleAccept, WorkerHandleServer) {
+ let accept = WorkerHandleAccept {
+ idx,
+ conn_tx,
+ counter,
+ };
+
+ let server = WorkerHandleServer { idx, stop_tx };
+
+ (accept, server)
+}
+
+/// counter: Arc<AtomicUsize> field is owned by `Accept` thread and `ServerWorker` thread.
+///
+/// `Accept` would increment the counter and `ServerWorker` would decrement it.
+///
+/// # Atomic Ordering:
+///
+/// `Accept` always look into it's cached `Availability` field for `ServerWorker` state.
+/// It lazily increment counter after successful dispatching new work to `ServerWorker`.
+/// On reaching counter limit `Accept` update it's cached `Availability` and mark worker as
+/// unable to accept any work.
+///
+/// `ServerWorker` always decrement the counter when every work received from `Accept` is done.
+/// On reaching counter limit worker would use `mio::Waker` and `WakerQueue` to wake up `Accept`
+/// and notify it to update cached `Availability` again to mark worker as able to accept work again.
+///
+/// Hence, a wake up would only happen after `Accept` increment it to limit.
+/// And a decrement to limit always wake up `Accept`.
+#[derive(Clone)]
+pub(crate) struct Counter {
+ counter: Arc<AtomicUsize>,
+ limit: usize,
+}
+
+impl Counter {
+ pub(crate) fn new(limit: usize) -> Self {
+ Self {
+ counter: Arc::new(AtomicUsize::new(1)),
+ limit,
+ }
+ }
+
+ /// Increment counter by 1 and return true when hitting limit
+ #[inline(always)]
+ pub(crate) fn inc(&self) -> bool {
+ self.counter.fetch_add(1, Ordering::Relaxed) != self.limit
+ }
+
+ /// Decrement counter by 1 and return true if crossing limit.
+ #[inline(always)]
+ pub(crate) fn dec(&self) -> bool {
+ self.counter.fetch_sub(1, Ordering::Relaxed) == self.limit
+ }
+
+ pub(crate) fn total(&self) -> usize {
+ self.counter.load(Ordering::SeqCst) - 1
+ }
+}
+
+pub(crate) struct WorkerCounter {
+ idx: usize,
+ inner: Rc<(WakerQueue, Counter)>,
+}
+
+impl Clone for WorkerCounter {
+ fn clone(&self) -> Self {
+ Self {
+ idx: self.idx,
+ inner: self.inner.clone(),
+ }
+ }
+}
+
+impl WorkerCounter {
+ pub(crate) fn new(idx: usize, waker_queue: WakerQueue, counter: Counter) -> Self {
+ Self {
+ idx,
+ inner: Rc::new((waker_queue, counter)),
+ }
+ }
+
+ #[inline(always)]
+ pub(crate) fn guard(&self) -> WorkerCounterGuard {
+ WorkerCounterGuard(self.clone())
+ }
+
+ fn total(&self) -> usize {
+ self.inner.1.total()
+ }
+}
+
+pub(crate) struct WorkerCounterGuard(WorkerCounter);
+
+impl Drop for WorkerCounterGuard {
+ fn drop(&mut self) {
+ let (waker_queue, counter) = &*self.0.inner;
+ if counter.dec() {
+ waker_queue.wake(WakerInterest::WorkerAvailable(self.0.idx));
+ }
+ }
+}
+
+/// Handle to worker that can send connection message to worker and share the availability of worker
+/// to other threads.
+///
+/// Held by [Accept](crate::accept::Accept).
+pub(crate) struct WorkerHandleAccept {
+ idx: usize,
+ conn_tx: UnboundedSender<Conn>,
+ counter: Counter,
+}
+
+impl WorkerHandleAccept {
+ #[inline(always)]
+ pub(crate) fn idx(&self) -> usize {
+ self.idx
+ }
+
+ #[inline(always)]
+ pub(crate) fn send(&self, conn: Conn) -> Result<(), Conn> {
+ self.conn_tx.send(conn).map_err(|msg| msg.0)
+ }
+
+ #[inline(always)]
+ pub(crate) fn inc_counter(&self) -> bool {
+ self.counter.inc()
+ }
+}
+
+/// Handle to worker than can send stop message to worker.
+///
+/// Held by [ServerBuilder](crate::builder::ServerBuilder).
+#[derive(Debug)]
+pub(crate) struct WorkerHandleServer {
+ pub(crate) idx: usize,
+ stop_tx: UnboundedSender<Stop>,
+}
+
+impl WorkerHandleServer {
+ pub(crate) fn stop(&self, graceful: bool) -> oneshot::Receiver<bool> {
+ let (tx, rx) = oneshot::channel();
+ let _ = self.stop_tx.send(Stop { graceful, tx });
+ rx
+ }
+}
+
+/// Service worker.
+///
+/// Worker accepts Socket objects via unbounded channel and starts stream processing.
+pub(crate) struct ServerWorker {
+ // UnboundedReceiver<Conn> should always be the first field.
+ // It must be dropped as soon as ServerWorker dropping.
+ conn_rx: UnboundedReceiver<Conn>,
+ stop_rx: UnboundedReceiver<Stop>,
+ counter: WorkerCounter,
+ services: Box<[WorkerService]>,
+ factories: Box<[Box<dyn InternalServiceFactory>]>,
+ state: WorkerState,
+ shutdown_timeout: Duration,
+}
+
+struct WorkerService {
+ factory_idx: usize,
+ status: WorkerServiceStatus,
+ service: BoxedServerService,
+}
+
+impl WorkerService {
+ fn created(&mut self, service: BoxedServerService) {
+ self.service = service;
+ self.status = WorkerServiceStatus::Unavailable;
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum WorkerServiceStatus {
+ Available,
+ Unavailable,
+ Failed,
+ Restarting,
+ Stopping,
+ Stopped,
+}
+
+impl Default for WorkerServiceStatus {
+ fn default() -> Self {
+ Self::Unavailable
+ }
+}
+
+/// Config for worker behavior passed down from server builder.
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct ServerWorkerConfig {
+ shutdown_timeout: Duration,
+ max_blocking_threads: usize,
+ max_concurrent_connections: usize,
+}
+
+impl Default for ServerWorkerConfig {
+ fn default() -> Self {
+ let parallelism = std::thread::available_parallelism().map_or(2, NonZeroUsize::get);
+
+ // 512 is the default max blocking thread count of a Tokio runtime.
+ let max_blocking_threads = std::cmp::max(512 / parallelism, 1);
+
+ Self {
+ shutdown_timeout: Duration::from_secs(30),
+ max_blocking_threads,
+ max_concurrent_connections: 25600,
+ }
+ }
+}
+
+impl ServerWorkerConfig {
+ pub(crate) fn max_blocking_threads(&mut self, num: usize) {
+ self.max_blocking_threads = num;
+ }
+
+ pub(crate) fn max_concurrent_connections(&mut self, num: usize) {
+ self.max_concurrent_connections = num;
+ }
+
+ pub(crate) fn shutdown_timeout(&mut self, dur: Duration) {
+ self.shutdown_timeout = dur;
+ }
+}
+
+impl ServerWorker {
+ pub(crate) fn start(
+ idx: usize,
+ factories: Vec<Box<dyn InternalServiceFactory>>,
+ waker_queue: WakerQueue,
+ config: ServerWorkerConfig,
+ ) -> io::Result<(WorkerHandleAccept, WorkerHandleServer)> {
+ trace!("starting server worker {}", idx);
+
+ let (tx1, conn_rx) = unbounded_channel();
+ let (tx2, stop_rx) = unbounded_channel();
+
+ let counter = Counter::new(config.max_concurrent_connections);
+ let pair = handle_pair(idx, tx1, tx2, counter.clone());
+
+ // get actix system context if it is set
+ let actix_system = System::try_current();
+
+ // get tokio runtime handle if it is set
+ let tokio_handle = tokio::runtime::Handle::try_current().ok();
+
+ // service factories initialization channel
+ let (factory_tx, factory_rx) = std::sync::mpsc::sync_channel::<io::Result<()>>(1);
+
+ // outline of following code:
+ //
+ // if system exists
+ // if uring enabled
+ // start arbiter using uring method
+ // else
+ // start arbiter with regular tokio
+ // else
+ // if uring enabled
+ // start uring in spawned thread
+ // else
+ // start regular tokio in spawned thread
+
+ // every worker runs in it's own thread and tokio runtime.
+ // use a custom tokio runtime builder to change the settings of runtime.
+
+ match (actix_system, tokio_handle) {
+ (None, None) => {
+ panic!("No runtime detected. Start a Tokio (or Actix) runtime.");
+ }
+
+ // no actix system
+ (None, Some(rt_handle)) => {
+ std::thread::Builder::new()
+ .name(format!("actix-server worker {}", idx))
+ .spawn(move || {
+ let (worker_stopped_tx, worker_stopped_rx) = oneshot::channel();
+
+ // local set for running service init futures and worker services
+ let ls = tokio::task::LocalSet::new();
+
+ // init services using existing Tokio runtime (so probably on main thread)
+ let services = rt_handle.block_on(ls.run_until(async {
+ let mut services = Vec::new();
+
+ for (idx, factory) in factories.iter().enumerate() {
+ match factory.create().await {
+ Ok((token, svc)) => services.push((idx, token, svc)),
+
+ Err(err) => {
+ error!("can not start worker: {:?}", err);
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("can not start server service {}", idx),
+ ));
+ }
+ }
+ }
+
+ Ok(services)
+ }));
+
+ let services = match services {
+ Ok(services) => {
+ factory_tx.send(Ok(())).unwrap();
+ services
+ }
+ Err(err) => {
+ factory_tx.send(Err(err)).unwrap();
+ return;
+ }
+ };
+
+ let worker_services = wrap_worker_services(services);
+
+ let worker_fut = async move {
+ // spawn to make sure ServerWorker runs as non boxed future.
+ spawn(async move {
+ ServerWorker {
+ conn_rx,
+ stop_rx,
+ services: worker_services.into_boxed_slice(),
+ counter: WorkerCounter::new(idx, waker_queue, counter),
+ factories: factories.into_boxed_slice(),
+ state: WorkerState::default(),
+ shutdown_timeout: config.shutdown_timeout,
+ }
+ .await;
+
+ // wake up outermost task waiting for shutdown
+ worker_stopped_tx.send(()).unwrap();
+ });
+
+ worker_stopped_rx.await.unwrap();
+ };
+
+ #[cfg(all(target_os = "linux", feature = "io-uring"))]
+ {
+ // TODO: pass max blocking thread config when tokio-uring enable configuration
+ // on building runtime.
+ let _ = config.max_blocking_threads;
+ tokio_uring::start(worker_fut);
+ }
+
+ #[cfg(not(all(target_os = "linux", feature = "io-uring")))]
+ {
+ let rt = tokio::runtime::Builder::new_current_thread()
+ .enable_all()
+ .max_blocking_threads(config.max_blocking_threads)
+ .build()
+ .unwrap();
+
+ rt.block_on(ls.run_until(worker_fut));
+ }
+ })
+ .expect("cannot spawn server worker thread");
+ }
+
+ // with actix system
+ (Some(_sys), _) => {
+ #[cfg(all(target_os = "linux", feature = "io-uring"))]
+ let arbiter = {
+ // TODO: pass max blocking thread config when tokio-uring enable configuration
+ // on building runtime.
+ let _ = config.max_blocking_threads;
+ Arbiter::new()
+ };
+
+ #[cfg(not(all(target_os = "linux", feature = "io-uring")))]
+ let arbiter = {
+ Arbiter::with_tokio_rt(move || {
+ tokio::runtime::Builder::new_current_thread()
+ .enable_all()
+ .max_blocking_threads(config.max_blocking_threads)
+ .build()
+ .unwrap()
+ })
+ };
+
+ arbiter.spawn(async move {
+ // spawn_local to run !Send future tasks.
+ spawn(async move {
+ let mut services = Vec::new();
+
+ for (idx, factory) in factories.iter().enumerate() {
+ match factory.create().await {
+ Ok((token, svc)) => services.push((idx, token, svc)),
+
+ Err(err) => {
+ error!("can not start worker: {:?}", err);
+ Arbiter::current().stop();
+ factory_tx
+ .send(Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("can not start server service {}", idx),
+ )))
+ .unwrap();
+ return;
+ }
+ }
+ }
+
+ factory_tx.send(Ok(())).unwrap();
+
+ let worker_services = wrap_worker_services(services);
+
+ // spawn to make sure ServerWorker runs as non boxed future.
+ spawn(ServerWorker {
+ conn_rx,
+ stop_rx,
+ services: worker_services.into_boxed_slice(),
+ counter: WorkerCounter::new(idx, waker_queue, counter),
+ factories: factories.into_boxed_slice(),
+ state: Default::default(),
+ shutdown_timeout: config.shutdown_timeout,
+ });
+ });
+ });
+ }
+ };
+
+ // wait for service factories initialization
+ factory_rx.recv().unwrap()?;
+
+ Ok(pair)
+ }
+
+ fn restart_service(&mut self, idx: usize, factory_id: usize) {
+ let factory = &self.factories[factory_id];
+ trace!("service {:?} failed, restarting", factory.name(idx));
+ self.services[idx].status = WorkerServiceStatus::Restarting;
+ self.state = WorkerState::Restarting(Restart {
+ factory_id,
+ token: idx,
+ fut: factory.create(),
+ });
+ }
+
+ fn shutdown(&mut self, force: bool) {
+ self.services
+ .iter_mut()
+ .filter(|srv| srv.status == WorkerServiceStatus::Available)
+ .for_each(|srv| {
+ srv.status = if force {
+ WorkerServiceStatus::Stopped
+ } else {
+ WorkerServiceStatus::Stopping
+ };
+ });
+ }
+
+ fn check_readiness(&mut self, cx: &mut Context<'_>) -> Result<bool, (usize, usize)> {
+ let mut ready = true;
+ for (idx, srv) in self.services.iter_mut().enumerate() {
+ if srv.status == WorkerServiceStatus::Available
+ || srv.status == WorkerServiceStatus::Unavailable
+ {
+ match srv.service.poll_ready(cx) {
+ Poll::Ready(Ok(_)) => {
+ if srv.status == WorkerServiceStatus::Unavailable {
+ trace!(
+ "service {:?} is available",
+ self.factories[srv.factory_idx].name(idx)
+ );
+ srv.status = WorkerServiceStatus::Available;
+ }
+ }
+ Poll::Pending => {
+ ready = false;
+
+ if srv.status == WorkerServiceStatus::Available {
+ trace!(
+ "service {:?} is unavailable",
+ self.factories[srv.factory_idx].name(idx)
+ );
+ srv.status = WorkerServiceStatus::Unavailable;
+ }
+ }
+ Poll::Ready(Err(_)) => {
+ error!(
+ "service {:?} readiness check returned error, restarting",
+ self.factories[srv.factory_idx].name(idx)
+ );
+ srv.status = WorkerServiceStatus::Failed;
+ return Err((idx, srv.factory_idx));
+ }
+ }
+ }
+ }
+
+ Ok(ready)
+ }
+}
+
+enum WorkerState {
+ Available,
+ Unavailable,
+ Restarting(Restart),
+ Shutdown(Shutdown),
+}
+
+struct Restart {
+ factory_id: usize,
+ token: usize,
+ fut: LocalBoxFuture<'static, Result<(usize, BoxedServerService), ()>>,
+}
+
+/// State necessary for server shutdown.
+struct Shutdown {
+ // Interval for checking the shutdown progress.
+ timer: Pin<Box<Sleep>>,
+
+ /// Start time of shutdown.
+ start_from: Instant,
+
+ /// Notify caller of the shutdown outcome (graceful/force).
+ tx: oneshot::Sender<bool>,
+}
+
+impl Default for WorkerState {
+ fn default() -> Self {
+ Self::Unavailable
+ }
+}
+
+impl Drop for ServerWorker {
+ fn drop(&mut self) {
+ Arbiter::try_current().as_ref().map(ArbiterHandle::stop);
+ }
+}
+
+impl Future for ServerWorker {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.as_mut().get_mut();
+
+ // `StopWorker` message handler
+ if let Poll::Ready(Some(Stop { graceful, tx })) = this.stop_rx.poll_recv(cx) {
+ let num = this.counter.total();
+ if num == 0 {
+ info!("shutting down idle worker");
+ let _ = tx.send(true);
+ return Poll::Ready(());
+ } else if graceful {
+ info!("graceful worker shutdown; finishing {} connections", num);
+ this.shutdown(false);
+
+ this.state = WorkerState::Shutdown(Shutdown {
+ timer: Box::pin(sleep(Duration::from_secs(1))),
+ start_from: Instant::now(),
+ tx,
+ });
+ } else {
+ info!("force shutdown worker, closing {} connections", num);
+ this.shutdown(true);
+
+ let _ = tx.send(false);
+ return Poll::Ready(());
+ }
+ }
+
+ match this.state {
+ WorkerState::Unavailable => match this.check_readiness(cx) {
+ Ok(true) => {
+ this.state = WorkerState::Available;
+ self.poll(cx)
+ }
+ Ok(false) => Poll::Pending,
+ Err((token, idx)) => {
+ this.restart_service(token, idx);
+ self.poll(cx)
+ }
+ },
+
+ WorkerState::Restarting(ref mut restart) => {
+ let factory_id = restart.factory_id;
+ let token = restart.token;
+
+ let (token_new, service) =
+ ready!(restart.fut.as_mut().poll(cx)).unwrap_or_else(|_| {
+ panic!(
+ "Can not restart {:?} service",
+ this.factories[factory_id].name(token)
+ )
+ });
+
+ assert_eq!(token, token_new);
+
+ trace!(
+ "service {:?} has been restarted",
+ this.factories[factory_id].name(token)
+ );
+
+ this.services[token].created(service);
+ this.state = WorkerState::Unavailable;
+
+ self.poll(cx)
+ }
+
+ WorkerState::Shutdown(ref mut shutdown) => {
+ // drop all pending connections in rx channel.
+ while let Poll::Ready(Some(conn)) = this.conn_rx.poll_recv(cx) {
+ // WorkerCounterGuard is needed as Accept thread has incremented counter.
+ // It's guard's job to decrement the counter together with drop of Conn.
+ let guard = this.counter.guard();
+ drop((conn, guard));
+ }
+
+ // wait for 1 second
+ ready!(shutdown.timer.as_mut().poll(cx));
+
+ if this.counter.total() == 0 {
+ // graceful shutdown
+ if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) {
+ let _ = shutdown.tx.send(true);
+ }
+
+ Poll::Ready(())
+ } else if shutdown.start_from.elapsed() >= this.shutdown_timeout {
+ // timeout forceful shutdown
+ if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) {
+ let _ = shutdown.tx.send(false);
+ }
+
+ Poll::Ready(())
+ } else {
+ // reset timer and wait for 1 second
+ let time = Instant::now() + Duration::from_secs(1);
+ shutdown.timer.as_mut().reset(time);
+ shutdown.timer.as_mut().poll(cx)
+ }
+ }
+
+ // actively poll stream and handle worker command
+ WorkerState::Available => loop {
+ match this.check_readiness(cx) {
+ Ok(true) => {}
+ Ok(false) => {
+ trace!("worker is unavailable");
+ this.state = WorkerState::Unavailable;
+ return self.poll(cx);
+ }
+ Err((token, idx)) => {
+ this.restart_service(token, idx);
+ return self.poll(cx);
+ }
+ }
+
+ // handle incoming io stream
+ match ready!(this.conn_rx.poll_recv(cx)) {
+ Some(msg) => {
+ let guard = this.counter.guard();
+ let _ = this.services[msg.token]
+ .service
+ .call((guard, msg.io))
+ .into_inner();
+ }
+ None => return Poll::Ready(()),
+ };
+ },
+ }
+ }
+}
+
+fn wrap_worker_services(services: Vec<(usize, usize, BoxedServerService)>) -> Vec<WorkerService> {
+ services
+ .into_iter()
+ .fold(Vec::new(), |mut services, (idx, token, service)| {
+ assert_eq!(token, services.len());
+ services.push(WorkerService {
+ factory_idx: idx,
+ service,
+ status: WorkerServiceStatus::Unavailable,
+ });
+ services
+ })
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +
use alloc::rc::Rc;
+use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use futures_core::ready;
+use pin_project_lite::pin_project;
+
+use super::{Service, ServiceFactory};
+
+/// Service for the `and_then` combinator, chaining a computation onto the end of another service
+/// which completes successfully.
+///
+/// This is created by the `Pipeline::and_then` method.
+pub struct AndThenService<A, B, Req>(Rc<(A, B)>, PhantomData<Req>);
+
+impl<A, B, Req> AndThenService<A, B, Req> {
+ /// Create new `AndThen` combinator
+ pub(crate) fn new(a: A, b: B) -> Self
+ where
+ A: Service<Req>,
+ B: Service<A::Response, Error = A::Error>,
+ {
+ Self(Rc::new((a, b)), PhantomData)
+ }
+}
+
+impl<A, B, Req> Clone for AndThenService<A, B, Req> {
+ fn clone(&self) -> Self {
+ AndThenService(self.0.clone(), PhantomData)
+ }
+}
+
+impl<A, B, Req> Service<Req> for AndThenService<A, B, Req>
+where
+ A: Service<Req>,
+ B: Service<A::Response, Error = A::Error>,
+{
+ type Response = B::Response;
+ type Error = A::Error;
+ type Future = AndThenServiceResponse<A, B, Req>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ let (a, b) = &*self.0;
+ let not_ready = !a.poll_ready(cx)?.is_ready();
+ if !b.poll_ready(cx)?.is_ready() || not_ready {
+ Poll::Pending
+ } else {
+ Poll::Ready(Ok(()))
+ }
+ }
+
+ fn call(&self, req: Req) -> Self::Future {
+ AndThenServiceResponse {
+ state: State::A {
+ fut: self.0 .0.call(req),
+ b: Some(self.0.clone()),
+ },
+ }
+ }
+}
+
+pin_project! {
+ pub struct AndThenServiceResponse<A, B, Req>
+ where
+ A: Service<Req>,
+ B: Service<A::Response, Error = A::Error>,
+ {
+ #[pin]
+ state: State<A, B, Req>,
+ }
+}
+
+pin_project! {
+ #[project = StateProj]
+ enum State<A, B, Req>
+ where
+ A: Service<Req>,
+ B: Service<A::Response, Error = A::Error>,
+ {
+ A {
+ #[pin]
+ fut: A::Future,
+ b: Option<Rc<(A, B)>>,
+ },
+ B {
+ #[pin]
+ fut: B::Future,
+ },
+ }
+}
+
+impl<A, B, Req> Future for AndThenServiceResponse<A, B, Req>
+where
+ A: Service<Req>,
+ B: Service<A::Response, Error = A::Error>,
+{
+ type Output = Result<B::Response, A::Error>;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.as_mut().project();
+
+ match this.state.as_mut().project() {
+ StateProj::A { fut, b } => {
+ let res = ready!(fut.poll(cx))?;
+ let b = b.take().unwrap();
+ let fut = b.1.call(res);
+ this.state.set(State::B { fut });
+ self.poll(cx)
+ }
+ StateProj::B { fut } => fut.poll(cx),
+ }
+ }
+}
+
+/// `.and_then()` service factory combinator
+pub struct AndThenServiceFactory<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ A::Config: Clone,
+ B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
+{
+ inner: Rc<(A, B)>,
+ _phantom: PhantomData<Req>,
+}
+
+impl<A, B, Req> AndThenServiceFactory<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ A::Config: Clone,
+ B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
+{
+ /// Create new `AndThenFactory` combinator
+ pub(crate) fn new(a: A, b: B) -> Self {
+ Self {
+ inner: Rc::new((a, b)),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<A, B, Req> ServiceFactory<Req> for AndThenServiceFactory<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ A::Config: Clone,
+ B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
+{
+ type Response = B::Response;
+ type Error = A::Error;
+
+ type Config = A::Config;
+ type Service = AndThenService<A::Service, B::Service, Req>;
+ type InitError = A::InitError;
+ type Future = AndThenServiceFactoryResponse<A, B, Req>;
+
+ fn new_service(&self, cfg: A::Config) -> Self::Future {
+ let inner = &*self.inner;
+ AndThenServiceFactoryResponse::new(
+ inner.0.new_service(cfg.clone()),
+ inner.1.new_service(cfg),
+ )
+ }
+}
+
+impl<A, B, Req> Clone for AndThenServiceFactory<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ A::Config: Clone,
+ B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
+{
+ fn clone(&self) -> Self {
+ Self {
+ inner: self.inner.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+pin_project! {
+ pub struct AndThenServiceFactoryResponse<A, B, Req>
+ where
+ A: ServiceFactory<Req>,
+ B: ServiceFactory<A::Response>,
+ {
+ #[pin]
+ fut_a: A::Future,
+ #[pin]
+ fut_b: B::Future,
+
+ a: Option<A::Service>,
+ b: Option<B::Service>,
+ }
+}
+
+impl<A, B, Req> AndThenServiceFactoryResponse<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ B: ServiceFactory<A::Response>,
+{
+ fn new(fut_a: A::Future, fut_b: B::Future) -> Self {
+ AndThenServiceFactoryResponse {
+ fut_a,
+ fut_b,
+ a: None,
+ b: None,
+ }
+ }
+}
+
+impl<A, B, Req> Future for AndThenServiceFactoryResponse<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ B: ServiceFactory<A::Response, Error = A::Error, InitError = A::InitError>,
+{
+ type Output = Result<AndThenService<A::Service, B::Service, Req>, A::InitError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+
+ if this.a.is_none() {
+ if let Poll::Ready(service) = this.fut_a.poll(cx)? {
+ *this.a = Some(service);
+ }
+ }
+ if this.b.is_none() {
+ if let Poll::Ready(service) = this.fut_b.poll(cx)? {
+ *this.b = Some(service);
+ }
+ }
+ if this.a.is_some() && this.b.is_some() {
+ Poll::Ready(Ok(AndThenService::new(
+ this.a.take().unwrap(),
+ this.b.take().unwrap(),
+ )))
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::rc::Rc;
+ use core::{
+ cell::Cell,
+ task::{Context, Poll},
+ };
+
+ use futures_util::future::lazy;
+
+ use crate::{
+ fn_factory, ok,
+ pipeline::{pipeline, pipeline_factory},
+ ready, Ready, Service, ServiceFactory,
+ };
+
+ struct Srv1(Rc<Cell<usize>>);
+
+ impl Service<&'static str> for Srv1 {
+ type Response = &'static str;
+ type Error = ();
+ type Future = Ready<Result<Self::Response, ()>>;
+
+ fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.0.set(self.0.get() + 1);
+ Poll::Ready(Ok(()))
+ }
+
+ fn call(&self, req: &'static str) -> Self::Future {
+ ok(req)
+ }
+ }
+
+ #[derive(Clone)]
+ struct Srv2(Rc<Cell<usize>>);
+
+ impl Service<&'static str> for Srv2 {
+ type Response = (&'static str, &'static str);
+ type Error = ();
+ type Future = Ready<Result<Self::Response, ()>>;
+
+ fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.0.set(self.0.get() + 1);
+ Poll::Ready(Ok(()))
+ }
+
+ fn call(&self, req: &'static str) -> Self::Future {
+ ok((req, "srv2"))
+ }
+ }
+
+ #[actix_rt::test]
+ async fn test_poll_ready() {
+ let cnt = Rc::new(Cell::new(0));
+ let srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt.clone()));
+ let res = lazy(|cx| srv.poll_ready(cx)).await;
+ assert_eq!(res, Poll::Ready(Ok(())));
+ assert_eq!(cnt.get(), 2);
+ }
+
+ #[actix_rt::test]
+ async fn test_call() {
+ let cnt = Rc::new(Cell::new(0));
+ let srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt));
+ let res = srv.call("srv1").await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv1", "srv2"));
+ }
+
+ #[actix_rt::test]
+ async fn test_new_service() {
+ let cnt = Rc::new(Cell::new(0));
+ let cnt2 = cnt.clone();
+ let new_srv = pipeline_factory(fn_factory(move || ready(Ok::<_, ()>(Srv1(cnt2.clone())))))
+ .and_then(move || ready(Ok(Srv2(cnt.clone()))));
+
+ let srv = new_srv.new_service(()).await.unwrap();
+ let res = srv.call("srv1").await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv1", "srv2"));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +
use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use futures_core::ready;
+use pin_project_lite::pin_project;
+
+use super::{IntoService, IntoServiceFactory, Service, ServiceFactory};
+
+/// Apply transform function to a service.
+///
+/// The In and Out type params refer to the request and response types for the wrapped service.
+pub fn apply_fn<I, S, F, Fut, Req, In, Res, Err>(
+ service: I,
+ wrap_fn: F,
+) -> Apply<S, F, Req, In, Res, Err>
+where
+ I: IntoService<S, In>,
+ S: Service<In, Error = Err>,
+ F: Fn(Req, &S) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ Apply::new(service.into_service(), wrap_fn)
+}
+
+/// Service factory that produces `apply_fn` service.
+///
+/// The In and Out type params refer to the request and response types for the wrapped service.
+pub fn apply_fn_factory<I, SF, F, Fut, Req, In, Res, Err>(
+ service: I,
+ f: F,
+) -> ApplyFactory<SF, F, Req, In, Res, Err>
+where
+ I: IntoServiceFactory<SF, In>,
+ SF: ServiceFactory<In, Error = Err>,
+ F: Fn(Req, &SF::Service) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ ApplyFactory::new(service.into_factory(), f)
+}
+
+/// `Apply` service combinator.
+///
+/// The In and Out type params refer to the request and response types for the wrapped service.
+pub struct Apply<S, F, Req, In, Res, Err>
+where
+ S: Service<In, Error = Err>,
+{
+ service: S,
+ wrap_fn: F,
+ _phantom: PhantomData<fn(Req) -> (In, Res, Err)>,
+}
+
+impl<S, F, Fut, Req, In, Res, Err> Apply<S, F, Req, In, Res, Err>
+where
+ S: Service<In, Error = Err>,
+ F: Fn(Req, &S) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ /// Create new `Apply` combinator
+ fn new(service: S, wrap_fn: F) -> Self {
+ Self {
+ service,
+ wrap_fn,
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<S, F, Fut, Req, In, Res, Err> Clone for Apply<S, F, Req, In, Res, Err>
+where
+ S: Service<In, Error = Err> + Clone,
+ F: Fn(Req, &S) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn clone(&self) -> Self {
+ Apply {
+ service: self.service.clone(),
+ wrap_fn: self.wrap_fn.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<S, F, Fut, Req, In, Res, Err> Service<Req> for Apply<S, F, Req, In, Res, Err>
+where
+ S: Service<In, Error = Err>,
+ F: Fn(Req, &S) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ type Response = Res;
+ type Error = Err;
+ type Future = Fut;
+
+ crate::forward_ready!(service);
+
+ fn call(&self, req: Req) -> Self::Future {
+ (self.wrap_fn)(req, &self.service)
+ }
+}
+
+/// `ApplyFactory` service factory combinator.
+pub struct ApplyFactory<SF, F, Req, In, Res, Err> {
+ factory: SF,
+ wrap_fn: F,
+ _phantom: PhantomData<fn(Req) -> (In, Res, Err)>,
+}
+
+impl<SF, F, Fut, Req, In, Res, Err> ApplyFactory<SF, F, Req, In, Res, Err>
+where
+ SF: ServiceFactory<In, Error = Err>,
+ F: Fn(Req, &SF::Service) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ /// Create new `ApplyFactory` new service instance
+ fn new(factory: SF, wrap_fn: F) -> Self {
+ Self {
+ factory,
+ wrap_fn,
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<SF, F, Fut, Req, In, Res, Err> Clone for ApplyFactory<SF, F, Req, In, Res, Err>
+where
+ SF: ServiceFactory<In, Error = Err> + Clone,
+ F: Fn(Req, &SF::Service) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn clone(&self) -> Self {
+ Self {
+ factory: self.factory.clone(),
+ wrap_fn: self.wrap_fn.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req> for ApplyFactory<SF, F, Req, In, Res, Err>
+where
+ SF: ServiceFactory<In, Error = Err>,
+ F: Fn(Req, &SF::Service) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ type Response = Res;
+ type Error = Err;
+
+ type Config = SF::Config;
+ type Service = Apply<SF::Service, F, Req, In, Res, Err>;
+ type InitError = SF::InitError;
+ type Future = ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>;
+
+ fn new_service(&self, cfg: SF::Config) -> Self::Future {
+ let svc = self.factory.new_service(cfg);
+ ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone())
+ }
+}
+
+pin_project! {
+ pub struct ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
+ where
+ SF: ServiceFactory<In, Error = Err>,
+ F: Fn(Req, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+ {
+ #[pin]
+ fut: SF::Future,
+ wrap_fn: Option<F>,
+ _phantom: PhantomData<fn(Req) -> Res>,
+ }
+}
+
+impl<SF, F, Fut, Req, In, Res, Err> ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
+where
+ SF: ServiceFactory<In, Error = Err>,
+ F: Fn(Req, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn new(fut: SF::Future, wrap_fn: F) -> Self {
+ Self {
+ fut,
+ wrap_fn: Some(wrap_fn),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<SF, F, Fut, Req, In, Res, Err> Future
+ for ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
+where
+ SF: ServiceFactory<In, Error = Err>,
+ F: Fn(Req, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ type Output = Result<Apply<SF::Service, F, Req, In, Res, Err>, SF::InitError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+
+ let svc = ready!(this.fut.poll(cx))?;
+ Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap())))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use futures_util::future::lazy;
+
+ use super::*;
+ use crate::{
+ ok,
+ pipeline::{pipeline, pipeline_factory},
+ Ready,
+ };
+
+ #[derive(Clone)]
+ struct Srv;
+
+ impl Service<()> for Srv {
+ type Response = ();
+ type Error = ();
+ type Future = Ready<Result<(), ()>>;
+
+ crate::always_ready!();
+
+ fn call(&self, _: ()) -> Self::Future {
+ ok(())
+ }
+ }
+
+ #[actix_rt::test]
+ async fn test_call() {
+ let srv = pipeline(apply_fn(Srv, |req: &'static str, srv| {
+ let fut = srv.call(());
+ async move {
+ fut.await.unwrap();
+ Ok((req, ()))
+ }
+ }));
+
+ assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
+
+ let res = srv.call("srv").await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv", ()));
+ }
+
+ #[actix_rt::test]
+ async fn test_new_service() {
+ let new_srv = pipeline_factory(apply_fn_factory(
+ || ok::<_, ()>(Srv),
+ |req: &'static str, srv| {
+ let fut = srv.call(());
+ async move {
+ fut.await.unwrap();
+ Ok((req, ()))
+ }
+ },
+ ));
+
+ let srv = new_srv.new_service(()).await.unwrap();
+
+ assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
+
+ let res = srv.call("srv").await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv", ()));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +
use alloc::rc::Rc;
+use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use futures_core::ready;
+use pin_project_lite::pin_project;
+
+use crate::{Service, ServiceFactory};
+
+/// Convert `Fn(Config, &Service1) -> Future<Service2>` fn to a service factory.
+pub fn apply_cfg<S1, Req, F, Cfg, Fut, S2, Err>(
+ srv: S1,
+ f: F,
+) -> impl ServiceFactory<
+ Req,
+ Config = Cfg,
+ Response = S2::Response,
+ Error = S2::Error,
+ Service = S2,
+ InitError = Err,
+ Future = Fut,
+> + Clone
+where
+ S1: Service<Req>,
+ F: Fn(Cfg, &S1) -> Fut,
+ Fut: Future<Output = Result<S2, Err>>,
+ S2: Service<Req>,
+{
+ ApplyConfigService {
+ srv: Rc::new((srv, f)),
+ _phantom: PhantomData,
+ }
+}
+
+/// Convert `Fn(Config, &ServiceFactory1) -> Future<ServiceFactory2>` fn to a service factory.
+///
+/// Service1 get constructed from `T` factory.
+pub fn apply_cfg_factory<SF, Req, F, Cfg, Fut, S>(
+ factory: SF,
+ f: F,
+) -> impl ServiceFactory<
+ Req,
+ Config = Cfg,
+ Response = S::Response,
+ Error = S::Error,
+ Service = S,
+ InitError = SF::InitError,
+> + Clone
+where
+ SF: ServiceFactory<Req, Config = ()>,
+ F: Fn(Cfg, &SF::Service) -> Fut,
+ SF::InitError: From<SF::Error>,
+ Fut: Future<Output = Result<S, SF::InitError>>,
+ S: Service<Req>,
+{
+ ApplyConfigServiceFactory {
+ srv: Rc::new((factory, f)),
+ _phantom: PhantomData,
+ }
+}
+
+/// Convert `Fn(Config, &Server) -> Future<Service>` fn to NewService\
+struct ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
+where
+ S1: Service<Req>,
+ F: Fn(Cfg, &S1) -> Fut,
+ Fut: Future<Output = Result<S2, Err>>,
+ S2: Service<Req>,
+{
+ srv: Rc<(S1, F)>,
+ _phantom: PhantomData<(Cfg, Req, Fut, S2)>,
+}
+
+impl<S1, Req, F, Cfg, Fut, S2, Err> Clone for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
+where
+ S1: Service<Req>,
+ F: Fn(Cfg, &S1) -> Fut,
+ Fut: Future<Output = Result<S2, Err>>,
+ S2: Service<Req>,
+{
+ fn clone(&self) -> Self {
+ ApplyConfigService {
+ srv: self.srv.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<S1, Req, F, Cfg, Fut, S2, Err> ServiceFactory<Req>
+ for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
+where
+ S1: Service<Req>,
+ F: Fn(Cfg, &S1) -> Fut,
+ Fut: Future<Output = Result<S2, Err>>,
+ S2: Service<Req>,
+{
+ type Response = S2::Response;
+ type Error = S2::Error;
+ type Config = Cfg;
+ type Service = S2;
+
+ type InitError = Err;
+ type Future = Fut;
+
+ fn new_service(&self, cfg: Cfg) -> Self::Future {
+ let (t, f) = &*self.srv;
+ f(cfg, t)
+ }
+}
+
+/// Convert `Fn(&Config) -> Future<Service>` fn to NewService
+struct ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
+where
+ SF: ServiceFactory<Req, Config = ()>,
+ F: Fn(Cfg, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<S, SF::InitError>>,
+ S: Service<Req>,
+{
+ srv: Rc<(SF, F)>,
+ _phantom: PhantomData<(Cfg, Req, Fut, S)>,
+}
+
+impl<SF, Req, F, Cfg, Fut, S> Clone for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
+where
+ SF: ServiceFactory<Req, Config = ()>,
+ F: Fn(Cfg, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<S, SF::InitError>>,
+ S: Service<Req>,
+{
+ fn clone(&self) -> Self {
+ Self {
+ srv: self.srv.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<SF, Req, F, Cfg, Fut, S> ServiceFactory<Req>
+ for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
+where
+ SF: ServiceFactory<Req, Config = ()>,
+ SF::InitError: From<SF::Error>,
+ F: Fn(Cfg, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<S, SF::InitError>>,
+ S: Service<Req>,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Config = Cfg;
+ type Service = S;
+
+ type InitError = SF::InitError;
+ type Future = ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>;
+
+ fn new_service(&self, cfg: Cfg) -> Self::Future {
+ ApplyConfigServiceFactoryResponse {
+ cfg: Some(cfg),
+ store: self.srv.clone(),
+ state: State::A {
+ fut: self.srv.0.new_service(()),
+ },
+ }
+ }
+}
+
+pin_project! {
+ struct ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
+ where
+ SF: ServiceFactory<Req, Config = ()>,
+ SF::InitError: From<SF::Error>,
+ F: Fn(Cfg, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<S, SF::InitError>>,
+ S: Service<Req>,
+ {
+ cfg: Option<Cfg>,
+ store: Rc<(SF, F)>,
+ #[pin]
+ state: State<SF, Fut, S, Req>,
+ }
+}
+
+pin_project! {
+ #[project = StateProj]
+ enum State<SF, Fut, S, Req>
+ where
+ SF: ServiceFactory<Req, Config = ()>,
+ SF::InitError: From<SF::Error>,
+ Fut: Future<Output = Result<S, SF::InitError>>,
+ S: Service<Req>,
+ {
+ A { #[pin] fut: SF::Future },
+ B { svc: SF::Service },
+ C { #[pin] fut: Fut },
+ }
+}
+
+impl<SF, Req, F, Cfg, Fut, S> Future for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
+where
+ SF: ServiceFactory<Req, Config = ()>,
+ SF::InitError: From<SF::Error>,
+ F: Fn(Cfg, &SF::Service) -> Fut,
+ Fut: Future<Output = Result<S, SF::InitError>>,
+ S: Service<Req>,
+{
+ type Output = Result<S, SF::InitError>;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.as_mut().project();
+
+ match this.state.as_mut().project() {
+ StateProj::A { fut } => {
+ let svc = ready!(fut.poll(cx))?;
+ this.state.set(State::B { svc });
+ self.poll(cx)
+ }
+ StateProj::B { svc } => {
+ ready!(svc.poll_ready(cx))?;
+ {
+ let (_, f) = &**this.store;
+ let fut = f(this.cfg.take().unwrap(), svc);
+ this.state.set(State::C { fut });
+ }
+ self.poll(cx)
+ }
+ StateProj::C { fut } => fut.poll(cx),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +
//! Trait object forms of services and service factories.
+
+use alloc::{boxed::Box, rc::Rc};
+use core::{future::Future, pin::Pin};
+
+use paste::paste;
+
+use crate::{Service, ServiceFactory};
+
+/// A boxed future with no send bound or lifetime parameters.
+pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
+
+macro_rules! service_object {
+ ($name: ident, $type: tt, $fn_name: ident) => {
+ paste! {
+ #[doc = "Type alias for service trait object using `" $type "`."]
+ pub type $name<Req, Res, Err> = $type<
+ dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>,
+ >;
+
+ #[doc = "Wraps service as a trait object using [`" $name "`]."]
+ pub fn $fn_name<S, Req>(service: S) -> $name<Req, S::Response, S::Error>
+ where
+ S: Service<Req> + 'static,
+ Req: 'static,
+ S::Future: 'static,
+ {
+ $type::new(ServiceWrapper::new(service))
+ }
+ }
+ };
+}
+
+service_object!(BoxService, Box, service);
+service_object!(RcService, Rc, rc_service);
+
+struct ServiceWrapper<S> {
+ inner: S,
+}
+
+impl<S> ServiceWrapper<S> {
+ fn new(inner: S) -> Self {
+ Self { inner }
+ }
+}
+
+impl<S, Req, Res, Err> Service<Req> for ServiceWrapper<S>
+where
+ S: Service<Req, Response = Res, Error = Err>,
+ S::Future: 'static,
+{
+ type Response = Res;
+ type Error = Err;
+ type Future = BoxFuture<Result<Res, Err>>;
+
+ crate::forward_ready!(inner);
+
+ fn call(&self, req: Req) -> Self::Future {
+ Box::pin(self.inner.call(req))
+ }
+}
+
+/// Wrapper for a service factory that will map it's services to boxed trait object services.
+pub struct BoxServiceFactory<Cfg, Req, Res, Err, InitErr>(Inner<Cfg, Req, Res, Err, InitErr>);
+
+/// Wraps a service factory that returns service trait objects.
+pub fn factory<SF, Req>(
+ factory: SF,
+) -> BoxServiceFactory<SF::Config, Req, SF::Response, SF::Error, SF::InitError>
+where
+ SF: ServiceFactory<Req> + 'static,
+ Req: 'static,
+ SF::Response: 'static,
+ SF::Service: 'static,
+ SF::Future: 'static,
+ SF::Error: 'static,
+ SF::InitError: 'static,
+{
+ BoxServiceFactory(Box::new(FactoryWrapper(factory)))
+}
+
+type Inner<C, Req, Res, Err, InitErr> = Box<
+ dyn ServiceFactory<
+ Req,
+ Config = C,
+ Response = Res,
+ Error = Err,
+ InitError = InitErr,
+ Service = BoxService<Req, Res, Err>,
+ Future = BoxFuture<Result<BoxService<Req, Res, Err>, InitErr>>,
+ >,
+>;
+
+impl<C, Req, Res, Err, InitErr> ServiceFactory<Req> for BoxServiceFactory<C, Req, Res, Err, InitErr>
+where
+ Req: 'static,
+ Res: 'static,
+ Err: 'static,
+ InitErr: 'static,
+{
+ type Response = Res;
+ type Error = Err;
+ type Config = C;
+ type Service = BoxService<Req, Res, Err>;
+ type InitError = InitErr;
+
+ type Future = BoxFuture<Result<Self::Service, InitErr>>;
+
+ fn new_service(&self, cfg: C) -> Self::Future {
+ self.0.new_service(cfg)
+ }
+}
+
+struct FactoryWrapper<SF>(SF);
+
+impl<SF, Req, Cfg, Res, Err, InitErr> ServiceFactory<Req> for FactoryWrapper<SF>
+where
+ Req: 'static,
+ Res: 'static,
+ Err: 'static,
+ InitErr: 'static,
+ SF: ServiceFactory<Req, Config = Cfg, Response = Res, Error = Err, InitError = InitErr>,
+ SF::Future: 'static,
+ SF::Service: 'static,
+ <SF::Service as Service<Req>>::Future: 'static,
+{
+ type Response = Res;
+ type Error = Err;
+ type Config = Cfg;
+ type Service = BoxService<Req, Res, Err>;
+ type InitError = InitErr;
+ type Future = BoxFuture<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, cfg: Cfg) -> Self::Future {
+ let f = self.0.new_service(cfg);
+ Box::pin(async { f.await.map(|s| Box::new(ServiceWrapper::new(s)) as _) })
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +
use crate::{
+ and_then::{AndThenService, AndThenServiceFactory},
+ map::Map,
+ map_err::MapErr,
+ transform_err::TransformMapInitErr,
+ IntoService, IntoServiceFactory, Service, ServiceFactory, Transform,
+};
+
+/// An extension trait for [`Service`]s that provides a variety of convenient adapters.
+pub trait ServiceExt<Req>: Service<Req> {
+ /// Map this service's output to a different type, returning a new service
+ /// of the resulting type.
+ ///
+ /// This function is similar to the `Option::map` or `Iterator::map` where
+ /// it will change the type of the underlying service.
+ ///
+ /// Note that this function consumes the receiving service and returns a
+ /// wrapped version of it, similar to the existing `map` methods in the
+ /// standard library.
+ fn map<F, R>(self, f: F) -> Map<Self, F, Req, R>
+ where
+ Self: Sized,
+ F: FnMut(Self::Response) -> R,
+ {
+ Map::new(self, f)
+ }
+
+ /// Map this service's error to a different error, returning a new service.
+ ///
+ /// This function is similar to the `Result::map_err` where it will change
+ /// the error type of the underlying service. For example, this can be useful to
+ /// ensure that services have the same error type.
+ ///
+ /// Note that this function consumes the receiving service and returns a
+ /// wrapped version of it.
+ fn map_err<F, E>(self, f: F) -> MapErr<Self, Req, F, E>
+ where
+ Self: Sized,
+ F: Fn(Self::Error) -> E,
+ {
+ MapErr::new(self, f)
+ }
+
+ /// Call another service after call to this one has resolved successfully.
+ ///
+ /// This function can be used to chain two services together and ensure that the second service
+ /// isn't called until call to the fist service have finished. Result of the call to the first
+ /// service is used as an input parameter for the second service's call.
+ ///
+ /// Note that this function consumes the receiving service and returns a wrapped version of it.
+ fn and_then<I, S1>(self, service: I) -> AndThenService<Self, S1, Req>
+ where
+ Self: Sized,
+ I: IntoService<S1, Self::Response>,
+ S1: Service<Self::Response, Error = Self::Error>,
+ {
+ AndThenService::new(self, service.into_service())
+ }
+}
+
+impl<S, Req> ServiceExt<Req> for S where S: Service<Req> {}
+
+/// An extension trait for [`ServiceFactory`]s that provides a variety of convenient adapters.
+pub trait ServiceFactoryExt<Req>: ServiceFactory<Req> {
+ /// Map this service's output to a different type, returning a new service
+ /// of the resulting type.
+ fn map<F, R>(self, f: F) -> crate::map::MapServiceFactory<Self, F, Req, R>
+ where
+ Self: Sized,
+ F: FnMut(Self::Response) -> R + Clone,
+ {
+ crate::map::MapServiceFactory::new(self, f)
+ }
+
+ /// Map this service's error to a different error, returning a new service.
+ fn map_err<F, E>(self, f: F) -> crate::map_err::MapErrServiceFactory<Self, Req, F, E>
+ where
+ Self: Sized,
+ F: Fn(Self::Error) -> E + Clone,
+ {
+ crate::map_err::MapErrServiceFactory::new(self, f)
+ }
+
+ /// Map this factory's init error to a different error, returning a new service.
+ fn map_init_err<F, E>(self, f: F) -> crate::map_init_err::MapInitErr<Self, F, Req, E>
+ where
+ Self: Sized,
+ F: Fn(Self::InitError) -> E + Clone,
+ {
+ crate::map_init_err::MapInitErr::new(self, f)
+ }
+
+ /// Call another service after call to this one has resolved successfully.
+ fn and_then<I, SF1>(self, factory: I) -> AndThenServiceFactory<Self, SF1, Req>
+ where
+ Self: Sized,
+ Self::Config: Clone,
+ I: IntoServiceFactory<SF1, Self::Response>,
+ SF1: ServiceFactory<
+ Self::Response,
+ Config = Self::Config,
+ Error = Self::Error,
+ InitError = Self::InitError,
+ >,
+ {
+ AndThenServiceFactory::new(self, factory.into_factory())
+ }
+}
+
+impl<SF, Req> ServiceFactoryExt<Req> for SF where SF: ServiceFactory<Req> {}
+
+/// An extension trait for [`Transform`]s that provides a variety of convenient adapters.
+pub trait TransformExt<S, Req>: Transform<S, Req> {
+ /// Return a new `Transform` whose init error is mapped to to a different type.
+ fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, Req, F, E>
+ where
+ Self: Sized,
+ F: Fn(Self::InitError) -> E + Clone,
+ {
+ TransformMapInitErr::new(self, f)
+ }
+}
+
+impl<T, Req> TransformExt<T, Req> for T where T: Transform<T, Req> {}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +
use core::{future::Future, marker::PhantomData};
+
+use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory};
+
+/// Create `ServiceFactory` for function that can act as a `Service`
+pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(f: F) -> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
+where
+ F: Fn(Req) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ FnServiceFactory::new(f)
+}
+
+/// Create `ServiceFactory` for function that can produce services
+///
+/// # Examples
+/// ```
+/// use std::io;
+/// use actix_service::{fn_factory, fn_service, Service, ServiceFactory};
+/// use futures_util::future::ok;
+///
+/// /// Service that divides two usize values.
+/// async fn div((x, y): (usize, usize)) -> Result<usize, io::Error> {
+/// if y == 0 {
+/// Err(io::Error::new(io::ErrorKind::Other, "divide by zero"))
+/// } else {
+/// Ok(x / y)
+/// }
+/// }
+///
+/// #[actix_rt::main]
+/// async fn main() -> io::Result<()> {
+/// // Create service factory that produces `div` services
+/// let factory = fn_factory(|| {
+/// ok::<_, io::Error>(fn_service(div))
+/// });
+///
+/// // construct new service
+/// let srv = factory.new_service(()).await?;
+///
+/// // now we can use `div` service
+/// let result = srv.call((10, 20)).await?;
+///
+/// println!("10 / 20 = {}", result);
+///
+/// Ok(())
+/// }
+/// ```
+pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(f: F) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ FnServiceNoConfig::new(f)
+}
+
+/// Create `ServiceFactory` for function that accepts config argument and can produce services
+///
+/// Any function that has following form `Fn(Config) -> Future<Output = Service>` could act as
+/// a `ServiceFactory`.
+///
+/// # Examples
+/// ```
+/// use std::io;
+/// use actix_service::{fn_factory_with_config, fn_service, Service, ServiceFactory};
+/// use futures_util::future::ok;
+///
+/// #[actix_rt::main]
+/// async fn main() -> io::Result<()> {
+/// // Create service factory. factory uses config argument for
+/// // services it generates.
+/// let factory = fn_factory_with_config(|y: usize| {
+/// ok::<_, io::Error>(fn_service(move |x: usize| ok::<_, io::Error>(x * y)))
+/// });
+///
+/// // construct new service with config argument
+/// let srv = factory.new_service(10).await?;
+///
+/// let result = srv.call(10).await?;
+/// assert_eq!(result, 100);
+///
+/// println!("10 * 10 = {}", result);
+/// Ok(())
+/// }
+/// ```
+pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Req, Err>(
+ f: F,
+) -> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
+where
+ F: Fn(Cfg) -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ FnServiceConfig::new(f)
+}
+
+pub struct FnService<F, Fut, Req, Res, Err>
+where
+ F: FnMut(Req) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ f: F,
+ _t: PhantomData<fn(Req)>,
+}
+
+impl<F, Fut, Req, Res, Err> FnService<F, Fut, Req, Res, Err>
+where
+ F: FnMut(Req) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ pub(crate) fn new(f: F) -> Self {
+ Self { f, _t: PhantomData }
+ }
+}
+
+impl<F, Fut, Req, Res, Err> Clone for FnService<F, Fut, Req, Res, Err>
+where
+ F: FnMut(Req) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn clone(&self) -> Self {
+ Self::new(self.f.clone())
+ }
+}
+
+impl<F, Fut, Req, Res, Err> Service<Req> for FnService<F, Fut, Req, Res, Err>
+where
+ F: Fn(Req) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ type Response = Res;
+ type Error = Err;
+ type Future = Fut;
+
+ crate::always_ready!();
+
+ fn call(&self, req: Req) -> Self::Future {
+ (self.f)(req)
+ }
+}
+
+impl<F, Fut, Req, Res, Err> IntoService<FnService<F, Fut, Req, Res, Err>, Req> for F
+where
+ F: Fn(Req) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn into_service(self) -> FnService<F, Fut, Req, Res, Err> {
+ FnService::new(self)
+ }
+}
+
+pub struct FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
+where
+ F: Fn(Req) -> Fut,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ f: F,
+ _t: PhantomData<fn(Req, Cfg)>,
+}
+
+impl<F, Fut, Req, Res, Err, Cfg> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
+where
+ F: Fn(Req) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn new(f: F) -> Self {
+ FnServiceFactory { f, _t: PhantomData }
+ }
+}
+
+impl<F, Fut, Req, Res, Err, Cfg> Clone for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
+where
+ F: Fn(Req) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn clone(&self) -> Self {
+ Self::new(self.f.clone())
+ }
+}
+
+impl<F, Fut, Req, Res, Err> Service<Req> for FnServiceFactory<F, Fut, Req, Res, Err, ()>
+where
+ F: Fn(Req) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ type Response = Res;
+ type Error = Err;
+ type Future = Fut;
+
+ crate::always_ready!();
+
+ fn call(&self, req: Req) -> Self::Future {
+ (self.f)(req)
+ }
+}
+
+impl<F, Fut, Req, Res, Err, Cfg> ServiceFactory<Req>
+ for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
+where
+ F: Fn(Req) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ type Response = Res;
+ type Error = Err;
+
+ type Config = Cfg;
+ type Service = FnService<F, Fut, Req, Res, Err>;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: Cfg) -> Self::Future {
+ ok(FnService::new(self.f.clone()))
+ }
+}
+
+impl<F, Fut, Req, Res, Err, Cfg>
+ IntoServiceFactory<FnServiceFactory<F, Fut, Req, Res, Err, Cfg>, Req> for F
+where
+ F: Fn(Req) -> Fut + Clone,
+ Fut: Future<Output = Result<Res, Err>>,
+{
+ fn into_factory(self) -> FnServiceFactory<F, Fut, Req, Res, Err, Cfg> {
+ FnServiceFactory::new(self)
+ }
+}
+
+/// Convert `Fn(&Config) -> Future<Service>` fn to NewService
+pub struct FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
+where
+ F: Fn(Cfg) -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ f: F,
+ _t: PhantomData<fn(Cfg, Req) -> (Fut, Srv, Err)>,
+}
+
+impl<F, Fut, Cfg, Srv, Req, Err> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
+where
+ F: Fn(Cfg) -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ fn new(f: F) -> Self {
+ FnServiceConfig { f, _t: PhantomData }
+ }
+}
+
+impl<F, Fut, Cfg, Srv, Req, Err> Clone for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
+where
+ F: Fn(Cfg) -> Fut + Clone,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ fn clone(&self) -> Self {
+ FnServiceConfig {
+ f: self.f.clone(),
+ _t: PhantomData,
+ }
+ }
+}
+
+impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req> for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
+where
+ F: Fn(Cfg) -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ type Response = Srv::Response;
+ type Error = Srv::Error;
+
+ type Config = Cfg;
+ type Service = Srv;
+ type InitError = Err;
+ type Future = Fut;
+
+ fn new_service(&self, cfg: Cfg) -> Self::Future {
+ (self.f)(cfg)
+ }
+}
+
+/// Converter for `Fn() -> Future<Service>` fn
+pub struct FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
+where
+ F: Fn() -> Fut,
+ Srv: Service<Req>,
+ Fut: Future<Output = Result<Srv, Err>>,
+{
+ f: F,
+ _t: PhantomData<fn(Cfg, Req)>,
+}
+
+impl<F, Cfg, Srv, Req, Fut, Err> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ fn new(f: F) -> Self {
+ Self { f, _t: PhantomData }
+ }
+}
+
+impl<F, Cfg, Srv, Req, Fut, Err> ServiceFactory<Req>
+ for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ type Response = Srv::Response;
+ type Error = Srv::Error;
+ type Config = Cfg;
+ type Service = Srv;
+ type InitError = Err;
+ type Future = Fut;
+
+ fn new_service(&self, _: Cfg) -> Self::Future {
+ (self.f)()
+ }
+}
+
+impl<F, Cfg, Srv, Req, Fut, Err> Clone for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
+where
+ F: Fn() -> Fut + Clone,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ fn clone(&self) -> Self {
+ Self::new(self.f.clone())
+ }
+}
+
+impl<F, Cfg, Srv, Req, Fut, Err>
+ IntoServiceFactory<FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>, Req> for F
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = Result<Srv, Err>>,
+ Srv: Service<Req>,
+{
+ fn into_factory(self) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err> {
+ FnServiceNoConfig::new(self)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::task::Poll;
+
+ use futures_util::future::lazy;
+
+ use super::*;
+
+ #[actix_rt::test]
+ async fn test_fn_service() {
+ let new_srv = fn_service(|()| ok::<_, ()>("srv"));
+
+ let srv = new_srv.new_service(()).await.unwrap();
+ let res = srv.call(()).await;
+ assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), "srv");
+ }
+
+ #[actix_rt::test]
+ async fn test_fn_service_service() {
+ let srv = fn_service(|()| ok::<_, ()>("srv"));
+
+ let res = srv.call(()).await;
+ assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), "srv");
+ }
+
+ #[actix_rt::test]
+ async fn test_fn_service_with_config() {
+ let new_srv = fn_factory_with_config(|cfg: usize| {
+ ok::<_, ()>(fn_service(move |()| ok::<_, ()>(("srv", cfg))))
+ });
+
+ let srv = new_srv.new_service(1).await.unwrap();
+ let res = srv.call(()).await;
+ assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv", 1));
+ }
+
+ #[actix_rt::test]
+ async fn test_auto_impl_send() {
+ use alloc::rc::Rc;
+
+ use crate::{map_config, ServiceExt, ServiceFactoryExt};
+
+ let srv_1 = fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8)));
+
+ let fac_1 = fn_factory_with_config(|_: Rc<u8>| {
+ ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8))))
+ });
+
+ let fac_2 =
+ fn_factory(|| ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8)))));
+
+ fn is_send<T: Send + Sync + Clone>(_: &T) {}
+
+ is_send(&fac_1);
+ is_send(&map_config(fac_1.clone(), |_: Rc<u8>| Rc::new(0u8)));
+ is_send(&fac_1.clone().map_err(|_| Rc::new(0u8)));
+ is_send(&fac_1.clone().map(|_| Rc::new(0u8)));
+ is_send(&fac_1.clone().map_init_err(|_| Rc::new(0u8)));
+ // `and_then` is always !Send
+ // is_send(&fac_1.clone().and_then(fac_1.clone()));
+ is_send(&fac_1.new_service(Rc::new(0u8)).await.unwrap());
+
+ is_send(&fac_2);
+ is_send(&fac_2.new_service(Rc::new(0u8)).await.unwrap());
+
+ is_send(&srv_1);
+ is_send(&ServiceExt::map(srv_1.clone(), |_| Rc::new(0u8)));
+ is_send(&ServiceExt::map_err(srv_1.clone(), |_| Rc::new(0u8)));
+ // `and_then` is always !Send
+ // is_send(&ServiceExt::and_then(srv_1.clone(), srv_1.clone()));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +
//! See [`Service`] docs for information on this crate's foundational trait.
+
+#![no_std]
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible, missing_docs)]
+#![allow(clippy::type_complexity)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+
+extern crate alloc;
+
+use alloc::{boxed::Box, rc::Rc, sync::Arc};
+use core::{
+ cell::RefCell,
+ future::Future,
+ task::{self, Context, Poll},
+};
+
+mod and_then;
+mod apply;
+mod apply_cfg;
+pub mod boxed;
+mod ext;
+mod fn_service;
+mod macros;
+mod map;
+mod map_config;
+mod map_err;
+mod map_init_err;
+mod pipeline;
+mod ready;
+mod then;
+mod transform;
+mod transform_err;
+
+#[allow(unused_imports)]
+use self::ready::{err, ok, ready, Ready};
+pub use self::{
+ apply::{apply_fn, apply_fn_factory},
+ apply_cfg::{apply_cfg, apply_cfg_factory},
+ ext::{ServiceExt, ServiceFactoryExt, TransformExt},
+ fn_service::{fn_factory, fn_factory_with_config, fn_service},
+ map_config::{map_config, unit_config},
+ transform::{apply, ApplyTransform, Transform},
+};
+
+/// An asynchronous operation from `Request` to a `Response`.
+///
+/// The `Service` trait models a request/response interaction, receiving requests and returning
+/// replies. You can think about a service as a function with one argument that returns some result
+/// asynchronously. Conceptually, the operation looks like this:
+///
+/// ```ignore
+/// async fn(Request) -> Result<Response, Err>
+/// ```
+///
+/// The `Service` trait just generalizes this form. Requests are defined as a generic type parameter
+/// and responses and other details are defined as associated types on the trait impl. Notice that
+/// this design means that services can receive many request types and converge them to a single
+/// response type.
+///
+/// Services can also have mutable state that influence computation by using a `Cell`, `RefCell`
+/// or `Mutex`. Services intentionally do not take `&mut self` to reduce overhead in the
+/// common cases.
+///
+/// `Service` provides a symmetric and uniform API; the same abstractions can be used to represent
+/// both clients and servers. Services describe only _transformation_ operations which encourage
+/// simple API surfaces. This leads to simpler design of each service, improves test-ability and
+/// makes composition easier.
+///
+/// ```ignore
+/// struct MyService;
+///
+/// impl Service<u8> for MyService {
+/// type Response = u64;
+/// type Error = MyError;
+/// type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
+///
+/// fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { ... }
+///
+/// fn call(&self, req: u8) -> Self::Future { ... }
+/// }
+/// ```
+///
+/// Sometimes it is not necessary to implement the Service trait. For example, the above service
+/// could be rewritten as a simple function and passed to [`fn_service`](fn_service()).
+///
+/// ```ignore
+/// async fn my_service(req: u8) -> Result<u64, MyError>;
+///
+/// let svc = fn_service(my_service)
+/// svc.call(123)
+/// ```
+pub trait Service<Req> {
+ /// Responses given by the service.
+ type Response;
+
+ /// Errors produced by the service when polling readiness or executing call.
+ type Error;
+
+ /// The future response value.
+ type Future: Future<Output = Result<Self::Response, Self::Error>>;
+
+ /// Returns `Ready` when the service is able to process requests.
+ ///
+ /// If the service is at capacity, then `Pending` is returned and the task is notified when the
+ /// service becomes ready again. This function is expected to be called while on a task.
+ ///
+ /// This is a best effort implementation. False positives are permitted. It is permitted for
+ /// the service to return `Ready` from a `poll_ready` call and the next invocation of `call`
+ /// results in an error.
+ ///
+ /// # Notes
+ /// 1. `poll_ready` might be called on a different task to `call`.
+ /// 1. In cases of chained services, `.poll_ready()` is called for all services at once.
+ fn poll_ready(&self, ctx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
+
+ /// Process the request and return the response asynchronously.
+ ///
+ /// This function is expected to be callable off-task. As such, implementations of `call` should
+ /// take care to not call `poll_ready`. If the service is at capacity and the request is unable
+ /// to be handled, the returned `Future` should resolve to an error.
+ ///
+ /// Invoking `call` without first invoking `poll_ready` is permitted. Implementations must be
+ /// resilient to this fact.
+ fn call(&self, req: Req) -> Self::Future;
+}
+
+/// Factory for creating `Service`s.
+///
+/// This is useful for cases where new `Service`s must be produced. One case is a TCP
+/// server listener: a listener accepts new connections, constructs a new `Service` for each using
+/// the `ServiceFactory` trait, and uses the new `Service` to process inbound requests on that new
+/// connection.
+///
+/// `Config` is a service factory configuration type.
+///
+/// Simple factories may be able to use [`fn_factory`] or [`fn_factory_with_config`] to
+/// reduce boilerplate.
+pub trait ServiceFactory<Req> {
+ /// Responses given by the created services.
+ type Response;
+
+ /// Errors produced by the created services.
+ type Error;
+
+ /// Service factory configuration.
+ type Config;
+
+ /// The kind of `Service` created by this factory.
+ type Service: Service<Req, Response = Self::Response, Error = Self::Error>;
+
+ /// Errors potentially raised while building a service.
+ type InitError;
+
+ /// The future of the `Service` instance.g
+ type Future: Future<Output = Result<Self::Service, Self::InitError>>;
+
+ /// Create and return a new service asynchronously.
+ fn new_service(&self, cfg: Self::Config) -> Self::Future;
+}
+
+// TODO: remove implement on mut reference.
+impl<'a, S, Req> Service<Req> for &'a mut S
+where
+ S: Service<Req> + 'a,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ (**self).poll_ready(ctx)
+ }
+
+ fn call(&self, request: Req) -> S::Future {
+ (**self).call(request)
+ }
+}
+
+impl<'a, S, Req> Service<Req> for &'a S
+where
+ S: Service<Req> + 'a,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ (**self).poll_ready(ctx)
+ }
+
+ fn call(&self, request: Req) -> S::Future {
+ (**self).call(request)
+ }
+}
+
+impl<S, Req> Service<Req> for Box<S>
+where
+ S: Service<Req> + ?Sized,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
+ (**self).poll_ready(ctx)
+ }
+
+ fn call(&self, request: Req) -> S::Future {
+ (**self).call(request)
+ }
+}
+
+impl<S, Req> Service<Req> for Rc<S>
+where
+ S: Service<Req> + ?Sized,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ (**self).poll_ready(ctx)
+ }
+
+ fn call(&self, request: Req) -> S::Future {
+ (**self).call(request)
+ }
+}
+
+/// This impl is deprecated since v2 because the `Service` trait now receives shared reference.
+impl<S, Req> Service<Req> for RefCell<S>
+where
+ S: Service<Req>,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.borrow().poll_ready(ctx)
+ }
+
+ fn call(&self, request: Req) -> S::Future {
+ self.borrow().call(request)
+ }
+}
+
+impl<S, Req> ServiceFactory<Req> for Rc<S>
+where
+ S: ServiceFactory<Req>,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Config = S::Config;
+ type Service = S::Service;
+ type InitError = S::InitError;
+ type Future = S::Future;
+
+ fn new_service(&self, cfg: S::Config) -> S::Future {
+ self.as_ref().new_service(cfg)
+ }
+}
+
+impl<S, Req> ServiceFactory<Req> for Arc<S>
+where
+ S: ServiceFactory<Req>,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Config = S::Config;
+ type Service = S::Service;
+ type InitError = S::InitError;
+ type Future = S::Future;
+
+ fn new_service(&self, cfg: S::Config) -> S::Future {
+ self.as_ref().new_service(cfg)
+ }
+}
+
+/// Trait for types that can be converted to a `Service`
+pub trait IntoService<S, Req>
+where
+ S: Service<Req>,
+{
+ /// Convert to a `Service`
+ fn into_service(self) -> S;
+}
+
+/// Trait for types that can be converted to a `ServiceFactory`
+pub trait IntoServiceFactory<SF, Req>
+where
+ SF: ServiceFactory<Req>,
+{
+ /// Convert `Self` to a `ServiceFactory`
+ fn into_factory(self) -> SF;
+}
+
+impl<S, Req> IntoService<S, Req> for S
+where
+ S: Service<Req>,
+{
+ fn into_service(self) -> S {
+ self
+ }
+}
+
+impl<SF, Req> IntoServiceFactory<SF, Req> for SF
+where
+ SF: ServiceFactory<Req>,
+{
+ fn into_factory(self) -> SF {
+ self
+ }
+}
+
+/// Convert object of type `U` to a service `S`
+pub fn into_service<I, S, Req>(tp: I) -> S
+where
+ I: IntoService<S, Req>,
+ S: Service<Req>,
+{
+ tp.into_service()
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +
/// An implementation of [`poll_ready`]() that always signals readiness.
+///
+/// This should only be used for basic leaf services that have no concept of un-readiness.
+/// For wrapper or other service types, use [`forward_ready!`] for simple cases or write a bespoke
+/// `poll_ready` implementation.
+///
+/// [`poll_ready`]: crate::Service::poll_ready
+///
+/// # Examples
+/// ```no_run
+/// use actix_service::Service;
+/// use futures_util::future::{ready, Ready};
+///
+/// struct IdentityService;
+///
+/// impl Service<u32> for IdentityService {
+/// type Response = u32;
+/// type Error = ();
+/// type Future = Ready<Result<Self::Response, Self::Error>>;
+///
+/// actix_service::always_ready!();
+///
+/// fn call(&self, req: u32) -> Self::Future {
+/// ready(Ok(req))
+/// }
+/// }
+/// ```
+///
+/// [`forward_ready!`]: crate::forward_ready
+#[macro_export]
+macro_rules! always_ready {
+ () => {
+ #[inline]
+ fn poll_ready(
+ &self,
+ _: &mut ::core::task::Context<'_>,
+ ) -> ::core::task::Poll<Result<(), Self::Error>> {
+ ::core::task::Poll::Ready(Ok(()))
+ }
+ };
+}
+
+/// An implementation of [`poll_ready`] that forwards readiness checks to a
+/// named struct field.
+///
+/// Tuple structs are not supported.
+///
+/// [`poll_ready`]: crate::Service::poll_ready
+///
+/// # Examples
+/// ```no_run
+/// use actix_service::Service;
+/// use futures_util::future::{ready, Ready};
+///
+/// struct WrapperService<S> {
+/// inner: S,
+/// }
+///
+/// impl<S> Service<()> for WrapperService<S>
+/// where
+/// S: Service<()>,
+/// {
+/// type Response = S::Response;
+/// type Error = S::Error;
+/// type Future = S::Future;
+///
+/// actix_service::forward_ready!(inner);
+///
+/// fn call(&self, req: ()) -> Self::Future {
+/// self.inner.call(req)
+/// }
+/// }
+/// ```
+#[macro_export]
+macro_rules! forward_ready {
+ ($field:ident) => {
+ #[inline]
+ fn poll_ready(
+ &self,
+ cx: &mut ::core::task::Context<'_>,
+ ) -> ::core::task::Poll<Result<(), Self::Error>> {
+ self.$field
+ .poll_ready(cx)
+ .map_err(::core::convert::Into::into)
+ }
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use core::{
+ cell::Cell,
+ convert::Infallible,
+ task::{self, Context, Poll},
+ };
+
+ use futures_util::{
+ future::{ready, Ready},
+ task::noop_waker,
+ };
+
+ use crate::Service;
+
+ struct IdentityService;
+
+ impl Service<u32> for IdentityService {
+ type Response = u32;
+ type Error = Infallible;
+ type Future = Ready<Result<Self::Response, Self::Error>>;
+
+ always_ready!();
+
+ fn call(&self, req: u32) -> Self::Future {
+ ready(Ok(req))
+ }
+ }
+
+ struct CountdownService(Cell<u32>);
+
+ impl Service<()> for CountdownService {
+ type Response = ();
+ type Error = Infallible;
+ type Future = Ready<Result<Self::Response, Self::Error>>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ let count = self.0.get();
+
+ if count == 0 {
+ Poll::Ready(Ok(()))
+ } else {
+ self.0.set(count - 1);
+ cx.waker().wake_by_ref();
+ Poll::Pending
+ }
+ }
+
+ fn call(&self, _: ()) -> Self::Future {
+ ready(Ok(()))
+ }
+ }
+
+ struct WrapperService<S> {
+ inner: S,
+ }
+
+ impl<S> Service<()> for WrapperService<S>
+ where
+ S: Service<()>,
+ {
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ forward_ready!(inner);
+
+ fn call(&self, _: ()) -> Self::Future {
+ self.inner.call(())
+ }
+ }
+
+ #[test]
+ fn test_always_ready_macro() {
+ let waker = noop_waker();
+ let mut cx = task::Context::from_waker(&waker);
+
+ let svc = IdentityService;
+
+ assert!(svc.poll_ready(&mut cx).is_ready());
+ assert!(svc.poll_ready(&mut cx).is_ready());
+ assert!(svc.poll_ready(&mut cx).is_ready());
+ }
+
+ #[test]
+ fn test_forward_ready_macro() {
+ let waker = noop_waker();
+ let mut cx = task::Context::from_waker(&waker);
+
+ let svc = WrapperService {
+ inner: CountdownService(Cell::new(3)),
+ };
+
+ assert!(svc.poll_ready(&mut cx).is_pending());
+ assert!(svc.poll_ready(&mut cx).is_pending());
+ assert!(svc.poll_ready(&mut cx).is_pending());
+ assert!(svc.poll_ready(&mut cx).is_ready());
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +
use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use pin_project_lite::pin_project;
+
+use super::{Service, ServiceFactory};
+
+/// Service for the `map` combinator, changing the type of a service's response.
+///
+/// This is created by the `ServiceExt::map` method.
+pub struct Map<A, F, Req, Res> {
+ service: A,
+ f: F,
+ _t: PhantomData<fn(Req) -> Res>,
+}
+
+impl<A, F, Req, Res> Map<A, F, Req, Res> {
+ /// Create new `Map` combinator
+ pub(crate) fn new(service: A, f: F) -> Self
+ where
+ A: Service<Req>,
+ F: FnMut(A::Response) -> Res,
+ {
+ Self {
+ service,
+ f,
+ _t: PhantomData,
+ }
+ }
+}
+
+impl<A, F, Req, Res> Clone for Map<A, F, Req, Res>
+where
+ A: Clone,
+ F: Clone,
+{
+ fn clone(&self) -> Self {
+ Map {
+ service: self.service.clone(),
+ f: self.f.clone(),
+ _t: PhantomData,
+ }
+ }
+}
+
+impl<A, F, Req, Res> Service<Req> for Map<A, F, Req, Res>
+where
+ A: Service<Req>,
+ F: FnMut(A::Response) -> Res + Clone,
+{
+ type Response = Res;
+ type Error = A::Error;
+ type Future = MapFuture<A, F, Req, Res>;
+
+ crate::forward_ready!(service);
+
+ fn call(&self, req: Req) -> Self::Future {
+ MapFuture::new(self.service.call(req), self.f.clone())
+ }
+}
+
+pin_project! {
+ pub struct MapFuture<A, F, Req, Res>
+ where
+ A: Service<Req>,
+ F: FnMut(A::Response) -> Res,
+ {
+ f: F,
+ #[pin]
+ fut: A::Future,
+ }
+}
+
+impl<A, F, Req, Res> MapFuture<A, F, Req, Res>
+where
+ A: Service<Req>,
+ F: FnMut(A::Response) -> Res,
+{
+ fn new(fut: A::Future, f: F) -> Self {
+ MapFuture { f, fut }
+ }
+}
+
+impl<A, F, Req, Res> Future for MapFuture<A, F, Req, Res>
+where
+ A: Service<Req>,
+ F: FnMut(A::Response) -> Res,
+{
+ type Output = Result<Res, A::Error>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+
+ match this.fut.poll(cx) {
+ Poll::Ready(Ok(resp)) => Poll::Ready(Ok((this.f)(resp))),
+ Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
+ Poll::Pending => Poll::Pending,
+ }
+ }
+}
+
+/// `MapNewService` new service combinator
+pub struct MapServiceFactory<A, F, Req, Res> {
+ a: A,
+ f: F,
+ r: PhantomData<fn(Req) -> Res>,
+}
+
+impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
+ /// Create new `Map` new service instance
+ pub(crate) fn new(a: A, f: F) -> Self
+ where
+ A: ServiceFactory<Req>,
+ F: FnMut(A::Response) -> Res,
+ {
+ Self {
+ a,
+ f,
+ r: PhantomData,
+ }
+ }
+}
+
+impl<A, F, Req, Res> Clone for MapServiceFactory<A, F, Req, Res>
+where
+ A: Clone,
+ F: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ a: self.a.clone(),
+ f: self.f.clone(),
+ r: PhantomData,
+ }
+ }
+}
+
+impl<A, F, Req, Res> ServiceFactory<Req> for MapServiceFactory<A, F, Req, Res>
+where
+ A: ServiceFactory<Req>,
+ F: FnMut(A::Response) -> Res + Clone,
+{
+ type Response = Res;
+ type Error = A::Error;
+
+ type Config = A::Config;
+ type Service = Map<A::Service, F, Req, Res>;
+ type InitError = A::InitError;
+ type Future = MapServiceFuture<A, F, Req, Res>;
+
+ fn new_service(&self, cfg: A::Config) -> Self::Future {
+ MapServiceFuture::new(self.a.new_service(cfg), self.f.clone())
+ }
+}
+
+pin_project! {
+ pub struct MapServiceFuture<A, F, Req, Res>
+ where
+ A: ServiceFactory<Req>,
+ F: FnMut(A::Response) -> Res,
+ {
+ #[pin]
+ fut: A::Future,
+ f: Option<F>,
+ }
+}
+
+impl<A, F, Req, Res> MapServiceFuture<A, F, Req, Res>
+where
+ A: ServiceFactory<Req>,
+ F: FnMut(A::Response) -> Res,
+{
+ fn new(fut: A::Future, f: F) -> Self {
+ MapServiceFuture { f: Some(f), fut }
+ }
+}
+
+impl<A, F, Req, Res> Future for MapServiceFuture<A, F, Req, Res>
+where
+ A: ServiceFactory<Req>,
+ F: FnMut(A::Response) -> Res,
+{
+ type Output = Result<Map<A::Service, F, Req, Res>, A::InitError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+
+ if let Poll::Ready(svc) = this.fut.poll(cx)? {
+ Poll::Ready(Ok(Map::new(svc, this.f.take().unwrap())))
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use futures_util::future::lazy;
+
+ use super::*;
+ use crate::{ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
+
+ struct Srv;
+
+ impl Service<()> for Srv {
+ type Response = ();
+ type Error = ();
+ type Future = Ready<Result<(), ()>>;
+
+ crate::always_ready!();
+
+ fn call(&self, _: ()) -> Self::Future {
+ ok(())
+ }
+ }
+
+ #[actix_rt::test]
+ async fn test_poll_ready() {
+ let srv = Srv.map(|_| "ok");
+ let res = lazy(|cx| srv.poll_ready(cx)).await;
+ assert_eq!(res, Poll::Ready(Ok(())));
+ }
+
+ #[actix_rt::test]
+ async fn test_call() {
+ let srv = Srv.map(|_| "ok");
+ let res = srv.call(()).await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), "ok");
+ }
+
+ #[actix_rt::test]
+ async fn test_new_service() {
+ let new_srv = (|| ok::<_, ()>(Srv)).into_factory().map(|_| "ok");
+ let srv = new_srv.new_service(&()).await.unwrap();
+ let res = srv.call(()).await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("ok"));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +
use core::marker::PhantomData;
+
+use super::{IntoServiceFactory, ServiceFactory};
+
+/// Adapt external config argument to a config for provided service factory
+///
+/// Note that this function consumes the receiving service factory and returns
+/// a wrapped version of it.
+pub fn map_config<I, SF, Req, F, Cfg>(factory: I, f: F) -> MapConfig<SF, Req, F, Cfg>
+where
+ I: IntoServiceFactory<SF, Req>,
+ SF: ServiceFactory<Req>,
+ F: Fn(Cfg) -> SF::Config,
+{
+ MapConfig::new(factory.into_factory(), f)
+}
+
+/// Replace config with unit.
+pub fn unit_config<I, SF, Cfg, Req>(factory: I) -> UnitConfig<SF, Cfg, Req>
+where
+ I: IntoServiceFactory<SF, Req>,
+ SF: ServiceFactory<Req, Config = ()>,
+{
+ UnitConfig::new(factory.into_factory())
+}
+
+/// `map_config()` adapter service factory
+pub struct MapConfig<SF, Req, F, Cfg> {
+ factory: SF,
+ cfg_mapper: F,
+ e: PhantomData<fn(Cfg, Req)>,
+}
+
+impl<SF, Req, F, Cfg> MapConfig<SF, Req, F, Cfg> {
+ /// Create new `MapConfig` combinator
+ pub(crate) fn new(factory: SF, cfg_mapper: F) -> Self
+ where
+ SF: ServiceFactory<Req>,
+ F: Fn(Cfg) -> SF::Config,
+ {
+ Self {
+ factory,
+ cfg_mapper,
+ e: PhantomData,
+ }
+ }
+}
+
+impl<SF, Req, F, Cfg> Clone for MapConfig<SF, Req, F, Cfg>
+where
+ SF: Clone,
+ F: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ factory: self.factory.clone(),
+ cfg_mapper: self.cfg_mapper.clone(),
+ e: PhantomData,
+ }
+ }
+}
+
+impl<SF, Req, F, Cfg> ServiceFactory<Req> for MapConfig<SF, Req, F, Cfg>
+where
+ SF: ServiceFactory<Req>,
+ F: Fn(Cfg) -> SF::Config,
+{
+ type Response = SF::Response;
+ type Error = SF::Error;
+
+ type Config = Cfg;
+ type Service = SF::Service;
+ type InitError = SF::InitError;
+ type Future = SF::Future;
+
+ fn new_service(&self, cfg: Self::Config) -> Self::Future {
+ let mapped_cfg = (self.cfg_mapper)(cfg);
+ self.factory.new_service(mapped_cfg)
+ }
+}
+
+/// `unit_config()` config combinator
+pub struct UnitConfig<SF, Cfg, Req> {
+ factory: SF,
+ _phantom: PhantomData<fn(Cfg, Req)>,
+}
+
+impl<SF, Cfg, Req> UnitConfig<SF, Cfg, Req>
+where
+ SF: ServiceFactory<Req, Config = ()>,
+{
+ /// Create new `UnitConfig` combinator
+ pub(crate) fn new(factory: SF) -> Self {
+ Self {
+ factory,
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<SF, Cfg, Req> Clone for UnitConfig<SF, Cfg, Req>
+where
+ SF: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ factory: self.factory.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<SF, Cfg, Req> ServiceFactory<Req> for UnitConfig<SF, Cfg, Req>
+where
+ SF: ServiceFactory<Req, Config = ()>,
+{
+ type Response = SF::Response;
+ type Error = SF::Error;
+
+ type Config = Cfg;
+ type Service = SF::Service;
+ type InitError = SF::InitError;
+ type Future = SF::Future;
+
+ fn new_service(&self, _: Cfg) -> Self::Future {
+ self.factory.new_service(())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +
use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use pin_project_lite::pin_project;
+
+use super::{Service, ServiceFactory};
+
+/// Service for the `map_err` combinator, changing the type of a service's error.
+///
+/// This is created by the `ServiceExt::map_err` method.
+pub struct MapErr<S, Req, F, E> {
+ service: S,
+ mapper: F,
+ _t: PhantomData<fn(Req) -> E>,
+}
+
+impl<S, Req, F, E> MapErr<S, Req, F, E> {
+ /// Create new `MapErr` combinator
+ pub(crate) fn new(service: S, mapper: F) -> Self
+ where
+ S: Service<Req>,
+ F: Fn(S::Error) -> E,
+ {
+ Self {
+ service,
+ mapper,
+ _t: PhantomData,
+ }
+ }
+}
+
+impl<S, Req, F, E> Clone for MapErr<S, Req, F, E>
+where
+ S: Clone,
+ F: Clone,
+{
+ fn clone(&self) -> Self {
+ MapErr {
+ service: self.service.clone(),
+ mapper: self.mapper.clone(),
+ _t: PhantomData,
+ }
+ }
+}
+
+impl<A, Req, F, E> Service<Req> for MapErr<A, Req, F, E>
+where
+ A: Service<Req>,
+ F: Fn(A::Error) -> E + Clone,
+{
+ type Response = A::Response;
+ type Error = E;
+ type Future = MapErrFuture<A, Req, F, E>;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.service.poll_ready(ctx).map_err(&self.mapper)
+ }
+
+ fn call(&self, req: Req) -> Self::Future {
+ MapErrFuture::new(self.service.call(req), self.mapper.clone())
+ }
+}
+
+pin_project! {
+ pub struct MapErrFuture<A, Req, F, E>
+ where
+ A: Service<Req>,
+ F: Fn(A::Error) -> E,
+ {
+ f: F,
+ #[pin]
+ fut: A::Future,
+ }
+}
+
+impl<A, Req, F, E> MapErrFuture<A, Req, F, E>
+where
+ A: Service<Req>,
+ F: Fn(A::Error) -> E,
+{
+ fn new(fut: A::Future, f: F) -> Self {
+ MapErrFuture { f, fut }
+ }
+}
+
+impl<A, Req, F, E> Future for MapErrFuture<A, Req, F, E>
+where
+ A: Service<Req>,
+ F: Fn(A::Error) -> E,
+{
+ type Output = Result<A::Response, E>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+ this.fut.poll(cx).map_err(this.f)
+ }
+}
+
+/// Factory for the `map_err` combinator, changing the type of a new
+/// service's error.
+///
+/// This is created by the `NewServiceExt::map_err` method.
+pub struct MapErrServiceFactory<SF, Req, F, E>
+where
+ SF: ServiceFactory<Req>,
+ F: Fn(SF::Error) -> E + Clone,
+{
+ a: SF,
+ f: F,
+ e: PhantomData<fn(Req) -> E>,
+}
+
+impl<SF, Req, F, E> MapErrServiceFactory<SF, Req, F, E>
+where
+ SF: ServiceFactory<Req>,
+ F: Fn(SF::Error) -> E + Clone,
+{
+ /// Create new `MapErr` new service instance
+ pub(crate) fn new(a: SF, f: F) -> Self {
+ Self {
+ a,
+ f,
+ e: PhantomData,
+ }
+ }
+}
+
+impl<SF, Req, F, E> Clone for MapErrServiceFactory<SF, Req, F, E>
+where
+ SF: ServiceFactory<Req> + Clone,
+ F: Fn(SF::Error) -> E + Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ a: self.a.clone(),
+ f: self.f.clone(),
+ e: PhantomData,
+ }
+ }
+}
+
+impl<SF, Req, F, E> ServiceFactory<Req> for MapErrServiceFactory<SF, Req, F, E>
+where
+ SF: ServiceFactory<Req>,
+ F: Fn(SF::Error) -> E + Clone,
+{
+ type Response = SF::Response;
+ type Error = E;
+
+ type Config = SF::Config;
+ type Service = MapErr<SF::Service, Req, F, E>;
+ type InitError = SF::InitError;
+ type Future = MapErrServiceFuture<SF, Req, F, E>;
+
+ fn new_service(&self, cfg: SF::Config) -> Self::Future {
+ MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone())
+ }
+}
+
+pin_project! {
+ pub struct MapErrServiceFuture<SF, Req, F, E>
+ where
+ SF: ServiceFactory<Req>,
+ F: Fn(SF::Error) -> E,
+ {
+ #[pin]
+ fut: SF::Future,
+ mapper: F,
+ }
+}
+
+impl<SF, Req, F, E> MapErrServiceFuture<SF, Req, F, E>
+where
+ SF: ServiceFactory<Req>,
+ F: Fn(SF::Error) -> E,
+{
+ fn new(fut: SF::Future, mapper: F) -> Self {
+ MapErrServiceFuture { fut, mapper }
+ }
+}
+
+impl<SF, Req, F, E> Future for MapErrServiceFuture<SF, Req, F, E>
+where
+ SF: ServiceFactory<Req>,
+ F: Fn(SF::Error) -> E + Clone,
+{
+ type Output = Result<MapErr<SF::Service, Req, F, E>, SF::InitError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+ if let Poll::Ready(svc) = this.fut.poll(cx)? {
+ Poll::Ready(Ok(MapErr::new(svc, this.mapper.clone())))
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use futures_util::future::lazy;
+
+ use super::*;
+ use crate::{err, ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
+
+ struct Srv;
+
+ impl Service<()> for Srv {
+ type Response = ();
+ type Error = ();
+ type Future = Ready<Result<(), ()>>;
+
+ fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ Poll::Ready(Err(()))
+ }
+
+ fn call(&self, _: ()) -> Self::Future {
+ err(())
+ }
+ }
+
+ #[actix_rt::test]
+ async fn test_poll_ready() {
+ let srv = Srv.map_err(|_| "error");
+ let res = lazy(|cx| srv.poll_ready(cx)).await;
+ assert_eq!(res, Poll::Ready(Err("error")));
+ }
+
+ #[actix_rt::test]
+ async fn test_call() {
+ let srv = Srv.map_err(|_| "error");
+ let res = srv.call(()).await;
+ assert!(res.is_err());
+ assert_eq!(res.err().unwrap(), "error");
+ }
+
+ #[actix_rt::test]
+ async fn test_new_service() {
+ let new_srv = (|| ok::<_, ()>(Srv)).into_factory().map_err(|_| "error");
+ let srv = new_srv.new_service(&()).await.unwrap();
+ let res = srv.call(()).await;
+ assert!(res.is_err());
+ assert_eq!(res.err().unwrap(), "error");
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +
use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use pin_project_lite::pin_project;
+
+use super::ServiceFactory;
+
+/// `MapInitErr` service combinator
+pub struct MapInitErr<A, F, Req, Err> {
+ a: A,
+ f: F,
+ e: PhantomData<fn(Req) -> Err>,
+}
+
+impl<A, F, Req, Err> MapInitErr<A, F, Req, Err>
+where
+ A: ServiceFactory<Req>,
+ F: Fn(A::InitError) -> Err,
+{
+ /// Create new `MapInitErr` combinator
+ pub(crate) fn new(a: A, f: F) -> Self {
+ Self {
+ a,
+ f,
+ e: PhantomData,
+ }
+ }
+}
+
+impl<A, F, Req, E> Clone for MapInitErr<A, F, Req, E>
+where
+ A: Clone,
+ F: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ a: self.a.clone(),
+ f: self.f.clone(),
+ e: PhantomData,
+ }
+ }
+}
+
+impl<A, F, Req, E> ServiceFactory<Req> for MapInitErr<A, F, Req, E>
+where
+ A: ServiceFactory<Req>,
+ F: Fn(A::InitError) -> E + Clone,
+{
+ type Response = A::Response;
+ type Error = A::Error;
+
+ type Config = A::Config;
+ type Service = A::Service;
+ type InitError = E;
+ type Future = MapInitErrFuture<A, F, Req, E>;
+
+ fn new_service(&self, cfg: A::Config) -> Self::Future {
+ MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone())
+ }
+}
+
+pin_project! {
+ pub struct MapInitErrFuture<A, F, Req, E>
+ where
+ A: ServiceFactory<Req>,
+ F: Fn(A::InitError) -> E,
+ {
+ f: F,
+ #[pin]
+ fut: A::Future,
+ }
+}
+
+impl<A, F, Req, E> MapInitErrFuture<A, F, Req, E>
+where
+ A: ServiceFactory<Req>,
+ F: Fn(A::InitError) -> E,
+{
+ fn new(fut: A::Future, f: F) -> Self {
+ MapInitErrFuture { f, fut }
+ }
+}
+
+impl<A, F, Req, E> Future for MapInitErrFuture<A, F, Req, E>
+where
+ A: ServiceFactory<Req>,
+ F: Fn(A::InitError) -> E,
+{
+ type Output = Result<A::Service, E>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+ this.fut.poll(cx).map_err(this.f)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +
// TODO: see if pipeline is necessary
+#![allow(dead_code)]
+
+use core::{
+ marker::PhantomData,
+ task::{Context, Poll},
+};
+
+use crate::{
+ and_then::{AndThenService, AndThenServiceFactory},
+ map::{Map, MapServiceFactory},
+ map_err::{MapErr, MapErrServiceFactory},
+ map_init_err::MapInitErr,
+ then::{ThenService, ThenServiceFactory},
+ IntoService, IntoServiceFactory, Service, ServiceFactory,
+};
+
+/// Construct new pipeline with one service in pipeline chain.
+pub(crate) fn pipeline<I, S, Req>(service: I) -> Pipeline<S, Req>
+where
+ I: IntoService<S, Req>,
+ S: Service<Req>,
+{
+ Pipeline {
+ service: service.into_service(),
+ _phantom: PhantomData,
+ }
+}
+
+/// Construct new pipeline factory with one service factory.
+pub(crate) fn pipeline_factory<I, SF, Req>(factory: I) -> PipelineFactory<SF, Req>
+where
+ I: IntoServiceFactory<SF, Req>,
+ SF: ServiceFactory<Req>,
+{
+ PipelineFactory {
+ factory: factory.into_factory(),
+ _phantom: PhantomData,
+ }
+}
+
+/// Pipeline service - pipeline allows to compose multiple service into one service.
+pub(crate) struct Pipeline<S, Req> {
+ service: S,
+ _phantom: PhantomData<fn(Req)>,
+}
+
+impl<S, Req> Pipeline<S, Req>
+where
+ S: Service<Req>,
+{
+ /// Call another service after call to this one has resolved successfully.
+ ///
+ /// This function can be used to chain two services together and ensure that
+ /// the second service isn't called until call to the fist service have
+ /// finished. Result of the call to the first service is used as an
+ /// input parameter for the second service's call.
+ ///
+ /// Note that this function consumes the receiving service and returns a
+ /// wrapped version of it.
+ pub fn and_then<I, S1>(
+ self,
+ service: I,
+ ) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req>
+ where
+ Self: Sized,
+ I: IntoService<S1, S::Response>,
+ S1: Service<S::Response, Error = S::Error>,
+ {
+ Pipeline {
+ service: AndThenService::new(self.service, service.into_service()),
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Chain on a computation for when a call to the service finished,
+ /// passing the result of the call to the next service `U`.
+ ///
+ /// Note that this function consumes the receiving pipeline and returns a
+ /// wrapped version of it.
+ pub fn then<F, S1>(
+ self,
+ service: F,
+ ) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req>
+ where
+ Self: Sized,
+ F: IntoService<S1, Result<S::Response, S::Error>>,
+ S1: Service<Result<S::Response, S::Error>, Error = S::Error>,
+ {
+ Pipeline {
+ service: ThenService::new(self.service, service.into_service()),
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Map this service's output to a different type, returning a new service
+ /// of the resulting type.
+ ///
+ /// This function is similar to the `Option::map` or `Iterator::map` where
+ /// it will change the type of the underlying service.
+ ///
+ /// Note that this function consumes the receiving service and returns a
+ /// wrapped version of it, similar to the existing `map` methods in the
+ /// standard library.
+ pub fn map<F, R>(self, f: F) -> Pipeline<Map<S, F, Req, R>, Req>
+ where
+ Self: Sized,
+ F: FnMut(S::Response) -> R,
+ {
+ Pipeline {
+ service: Map::new(self.service, f),
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Map this service's error to a different error, returning a new service.
+ ///
+ /// This function is similar to the `Result::map_err` where it will change
+ /// the error type of the underlying service. This is useful for example to
+ /// ensure that services have the same error type.
+ ///
+ /// Note that this function consumes the receiving service and returns a
+ /// wrapped version of it.
+ pub fn map_err<F, E>(self, f: F) -> Pipeline<MapErr<S, Req, F, E>, Req>
+ where
+ Self: Sized,
+ F: Fn(S::Error) -> E,
+ {
+ Pipeline {
+ service: MapErr::new(self.service, f),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<T, Req> Clone for Pipeline<T, Req>
+where
+ T: Clone,
+{
+ fn clone(&self) -> Self {
+ Pipeline {
+ service: self.service.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<S: Service<Req>, Req> Service<Req> for Pipeline<S, Req> {
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ #[inline]
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
+ self.service.poll_ready(ctx)
+ }
+
+ #[inline]
+ fn call(&self, req: Req) -> Self::Future {
+ self.service.call(req)
+ }
+}
+
+/// Pipeline factory
+pub(crate) struct PipelineFactory<SF, Req> {
+ factory: SF,
+ _phantom: PhantomData<fn(Req)>,
+}
+
+impl<SF, Req> PipelineFactory<SF, Req>
+where
+ SF: ServiceFactory<Req>,
+{
+ /// Call another service after call to this one has resolved successfully.
+ pub fn and_then<I, SF1>(
+ self,
+ factory: I,
+ ) -> PipelineFactory<
+ impl ServiceFactory<
+ Req,
+ Response = SF1::Response,
+ Error = SF::Error,
+ Config = SF::Config,
+ InitError = SF::InitError,
+ Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone,
+ > + Clone,
+ Req,
+ >
+ where
+ Self: Sized,
+ SF::Config: Clone,
+ I: IntoServiceFactory<SF1, SF::Response>,
+ SF1: ServiceFactory<
+ SF::Response,
+ Config = SF::Config,
+ Error = SF::Error,
+ InitError = SF::InitError,
+ >,
+ {
+ PipelineFactory {
+ factory: AndThenServiceFactory::new(self.factory, factory.into_factory()),
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Create `NewService` to chain on a computation for when a call to the
+ /// service finished, passing the result of the call to the next
+ /// service `U`.
+ ///
+ /// Note that this function consumes the receiving pipeline and returns a
+ /// wrapped version of it.
+ pub fn then<I, SF1>(
+ self,
+ factory: I,
+ ) -> PipelineFactory<
+ impl ServiceFactory<
+ Req,
+ Response = SF1::Response,
+ Error = SF::Error,
+ Config = SF::Config,
+ InitError = SF::InitError,
+ Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone,
+ > + Clone,
+ Req,
+ >
+ where
+ Self: Sized,
+ SF::Config: Clone,
+ I: IntoServiceFactory<SF1, Result<SF::Response, SF::Error>>,
+ SF1: ServiceFactory<
+ Result<SF::Response, SF::Error>,
+ Config = SF::Config,
+ Error = SF::Error,
+ InitError = SF::InitError,
+ >,
+ {
+ PipelineFactory {
+ factory: ThenServiceFactory::new(self.factory, factory.into_factory()),
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Map this service's output to a different type, returning a new service
+ /// of the resulting type.
+ pub fn map<F, R>(self, f: F) -> PipelineFactory<MapServiceFactory<SF, F, Req, R>, Req>
+ where
+ Self: Sized,
+ F: FnMut(SF::Response) -> R + Clone,
+ {
+ PipelineFactory {
+ factory: MapServiceFactory::new(self.factory, f),
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Map this service's error to a different error, returning a new service.
+ pub fn map_err<F, E>(self, f: F) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
+ where
+ Self: Sized,
+ F: Fn(SF::Error) -> E + Clone,
+ {
+ PipelineFactory {
+ factory: MapErrServiceFactory::new(self.factory, f),
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Map this factory's init error to a different error, returning a new service.
+ pub fn map_init_err<F, E>(self, f: F) -> PipelineFactory<MapInitErr<SF, F, Req, E>, Req>
+ where
+ Self: Sized,
+ F: Fn(SF::InitError) -> E + Clone,
+ {
+ PipelineFactory {
+ factory: MapInitErr::new(self.factory, f),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<T, Req> Clone for PipelineFactory<T, Req>
+where
+ T: Clone,
+{
+ fn clone(&self) -> Self {
+ PipelineFactory {
+ factory: self.factory.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<SF, Req> ServiceFactory<Req> for PipelineFactory<SF, Req>
+where
+ SF: ServiceFactory<Req>,
+{
+ type Config = SF::Config;
+ type Response = SF::Response;
+ type Error = SF::Error;
+ type Service = SF::Service;
+ type InitError = SF::InitError;
+ type Future = SF::Future;
+
+ #[inline]
+ fn new_service(&self, cfg: SF::Config) -> Self::Future {
+ self.factory.new_service(cfg)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +
//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`.
+
+use core::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+/// Future for the [`ready`](ready()) function.
+#[derive(Debug, Clone)]
+#[must_use = "futures do nothing unless you `.await` or poll them"]
+pub struct Ready<T> {
+ val: Option<T>,
+}
+
+impl<T> Ready<T> {
+ /// Unwraps the value from this immediately ready future.
+ #[inline]
+ pub fn into_inner(mut self) -> T {
+ self.val.take().unwrap()
+ }
+}
+
+impl<T> Unpin for Ready<T> {}
+
+impl<T> Future for Ready<T> {
+ type Output = T;
+
+ #[inline]
+ fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
+ let val = self.val.take().expect("Ready can not be polled twice.");
+ Poll::Ready(val)
+ }
+}
+
+/// Creates a future that is immediately ready with a value.
+#[allow(dead_code)]
+pub(crate) fn ready<T>(val: T) -> Ready<T> {
+ Ready { val: Some(val) }
+}
+
+/// Create a future that is immediately ready with a success value.
+#[allow(dead_code)]
+pub(crate) fn ok<T, E>(val: T) -> Ready<Result<T, E>> {
+ Ready { val: Some(Ok(val)) }
+}
+
+/// Create a future that is immediately ready with an error value.
+#[allow(dead_code)]
+pub(crate) fn err<T, E>(err: E) -> Ready<Result<T, E>> {
+ Ready {
+ val: Some(Err(err)),
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +
use alloc::rc::Rc;
+use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use futures_core::ready;
+use pin_project_lite::pin_project;
+
+use super::{Service, ServiceFactory};
+
+/// Service for the `then` combinator, chaining a computation onto the end of
+/// another service.
+///
+/// This is created by the `Pipeline::then` method.
+pub(crate) struct ThenService<A, B, Req>(Rc<(A, B)>, PhantomData<Req>);
+
+impl<A, B, Req> ThenService<A, B, Req> {
+ /// Create new `.then()` combinator
+ pub(crate) fn new(a: A, b: B) -> ThenService<A, B, Req>
+ where
+ A: Service<Req>,
+ B: Service<Result<A::Response, A::Error>, Error = A::Error>,
+ {
+ Self(Rc::new((a, b)), PhantomData)
+ }
+}
+
+impl<A, B, Req> Clone for ThenService<A, B, Req> {
+ fn clone(&self) -> Self {
+ ThenService(self.0.clone(), PhantomData)
+ }
+}
+
+impl<A, B, Req> Service<Req> for ThenService<A, B, Req>
+where
+ A: Service<Req>,
+ B: Service<Result<A::Response, A::Error>, Error = A::Error>,
+{
+ type Response = B::Response;
+ type Error = B::Error;
+ type Future = ThenServiceResponse<A, B, Req>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ let (a, b) = &*self.0;
+ let not_ready = !a.poll_ready(cx)?.is_ready();
+ if !b.poll_ready(cx)?.is_ready() || not_ready {
+ Poll::Pending
+ } else {
+ Poll::Ready(Ok(()))
+ }
+ }
+
+ fn call(&self, req: Req) -> Self::Future {
+ ThenServiceResponse {
+ state: State::A {
+ fut: self.0 .0.call(req),
+ b: Some(self.0.clone()),
+ },
+ }
+ }
+}
+
+pin_project! {
+ pub(crate) struct ThenServiceResponse<A, B, Req>
+ where
+ A: Service<Req>,
+ B: Service<Result<A::Response, A::Error>>,
+ {
+ #[pin]
+ state: State<A, B, Req>,
+ }
+}
+
+pin_project! {
+ #[project = StateProj]
+ enum State<A, B, Req>
+ where
+ A: Service<Req>,
+ B: Service<Result<A::Response, A::Error>>,
+ {
+ A { #[pin] fut: A::Future, b: Option<Rc<(A, B)>> },
+ B { #[pin] fut: B::Future },
+ }
+}
+
+impl<A, B, Req> Future for ThenServiceResponse<A, B, Req>
+where
+ A: Service<Req>,
+ B: Service<Result<A::Response, A::Error>>,
+{
+ type Output = Result<B::Response, B::Error>;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.as_mut().project();
+
+ match this.state.as_mut().project() {
+ StateProj::A { fut, b } => {
+ let res = ready!(fut.poll(cx));
+ let b = b.take().unwrap();
+ let fut = b.1.call(res);
+ this.state.set(State::B { fut });
+ self.poll(cx)
+ }
+ StateProj::B { fut } => fut.poll(cx),
+ }
+ }
+}
+
+/// `.then()` service factory combinator
+pub(crate) struct ThenServiceFactory<A, B, Req>(Rc<(A, B)>, PhantomData<Req>);
+
+impl<A, B, Req> ThenServiceFactory<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ A::Config: Clone,
+ B: ServiceFactory<
+ Result<A::Response, A::Error>,
+ Config = A::Config,
+ Error = A::Error,
+ InitError = A::InitError,
+ >,
+{
+ /// Create new `AndThen` combinator
+ pub(crate) fn new(a: A, b: B) -> Self {
+ Self(Rc::new((a, b)), PhantomData)
+ }
+}
+
+impl<A, B, Req> ServiceFactory<Req> for ThenServiceFactory<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ A::Config: Clone,
+ B: ServiceFactory<
+ Result<A::Response, A::Error>,
+ Config = A::Config,
+ Error = A::Error,
+ InitError = A::InitError,
+ >,
+{
+ type Response = B::Response;
+ type Error = A::Error;
+
+ type Config = A::Config;
+ type Service = ThenService<A::Service, B::Service, Req>;
+ type InitError = A::InitError;
+ type Future = ThenServiceFactoryResponse<A, B, Req>;
+
+ fn new_service(&self, cfg: A::Config) -> Self::Future {
+ let srv = &*self.0;
+ ThenServiceFactoryResponse::new(srv.0.new_service(cfg.clone()), srv.1.new_service(cfg))
+ }
+}
+
+impl<A, B, Req> Clone for ThenServiceFactory<A, B, Req> {
+ fn clone(&self) -> Self {
+ Self(self.0.clone(), PhantomData)
+ }
+}
+
+pin_project! {
+ pub(crate) struct ThenServiceFactoryResponse<A, B, Req>
+ where
+ A: ServiceFactory<Req>,
+ B: ServiceFactory<
+ Result<A::Response, A::Error>,
+ Config = A::Config,
+ Error = A::Error,
+ InitError = A::InitError,
+ >,
+ {
+ #[pin]
+ fut_b: B::Future,
+ #[pin]
+ fut_a: A::Future,
+ a: Option<A::Service>,
+ b: Option<B::Service>,
+ }
+}
+
+impl<A, B, Req> ThenServiceFactoryResponse<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ B: ServiceFactory<
+ Result<A::Response, A::Error>,
+ Config = A::Config,
+ Error = A::Error,
+ InitError = A::InitError,
+ >,
+{
+ fn new(fut_a: A::Future, fut_b: B::Future) -> Self {
+ Self {
+ fut_a,
+ fut_b,
+ a: None,
+ b: None,
+ }
+ }
+}
+
+impl<A, B, Req> Future for ThenServiceFactoryResponse<A, B, Req>
+where
+ A: ServiceFactory<Req>,
+ B: ServiceFactory<
+ Result<A::Response, A::Error>,
+ Config = A::Config,
+ Error = A::Error,
+ InitError = A::InitError,
+ >,
+{
+ type Output = Result<ThenService<A::Service, B::Service, Req>, A::InitError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+
+ if this.a.is_none() {
+ if let Poll::Ready(service) = this.fut_a.poll(cx)? {
+ *this.a = Some(service);
+ }
+ }
+ if this.b.is_none() {
+ if let Poll::Ready(service) = this.fut_b.poll(cx)? {
+ *this.b = Some(service);
+ }
+ }
+ if this.a.is_some() && this.b.is_some() {
+ Poll::Ready(Ok(ThenService::new(
+ this.a.take().unwrap(),
+ this.b.take().unwrap(),
+ )))
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::rc::Rc;
+ use core::{
+ cell::Cell,
+ task::{Context, Poll},
+ };
+
+ use futures_util::future::lazy;
+
+ use crate::{
+ err, ok,
+ pipeline::{pipeline, pipeline_factory},
+ ready, Ready, Service, ServiceFactory,
+ };
+
+ #[derive(Clone)]
+ struct Srv1(Rc<Cell<usize>>);
+
+ impl Service<Result<&'static str, &'static str>> for Srv1 {
+ type Response = &'static str;
+ type Error = ();
+ type Future = Ready<Result<Self::Response, Self::Error>>;
+
+ fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.0.set(self.0.get() + 1);
+ Poll::Ready(Ok(()))
+ }
+
+ fn call(&self, req: Result<&'static str, &'static str>) -> Self::Future {
+ match req {
+ Ok(msg) => ok(msg),
+ Err(_) => err(()),
+ }
+ }
+ }
+
+ struct Srv2(Rc<Cell<usize>>);
+
+ impl Service<Result<&'static str, ()>> for Srv2 {
+ type Response = (&'static str, &'static str);
+ type Error = ();
+ type Future = Ready<Result<Self::Response, ()>>;
+
+ fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.0.set(self.0.get() + 1);
+ Poll::Ready(Err(()))
+ }
+
+ fn call(&self, req: Result<&'static str, ()>) -> Self::Future {
+ match req {
+ Ok(msg) => ok((msg, "ok")),
+ Err(()) => ok(("srv2", "err")),
+ }
+ }
+ }
+
+ #[actix_rt::test]
+ async fn test_poll_ready() {
+ let cnt = Rc::new(Cell::new(0));
+ let srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt.clone()));
+ let res = lazy(|cx| srv.poll_ready(cx)).await;
+ assert_eq!(res, Poll::Ready(Err(())));
+ assert_eq!(cnt.get(), 2);
+ }
+
+ #[actix_rt::test]
+ async fn test_call() {
+ let cnt = Rc::new(Cell::new(0));
+ let srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt));
+
+ let res = srv.call(Ok("srv1")).await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv1", "ok"));
+
+ let res = srv.call(Err("srv")).await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv2", "err"));
+ }
+
+ #[actix_rt::test]
+ async fn test_factory() {
+ let cnt = Rc::new(Cell::new(0));
+ let cnt2 = cnt.clone();
+ let blank = move || ready(Ok::<_, ()>(Srv1(cnt2.clone())));
+ let factory = pipeline_factory(blank).then(move || ready(Ok(Srv2(cnt.clone()))));
+ let srv = factory.new_service(&()).await.unwrap();
+ let res = srv.call(Ok("srv1")).await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv1", "ok"));
+
+ let res = srv.call(Err("srv")).await;
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), ("srv2", "err"));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +
use alloc::{rc::Rc, sync::Arc};
+use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use futures_core::ready;
+use pin_project_lite::pin_project;
+
+use crate::{IntoServiceFactory, Service, ServiceFactory};
+
+/// Apply a [`Transform`] to a [`Service`].
+pub fn apply<T, S, I, Req>(t: T, factory: I) -> ApplyTransform<T, S, Req>
+where
+ I: IntoServiceFactory<S, Req>,
+ S: ServiceFactory<Req>,
+ T: Transform<S::Service, Req, InitError = S::InitError>,
+{
+ ApplyTransform::new(t, factory.into_factory())
+}
+
+/// Defines the interface of a service factory that wraps inner service during construction.
+///
+/// Transformers wrap an inner service and runs during inbound and/or outbound processing in the
+/// service lifecycle. It may modify request and/or response.
+///
+/// For example, a timeout service wrapper:
+///
+/// ```ignore
+/// pub struct Timeout<S> {
+/// service: S,
+/// timeout: Duration,
+/// }
+///
+/// impl<S: Service<Req>, Req> Service<Req> for Timeout<S> {
+/// type Response = S::Response;
+/// type Error = TimeoutError<S::Error>;
+/// type Future = TimeoutServiceResponse<S>;
+///
+/// actix_service::forward_ready!(service);
+///
+/// fn call(&self, req: Req) -> Self::Future {
+/// TimeoutServiceResponse {
+/// fut: self.service.call(req),
+/// sleep: Sleep::new(clock::now() + self.timeout),
+/// }
+/// }
+/// }
+/// ```
+///
+/// This wrapper service is decoupled from the underlying service implementation and could be
+/// applied to any service.
+///
+/// The `Transform` trait defines the interface of a service wrapper. `Transform` is often
+/// implemented for middleware, defining how to construct a middleware Service. A Service that is
+/// constructed by the factory takes the Service that follows it during execution as a parameter,
+/// assuming ownership of the next Service.
+///
+/// A transform for the `Timeout` middleware could look like this:
+///
+/// ```ignore
+/// pub struct TimeoutTransform {
+/// timeout: Duration,
+/// }
+///
+/// impl<S: Service<Req>, Req> Transform<S, Req> for TimeoutTransform {
+/// type Response = S::Response;
+/// type Error = TimeoutError<S::Error>;
+/// type InitError = S::Error;
+/// type Transform = Timeout<S>;
+/// type Future = Ready<Result<Self::Transform, Self::InitError>>;
+///
+/// fn new_transform(&self, service: S) -> Self::Future {
+/// ready(Ok(Timeout {
+/// service,
+/// timeout: self.timeout,
+/// }))
+/// }
+/// }
+/// ```
+pub trait Transform<S, Req> {
+ /// Responses produced by the service.
+ type Response;
+
+ /// Errors produced by the service.
+ type Error;
+
+ /// The `TransformService` value created by this factory
+ type Transform: Service<Req, Response = Self::Response, Error = Self::Error>;
+
+ /// Errors produced while building a transform service.
+ type InitError;
+
+ /// The future response value.
+ type Future: Future<Output = Result<Self::Transform, Self::InitError>>;
+
+ /// Creates and returns a new Transform component, asynchronously
+ fn new_transform(&self, service: S) -> Self::Future;
+}
+
+impl<T, S, Req> Transform<S, Req> for Rc<T>
+where
+ T: Transform<S, Req>,
+{
+ type Response = T::Response;
+ type Error = T::Error;
+ type Transform = T::Transform;
+ type InitError = T::InitError;
+ type Future = T::Future;
+
+ fn new_transform(&self, service: S) -> T::Future {
+ self.as_ref().new_transform(service)
+ }
+}
+
+impl<T, S, Req> Transform<S, Req> for Arc<T>
+where
+ T: Transform<S, Req>,
+{
+ type Response = T::Response;
+ type Error = T::Error;
+ type Transform = T::Transform;
+ type InitError = T::InitError;
+ type Future = T::Future;
+
+ fn new_transform(&self, service: S) -> T::Future {
+ self.as_ref().new_transform(service)
+ }
+}
+
+/// Apply a [`Transform`] to a [`Service`].
+pub struct ApplyTransform<T, S, Req>(Rc<(T, S)>, PhantomData<Req>);
+
+impl<T, S, Req> ApplyTransform<T, S, Req>
+where
+ S: ServiceFactory<Req>,
+ T: Transform<S::Service, Req, InitError = S::InitError>,
+{
+ /// Create new `ApplyTransform` new service instance
+ fn new(t: T, service: S) -> Self {
+ Self(Rc::new((t, service)), PhantomData)
+ }
+}
+
+impl<T, S, Req> Clone for ApplyTransform<T, S, Req> {
+ fn clone(&self) -> Self {
+ ApplyTransform(self.0.clone(), PhantomData)
+ }
+}
+
+impl<T, S, Req> ServiceFactory<Req> for ApplyTransform<T, S, Req>
+where
+ S: ServiceFactory<Req>,
+ T: Transform<S::Service, Req, InitError = S::InitError>,
+{
+ type Response = T::Response;
+ type Error = T::Error;
+
+ type Config = S::Config;
+ type Service = T::Transform;
+ type InitError = T::InitError;
+ type Future = ApplyTransformFuture<T, S, Req>;
+
+ fn new_service(&self, cfg: S::Config) -> Self::Future {
+ ApplyTransformFuture {
+ store: self.0.clone(),
+ state: ApplyTransformFutureState::A {
+ fut: self.0.as_ref().1.new_service(cfg),
+ },
+ }
+ }
+}
+
+pin_project! {
+ pub struct ApplyTransformFuture<T, S, Req>
+ where
+ S: ServiceFactory<Req>,
+ T: Transform<S::Service, Req, InitError = S::InitError>,
+ {
+ store: Rc<(T, S)>,
+ #[pin]
+ state: ApplyTransformFutureState<T, S, Req>,
+ }
+}
+
+pin_project! {
+ #[project = ApplyTransformFutureStateProj]
+ pub enum ApplyTransformFutureState<T, S, Req>
+ where
+ S: ServiceFactory<Req>,
+ T: Transform<S::Service, Req, InitError = S::InitError>,
+ {
+ A { #[pin] fut: S::Future },
+ B { #[pin] fut: T::Future },
+ }
+}
+
+impl<T, S, Req> Future for ApplyTransformFuture<T, S, Req>
+where
+ S: ServiceFactory<Req>,
+ T: Transform<S::Service, Req, InitError = S::InitError>,
+{
+ type Output = Result<T::Transform, T::InitError>;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.as_mut().project();
+
+ match this.state.as_mut().project() {
+ ApplyTransformFutureStateProj::A { fut } => {
+ let srv = ready!(fut.poll(cx))?;
+ let fut = this.store.0.new_transform(srv);
+ this.state.set(ApplyTransformFutureState::B { fut });
+ self.poll(cx)
+ }
+ ApplyTransformFutureStateProj::B { fut } => fut.poll(cx),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::time::Duration;
+
+ use actix_utils::future::{ready, Ready};
+
+ use super::*;
+
+ // pseudo-doctest for Transform trait
+ pub struct TimeoutTransform {
+ timeout: Duration,
+ }
+
+ // pseudo-doctest for Transform trait
+ impl<S: Service<Req>, Req> Transform<S, Req> for TimeoutTransform {
+ type Response = S::Response;
+ type Error = S::Error;
+ type InitError = S::Error;
+ type Transform = Timeout<S>;
+ type Future = Ready<Result<Self::Transform, Self::InitError>>;
+
+ fn new_transform(&self, service: S) -> Self::Future {
+ ready(Ok(Timeout {
+ service,
+ _timeout: self.timeout,
+ }))
+ }
+ }
+
+ // pseudo-doctest for Transform trait
+ pub struct Timeout<S> {
+ service: S,
+ _timeout: Duration,
+ }
+
+ // pseudo-doctest for Transform trait
+ impl<S: Service<Req>, Req> Service<Req> for Timeout<S> {
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ crate::forward_ready!(service);
+
+ fn call(&self, req: Req) -> Self::Future {
+ self.service.call(req)
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +
use core::{
+ future::Future,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use pin_project_lite::pin_project;
+
+use super::Transform;
+
+/// Transform for the [`TransformExt::map_init_err`] combinator, changing the type of a new
+/// [`Transform`]'s initialization error.
+pub struct TransformMapInitErr<T, S, Req, F, E> {
+ transform: T,
+ mapper: F,
+ _phantom: PhantomData<fn(Req) -> (S, E)>,
+}
+
+impl<T, S, F, E, Req> TransformMapInitErr<T, S, Req, F, E> {
+ pub(crate) fn new(t: T, f: F) -> Self
+ where
+ T: Transform<S, Req>,
+ F: Fn(T::InitError) -> E,
+ {
+ Self {
+ transform: t,
+ mapper: f,
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<T, S, Req, F, E> Clone for TransformMapInitErr<T, S, Req, F, E>
+where
+ T: Clone,
+ F: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ transform: self.transform.clone(),
+ mapper: self.mapper.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl<T, S, F, E, Req> Transform<S, Req> for TransformMapInitErr<T, S, Req, F, E>
+where
+ T: Transform<S, Req>,
+ F: Fn(T::InitError) -> E + Clone,
+{
+ type Response = T::Response;
+ type Error = T::Error;
+ type Transform = T::Transform;
+
+ type InitError = E;
+ type Future = TransformMapInitErrFuture<T, S, F, E, Req>;
+
+ fn new_transform(&self, service: S) -> Self::Future {
+ TransformMapInitErrFuture {
+ fut: self.transform.new_transform(service),
+ f: self.mapper.clone(),
+ }
+ }
+}
+
+pin_project! {
+ pub struct TransformMapInitErrFuture<T, S, F, E, Req>
+ where
+ T: Transform<S, Req>,
+ F: Fn(T::InitError) -> E,
+ {
+ #[pin]
+ fut: T::Future,
+ f: F,
+ }
+}
+
+impl<T, S, F, E, Req> Future for TransformMapInitErrFuture<T, S, F, E, Req>
+where
+ T: Transform<S, Req>,
+ F: Fn(T::InitError) -> E + Clone,
+{
+ type Output = Result<T::Transform, E>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+ if let Poll::Ready(res) = this.fut.poll(cx) {
+ Poll::Ready(res.map_err(this.f))
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +
//! TLS connection acceptor services.
+
+use std::{
+ convert::Infallible,
+ error::Error,
+ fmt,
+ sync::atomic::{AtomicUsize, Ordering},
+};
+
+use actix_utils::counter::Counter;
+
+#[cfg(feature = "openssl")]
+pub mod openssl;
+
+#[cfg(feature = "rustls-0_20")]
+pub mod rustls_0_20;
+
+#[doc(hidden)]
+#[cfg(feature = "rustls-0_20")]
+pub use rustls_0_20 as rustls;
+
+#[cfg(feature = "rustls-0_21")]
+pub mod rustls_0_21;
+
+#[cfg(feature = "rustls-0_22")]
+pub mod rustls_0_22;
+
+#[cfg(feature = "rustls-0_23")]
+pub mod rustls_0_23;
+
+#[cfg(feature = "native-tls")]
+pub mod native_tls;
+
+pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
+
+#[cfg(any(
+ feature = "openssl",
+ feature = "rustls-0_20",
+ feature = "rustls-0_21",
+ feature = "rustls-0_22",
+ feature = "rustls-0_23",
+ feature = "native-tls",
+))]
+pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
+ std::time::Duration::from_secs(3);
+
+thread_local! {
+ static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed));
+}
+
+/// Sets the maximum per-worker concurrent TLS connection limit.
+///
+/// All listeners will stop accepting connections when this limit is reached.
+/// It can be used to regulate the global TLS CPU usage.
+///
+/// By default, the connection limit is 256.
+pub fn max_concurrent_tls_connect(num: usize) {
+ MAX_CONN.store(num, Ordering::Relaxed);
+}
+
+/// TLS handshake error, TLS timeout, or inner service error.
+///
+/// All TLS acceptors from this crate will return the `SvcErr` type parameter as [`Infallible`],
+/// which can be cast to your own service type, inferred or otherwise, using [`into_service_error`].
+///
+/// [`into_service_error`]: Self::into_service_error
+#[derive(Debug)]
+pub enum TlsError<TlsErr, SvcErr> {
+ /// TLS handshake has timed-out.
+ Timeout,
+
+ /// Wraps TLS service errors.
+ Tls(TlsErr),
+
+ /// Wraps service errors.
+ Service(SvcErr),
+}
+
+impl<TlsErr> TlsError<TlsErr, Infallible> {
+ /// Casts the infallible service error type returned from acceptors into caller's type.
+ ///
+ /// # Examples
+ /// ```
+ /// # use std::convert::Infallible;
+ /// # use actix_tls::accept::TlsError;
+ /// let a: TlsError<u32, Infallible> = TlsError::Tls(42);
+ /// let _b: TlsError<u32, u64> = a.into_service_error();
+ /// ```
+ pub fn into_service_error<SvcErr>(self) -> TlsError<TlsErr, SvcErr> {
+ match self {
+ Self::Timeout => TlsError::Timeout,
+ Self::Tls(err) => TlsError::Tls(err),
+ Self::Service(err) => match err {},
+ }
+ }
+}
+
+impl<TlsErr, SvcErr> fmt::Display for TlsError<TlsErr, SvcErr> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Timeout => f.write_str("TLS handshake has timed-out"),
+ Self::Tls(_) => f.write_str("TLS handshake error"),
+ Self::Service(_) => f.write_str("Service error"),
+ }
+ }
+}
+
+impl<TlsErr, SvcErr> Error for TlsError<TlsErr, SvcErr>
+where
+ TlsErr: Error + 'static,
+ SvcErr: Error + 'static,
+{
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ TlsError::Tls(err) => Some(err),
+ TlsError::Service(err) => Some(err),
+ TlsError::Timeout => None,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn tls_service_error_inference() {
+ let a: TlsError<u32, Infallible> = TlsError::Tls(42);
+ let _b: TlsError<u32, u64> = a.into_service_error();
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +
//! `native-tls` based TLS connection acceptor service.
+//!
+//! See [`Acceptor`] for main service factory docs.
+
+use std::{
+ convert::Infallible,
+ io::{self, IoSlice},
+ pin::Pin,
+ task::{Context, Poll},
+ time::Duration,
+};
+
+use actix_rt::{
+ net::{ActixStream, Ready},
+ time::timeout,
+};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::{
+ counter::Counter,
+ future::{ready, Ready as FutReady},
+};
+use futures_core::future::LocalBoxFuture;
+use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+use tokio_native_tls::{native_tls::Error, TlsAcceptor};
+
+use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
+
+pub mod reexports {
+ //! Re-exports from `native-tls` that are useful for acceptors.
+
+ pub use tokio_native_tls::{native_tls::Error, TlsAcceptor};
+}
+
+/// Wraps a `native-tls` based async TLS stream in order to implement [`ActixStream`].
+pub struct TlsStream<IO>(tokio_native_tls::TlsStream<IO>);
+
+impl_more::impl_from!(<IO> in tokio_native_tls::TlsStream<IO> => TlsStream<IO>);
+impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_native_tls::TlsStream<IO>);
+
+impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut ReadBuf<'_>,
+ ) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
+ }
+}
+
+impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
+ fn poll_write(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_flush(cx)
+ }
+
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
+ }
+
+ fn poll_write_vectored(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ bufs: &[IoSlice<'_>],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ (**self).is_write_vectored()
+ }
+}
+
+impl<IO: ActixStream> ActixStream for TlsStream<IO> {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_read_ready((**self).get_ref().get_ref().get_ref(), cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_write_ready((**self).get_ref().get_ref().get_ref(), cx)
+ }
+}
+
+/// Accept TLS connections via the `native-tls` crate.
+pub struct Acceptor {
+ acceptor: TlsAcceptor,
+ handshake_timeout: Duration,
+}
+
+impl Acceptor {
+ /// Constructs `native-tls` based acceptor service factory.
+ pub fn new(acceptor: TlsAcceptor) -> Self {
+ Acceptor {
+ acceptor,
+ handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
+ }
+ }
+
+ /// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+ ///
+ /// Default timeout is 3 seconds.
+ pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
+ self.handshake_timeout = handshake_timeout;
+ self
+ }
+}
+
+impl Clone for Acceptor {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self {
+ acceptor: self.acceptor.clone(),
+ handshake_timeout: self.handshake_timeout,
+ }
+ }
+}
+
+impl<IO: ActixStream + 'static> ServiceFactory<IO> for Acceptor {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<Error, Infallible>;
+ type Config = ();
+ type Service = AcceptorService;
+ type InitError = ();
+ type Future = FutReady<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ let res = MAX_CONN_COUNTER.with(|conns| {
+ Ok(AcceptorService {
+ acceptor: self.acceptor.clone(),
+ conns: conns.clone(),
+ handshake_timeout: self.handshake_timeout,
+ })
+ });
+
+ ready(res)
+ }
+}
+
+/// Native-TLS based acceptor service.
+pub struct AcceptorService {
+ acceptor: TlsAcceptor,
+ conns: Counter,
+ handshake_timeout: Duration,
+}
+
+impl<IO: ActixStream + 'static> Service<IO> for AcceptorService {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<Error, Infallible>;
+ type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ if self.conns.available(cx) {
+ Poll::Ready(Ok(()))
+ } else {
+ Poll::Pending
+ }
+ }
+
+ fn call(&self, io: IO) -> Self::Future {
+ let guard = self.conns.get();
+ let acceptor = self.acceptor.clone();
+
+ let dur = self.handshake_timeout;
+
+ Box::pin(async move {
+ match timeout(dur, acceptor.accept(io)).await {
+ Ok(Ok(io)) => {
+ drop(guard);
+ Ok(TlsStream(io))
+ }
+ Ok(Err(err)) => Err(TlsError::Tls(err)),
+ Err(_timeout) => Err(TlsError::Timeout),
+ }
+ })
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +
//! `openssl` based TLS acceptor service.
+//!
+//! See [`Acceptor`] for main service factory docs.
+
+use std::{
+ convert::Infallible,
+ future::Future,
+ io::{self, IoSlice},
+ pin::Pin,
+ task::{Context, Poll},
+ time::Duration,
+};
+
+use actix_rt::{
+ net::{ActixStream, Ready},
+ time::{sleep, Sleep},
+};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::{
+ counter::{Counter, CounterGuard},
+ future::{ready, Ready as FutReady},
+};
+use openssl::ssl::{Error, Ssl, SslAcceptor};
+use pin_project_lite::pin_project;
+use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+
+use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
+
+pub mod reexports {
+ //! Re-exports from `openssl` that are useful for acceptors.
+
+ pub use openssl::ssl::{
+ AlpnError, Error, HandshakeError, Ssl, SslAcceptor, SslAcceptorBuilder,
+ };
+}
+
+/// Wraps an `openssl` based async TLS stream in order to implement [`ActixStream`].
+pub struct TlsStream<IO>(tokio_openssl::SslStream<IO>);
+
+impl_more::impl_from!(<IO> in tokio_openssl::SslStream<IO> => TlsStream<IO>);
+impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_openssl::SslStream<IO>);
+
+impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut ReadBuf<'_>,
+ ) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
+ }
+}
+
+impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
+ fn poll_write(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_flush(cx)
+ }
+
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
+ }
+
+ fn poll_write_vectored(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ bufs: &[IoSlice<'_>],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ (**self).is_write_vectored()
+ }
+}
+
+impl<IO: ActixStream> ActixStream for TlsStream<IO> {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_read_ready((**self).get_ref(), cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_write_ready((**self).get_ref(), cx)
+ }
+}
+
+/// Accept TLS connections via the `openssl` crate.
+pub struct Acceptor {
+ acceptor: SslAcceptor,
+ handshake_timeout: Duration,
+}
+
+impl Acceptor {
+ /// Create `openssl` based acceptor service factory.
+ #[inline]
+ pub fn new(acceptor: SslAcceptor) -> Self {
+ Acceptor {
+ acceptor,
+ handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
+ }
+ }
+
+ /// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+ ///
+ /// Default timeout is 3 seconds.
+ pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
+ self.handshake_timeout = handshake_timeout;
+ self
+ }
+}
+
+impl Clone for Acceptor {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self {
+ acceptor: self.acceptor.clone(),
+ handshake_timeout: self.handshake_timeout,
+ }
+ }
+}
+
+impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<Error, Infallible>;
+ type Config = ();
+ type Service = AcceptorService;
+ type InitError = ();
+ type Future = FutReady<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ let res = MAX_CONN_COUNTER.with(|conns| {
+ Ok(AcceptorService {
+ acceptor: self.acceptor.clone(),
+ conns: conns.clone(),
+ handshake_timeout: self.handshake_timeout,
+ })
+ });
+
+ ready(res)
+ }
+}
+
+/// OpenSSL based acceptor service.
+pub struct AcceptorService {
+ acceptor: SslAcceptor,
+ conns: Counter,
+ handshake_timeout: Duration,
+}
+
+impl<IO: ActixStream> Service<IO> for AcceptorService {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<Error, Infallible>;
+ type Future = AcceptFut<IO>;
+
+ fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ if self.conns.available(ctx) {
+ Poll::Ready(Ok(()))
+ } else {
+ Poll::Pending
+ }
+ }
+
+ fn call(&self, io: IO) -> Self::Future {
+ let ssl_ctx = self.acceptor.context();
+ let ssl = Ssl::new(ssl_ctx).expect("Provided SSL acceptor was invalid.");
+
+ AcceptFut {
+ _guard: self.conns.get(),
+ timeout: sleep(self.handshake_timeout),
+ stream: Some(tokio_openssl::SslStream::new(ssl, io).unwrap()),
+ }
+ }
+}
+
+pin_project! {
+ /// Accept future for OpenSSL service.
+ #[doc(hidden)]
+ pub struct AcceptFut<IO: ActixStream> {
+ stream: Option<tokio_openssl::SslStream<IO>>,
+ #[pin]
+ timeout: Sleep,
+ _guard: CounterGuard,
+ }
+}
+
+impl<IO: ActixStream> Future for AcceptFut<IO> {
+ type Output = Result<TlsStream<IO>, TlsError<Error, Infallible>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+
+ match Pin::new(this.stream.as_mut().unwrap()).poll_accept(cx) {
+ Poll::Ready(Ok(())) => Poll::Ready(Ok(this
+ .stream
+ .take()
+ .expect("Acceptor should not be polled after it has completed.")
+ .into())),
+ Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
+ Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +
//! `rustls` v0.20 based TLS connection acceptor service.
+//!
+//! See [`Acceptor`] for main service factory docs.
+
+use std::{
+ convert::Infallible,
+ future::Future,
+ io::{self, IoSlice},
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+ time::Duration,
+};
+
+use actix_rt::{
+ net::{ActixStream, Ready},
+ time::{sleep, Sleep},
+};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::{
+ counter::{Counter, CounterGuard},
+ future::{ready, Ready as FutReady},
+};
+use pin_project_lite::pin_project;
+use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+use tokio_rustls::{Accept, TlsAcceptor};
+use tokio_rustls_023 as tokio_rustls;
+
+use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
+
+pub mod reexports {
+ //! Re-exports from `rustls` that are useful for acceptors.
+
+ pub use tokio_rustls_023::rustls::ServerConfig;
+}
+
+/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
+pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
+
+impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
+impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
+
+impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut ReadBuf<'_>,
+ ) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
+ }
+}
+
+impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
+ fn poll_write(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_flush(cx)
+ }
+
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
+ }
+
+ fn poll_write_vectored(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ bufs: &[IoSlice<'_>],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ (**self).is_write_vectored()
+ }
+}
+
+impl<IO: ActixStream> ActixStream for TlsStream<IO> {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_read_ready((**self).get_ref().0, cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_write_ready((**self).get_ref().0, cx)
+ }
+}
+
+/// Accept TLS connections via the `rustls` crate.
+pub struct Acceptor {
+ config: Arc<reexports::ServerConfig>,
+ handshake_timeout: Duration,
+}
+
+impl Acceptor {
+ /// Constructs `rustls` based acceptor service factory.
+ pub fn new(config: reexports::ServerConfig) -> Self {
+ Acceptor {
+ config: Arc::new(config),
+ handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
+ }
+ }
+
+ /// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+ ///
+ /// Default timeout is 3 seconds.
+ pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
+ self.handshake_timeout = handshake_timeout;
+ self
+ }
+}
+
+impl Clone for Acceptor {
+ fn clone(&self) -> Self {
+ Self {
+ config: self.config.clone(),
+ handshake_timeout: self.handshake_timeout,
+ }
+ }
+}
+
+impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Config = ();
+ type Service = AcceptorService;
+ type InitError = ();
+ type Future = FutReady<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ let res = MAX_CONN_COUNTER.with(|conns| {
+ Ok(AcceptorService {
+ acceptor: self.config.clone().into(),
+ conns: conns.clone(),
+ handshake_timeout: self.handshake_timeout,
+ })
+ });
+
+ ready(res)
+ }
+}
+
+/// Rustls based acceptor service.
+pub struct AcceptorService {
+ acceptor: TlsAcceptor,
+ conns: Counter,
+ handshake_timeout: Duration,
+}
+
+impl<IO: ActixStream> Service<IO> for AcceptorService {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Future = AcceptFut<IO>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ if self.conns.available(cx) {
+ Poll::Ready(Ok(()))
+ } else {
+ Poll::Pending
+ }
+ }
+
+ fn call(&self, req: IO) -> Self::Future {
+ AcceptFut {
+ fut: self.acceptor.accept(req),
+ timeout: sleep(self.handshake_timeout),
+ _guard: self.conns.get(),
+ }
+ }
+}
+
+pin_project! {
+ /// Accept future for Rustls service.
+ #[doc(hidden)]
+ pub struct AcceptFut<IO: ActixStream> {
+ fut: Accept<IO>,
+ #[pin]
+ timeout: Sleep,
+ _guard: CounterGuard,
+ }
+}
+
+impl<IO: ActixStream> Future for AcceptFut<IO> {
+ type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.project();
+ match Pin::new(&mut this.fut).poll(cx) {
+ Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
+ Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
+ Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +
//! `rustls` v0.21 based TLS connection acceptor service.
+//!
+//! See [`Acceptor`] for main service factory docs.
+
+use std::{
+ convert::Infallible,
+ future::Future,
+ io::{self, IoSlice},
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+ time::Duration,
+};
+
+use actix_rt::{
+ net::{ActixStream, Ready},
+ time::{sleep, Sleep},
+};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::{
+ counter::{Counter, CounterGuard},
+ future::{ready, Ready as FutReady},
+};
+use pin_project_lite::pin_project;
+use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+use tokio_rustls::{Accept, TlsAcceptor};
+use tokio_rustls_024 as tokio_rustls;
+
+use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
+
+pub mod reexports {
+ //! Re-exports from `rustls` that are useful for acceptors.
+
+ pub use tokio_rustls_024::rustls::ServerConfig;
+}
+
+/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
+pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
+
+impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
+impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
+
+impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut ReadBuf<'_>,
+ ) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
+ }
+}
+
+impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
+ fn poll_write(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_flush(cx)
+ }
+
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
+ }
+
+ fn poll_write_vectored(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ bufs: &[IoSlice<'_>],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ (**self).is_write_vectored()
+ }
+}
+
+impl<IO: ActixStream> ActixStream for TlsStream<IO> {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_read_ready((**self).get_ref().0, cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_write_ready((**self).get_ref().0, cx)
+ }
+}
+
+/// Accept TLS connections via the `rustls` crate.
+pub struct Acceptor {
+ config: Arc<reexports::ServerConfig>,
+ handshake_timeout: Duration,
+}
+
+impl Acceptor {
+ /// Constructs `rustls` based acceptor service factory.
+ pub fn new(config: reexports::ServerConfig) -> Self {
+ Acceptor {
+ config: Arc::new(config),
+ handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
+ }
+ }
+
+ /// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+ ///
+ /// Default timeout is 3 seconds.
+ pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
+ self.handshake_timeout = handshake_timeout;
+ self
+ }
+}
+
+impl Clone for Acceptor {
+ fn clone(&self) -> Self {
+ Self {
+ config: self.config.clone(),
+ handshake_timeout: self.handshake_timeout,
+ }
+ }
+}
+
+impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Config = ();
+ type Service = AcceptorService;
+ type InitError = ();
+ type Future = FutReady<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ let res = MAX_CONN_COUNTER.with(|conns| {
+ Ok(AcceptorService {
+ acceptor: self.config.clone().into(),
+ conns: conns.clone(),
+ handshake_timeout: self.handshake_timeout,
+ })
+ });
+
+ ready(res)
+ }
+}
+
+/// Rustls based acceptor service.
+pub struct AcceptorService {
+ acceptor: TlsAcceptor,
+ conns: Counter,
+ handshake_timeout: Duration,
+}
+
+impl<IO: ActixStream> Service<IO> for AcceptorService {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Future = AcceptFut<IO>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ if self.conns.available(cx) {
+ Poll::Ready(Ok(()))
+ } else {
+ Poll::Pending
+ }
+ }
+
+ fn call(&self, req: IO) -> Self::Future {
+ AcceptFut {
+ fut: self.acceptor.accept(req),
+ timeout: sleep(self.handshake_timeout),
+ _guard: self.conns.get(),
+ }
+ }
+}
+
+pin_project! {
+ /// Accept future for Rustls service.
+ #[doc(hidden)]
+ pub struct AcceptFut<IO: ActixStream> {
+ fut: Accept<IO>,
+ #[pin]
+ timeout: Sleep,
+ _guard: CounterGuard,
+ }
+}
+
+impl<IO: ActixStream> Future for AcceptFut<IO> {
+ type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.project();
+ match Pin::new(&mut this.fut).poll(cx) {
+ Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
+ Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
+ Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +
//! `rustls` v0.22 based TLS connection acceptor service.
+//!
+//! See [`Acceptor`] for main service factory docs.
+
+use std::{
+ convert::Infallible,
+ future::Future,
+ io::{self, IoSlice},
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+ time::Duration,
+};
+
+use actix_rt::{
+ net::{ActixStream, Ready},
+ time::{sleep, Sleep},
+};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::{
+ counter::{Counter, CounterGuard},
+ future::{ready, Ready as FutReady},
+};
+use pin_project_lite::pin_project;
+use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+use tokio_rustls::{Accept, TlsAcceptor};
+use tokio_rustls_025 as tokio_rustls;
+
+use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
+
+pub mod reexports {
+ //! Re-exports from `rustls` that are useful for acceptors.
+
+ pub use tokio_rustls_025::rustls::ServerConfig;
+}
+
+/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
+pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
+
+impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
+impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
+
+impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut ReadBuf<'_>,
+ ) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
+ }
+}
+
+impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
+ fn poll_write(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_flush(cx)
+ }
+
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
+ }
+
+ fn poll_write_vectored(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ bufs: &[IoSlice<'_>],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ (**self).is_write_vectored()
+ }
+}
+
+impl<IO: ActixStream> ActixStream for TlsStream<IO> {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_read_ready((**self).get_ref().0, cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_write_ready((**self).get_ref().0, cx)
+ }
+}
+
+/// Accept TLS connections via the `rustls` crate.
+pub struct Acceptor {
+ config: Arc<reexports::ServerConfig>,
+ handshake_timeout: Duration,
+}
+
+impl Acceptor {
+ /// Constructs `rustls` based acceptor service factory.
+ pub fn new(config: reexports::ServerConfig) -> Self {
+ Acceptor {
+ config: Arc::new(config),
+ handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
+ }
+ }
+
+ /// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+ ///
+ /// Default timeout is 3 seconds.
+ pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
+ self.handshake_timeout = handshake_timeout;
+ self
+ }
+}
+
+impl Clone for Acceptor {
+ fn clone(&self) -> Self {
+ Self {
+ config: self.config.clone(),
+ handshake_timeout: self.handshake_timeout,
+ }
+ }
+}
+
+impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Config = ();
+ type Service = AcceptorService;
+ type InitError = ();
+ type Future = FutReady<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ let res = MAX_CONN_COUNTER.with(|conns| {
+ Ok(AcceptorService {
+ acceptor: self.config.clone().into(),
+ conns: conns.clone(),
+ handshake_timeout: self.handshake_timeout,
+ })
+ });
+
+ ready(res)
+ }
+}
+
+/// Rustls based acceptor service.
+pub struct AcceptorService {
+ acceptor: TlsAcceptor,
+ conns: Counter,
+ handshake_timeout: Duration,
+}
+
+impl<IO: ActixStream> Service<IO> for AcceptorService {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Future = AcceptFut<IO>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ if self.conns.available(cx) {
+ Poll::Ready(Ok(()))
+ } else {
+ Poll::Pending
+ }
+ }
+
+ fn call(&self, req: IO) -> Self::Future {
+ AcceptFut {
+ fut: self.acceptor.accept(req),
+ timeout: sleep(self.handshake_timeout),
+ _guard: self.conns.get(),
+ }
+ }
+}
+
+pin_project! {
+ /// Accept future for Rustls service.
+ #[doc(hidden)]
+ pub struct AcceptFut<IO: ActixStream> {
+ fut: Accept<IO>,
+ #[pin]
+ timeout: Sleep,
+ _guard: CounterGuard,
+ }
+}
+
+impl<IO: ActixStream> Future for AcceptFut<IO> {
+ type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.project();
+ match Pin::new(&mut this.fut).poll(cx) {
+ Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
+ Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
+ Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +
//! `rustls` v0.23 based TLS connection acceptor service.
+//!
+//! See [`Acceptor`] for main service factory docs.
+
+use std::{
+ convert::Infallible,
+ future::Future,
+ io::{self, IoSlice},
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+ time::Duration,
+};
+
+use actix_rt::{
+ net::{ActixStream, Ready},
+ time::{sleep, Sleep},
+};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::{
+ counter::{Counter, CounterGuard},
+ future::{ready, Ready as FutReady},
+};
+use pin_project_lite::pin_project;
+use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+use tokio_rustls::{Accept, TlsAcceptor};
+use tokio_rustls_026 as tokio_rustls;
+
+use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
+
+pub mod reexports {
+ //! Re-exports from `rustls` that are useful for acceptors.
+
+ pub use tokio_rustls_026::rustls::ServerConfig;
+}
+
+/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
+pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
+
+impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
+impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
+
+impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut ReadBuf<'_>,
+ ) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
+ }
+}
+
+impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
+ fn poll_write(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_flush(cx)
+ }
+
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+ Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
+ }
+
+ fn poll_write_vectored(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ bufs: &[IoSlice<'_>],
+ ) -> Poll<io::Result<usize>> {
+ Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ (**self).is_write_vectored()
+ }
+}
+
+impl<IO: ActixStream> ActixStream for TlsStream<IO> {
+ fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_read_ready((**self).get_ref().0, cx)
+ }
+
+ fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
+ IO::poll_write_ready((**self).get_ref().0, cx)
+ }
+}
+
+/// Accept TLS connections via the `rustls` crate.
+pub struct Acceptor {
+ config: Arc<reexports::ServerConfig>,
+ handshake_timeout: Duration,
+}
+
+impl Acceptor {
+ /// Constructs `rustls` based acceptor service factory.
+ pub fn new(config: reexports::ServerConfig) -> Self {
+ Acceptor {
+ config: Arc::new(config),
+ handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
+ }
+ }
+
+ /// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
+ ///
+ /// Default timeout is 3 seconds.
+ pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
+ self.handshake_timeout = handshake_timeout;
+ self
+ }
+}
+
+impl Clone for Acceptor {
+ fn clone(&self) -> Self {
+ Self {
+ config: self.config.clone(),
+ handshake_timeout: self.handshake_timeout,
+ }
+ }
+}
+
+impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Config = ();
+ type Service = AcceptorService;
+ type InitError = ();
+ type Future = FutReady<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ let res = MAX_CONN_COUNTER.with(|conns| {
+ Ok(AcceptorService {
+ acceptor: self.config.clone().into(),
+ conns: conns.clone(),
+ handshake_timeout: self.handshake_timeout,
+ })
+ });
+
+ ready(res)
+ }
+}
+
+/// Rustls based acceptor service.
+pub struct AcceptorService {
+ acceptor: TlsAcceptor,
+ conns: Counter,
+ handshake_timeout: Duration,
+}
+
+impl<IO: ActixStream> Service<IO> for AcceptorService {
+ type Response = TlsStream<IO>;
+ type Error = TlsError<io::Error, Infallible>;
+ type Future = AcceptFut<IO>;
+
+ fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ if self.conns.available(cx) {
+ Poll::Ready(Ok(()))
+ } else {
+ Poll::Pending
+ }
+ }
+
+ fn call(&self, req: IO) -> Self::Future {
+ AcceptFut {
+ fut: self.acceptor.accept(req),
+ timeout: sleep(self.handshake_timeout),
+ _guard: self.conns.get(),
+ }
+ }
+}
+
+pin_project! {
+ /// Accept future for Rustls service.
+ #[doc(hidden)]
+ pub struct AcceptFut<IO: ActixStream> {
+ fut: Accept<IO>,
+ #[pin]
+ timeout: Sleep,
+ _guard: CounterGuard,
+ }
+}
+
+impl<IO: ActixStream> Future for AcceptFut<IO> {
+ type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut this = self.project();
+ match Pin::new(&mut this.fut).poll(cx) {
+ Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
+ Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
+ Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +
use std::{
+ collections::{vec_deque, VecDeque},
+ fmt, iter,
+ net::SocketAddr,
+};
+
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub(crate) enum ConnectAddrs {
+ None,
+ One(SocketAddr),
+ // TODO: consider using smallvec
+ Multi(VecDeque<SocketAddr>),
+}
+
+impl ConnectAddrs {
+ pub(crate) fn is_unresolved(&self) -> bool {
+ matches!(self, Self::None)
+ }
+
+ pub(crate) fn is_resolved(&self) -> bool {
+ !self.is_unresolved()
+ }
+}
+
+impl Default for ConnectAddrs {
+ fn default() -> Self {
+ Self::None
+ }
+}
+
+impl From<Option<SocketAddr>> for ConnectAddrs {
+ fn from(addr: Option<SocketAddr>) -> Self {
+ match addr {
+ Some(addr) => ConnectAddrs::One(addr),
+ None => ConnectAddrs::None,
+ }
+ }
+}
+
+/// Iterator over addresses in a [`Connect`] request.
+#[derive(Clone)]
+pub(crate) enum ConnectAddrsIter<'a> {
+ None,
+ One(SocketAddr),
+ Multi(vec_deque::Iter<'a, SocketAddr>),
+ MultiOwned(vec_deque::IntoIter<SocketAddr>),
+}
+
+impl Iterator for ConnectAddrsIter<'_> {
+ type Item = SocketAddr;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match *self {
+ Self::None => None,
+ Self::One(addr) => {
+ *self = Self::None;
+ Some(addr)
+ }
+ Self::Multi(ref mut iter) => iter.next().copied(),
+ Self::MultiOwned(ref mut iter) => iter.next(),
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ match *self {
+ Self::None => (0, Some(0)),
+ Self::One(_) => (1, Some(1)),
+ Self::Multi(ref iter) => iter.size_hint(),
+ Self::MultiOwned(ref iter) => iter.size_hint(),
+ }
+ }
+}
+
+impl fmt::Debug for ConnectAddrsIter<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.clone()).finish()
+ }
+}
+
+impl iter::ExactSizeIterator for ConnectAddrsIter<'_> {}
+
+impl iter::FusedIterator for ConnectAddrsIter<'_> {}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +
use super::Host;
+
+/// Wraps underlying I/O and the connection request that initiated it.
+#[derive(Debug)]
+pub struct Connection<R, IO> {
+ pub(crate) req: R,
+ pub(crate) io: IO,
+}
+
+impl_more::impl_deref_and_mut!(<R, IO> in Connection<R, IO> => io: IO);
+
+impl<R, IO> Connection<R, IO> {
+ /// Construct new `Connection` from request and IO parts.
+ pub fn new(req: R, io: IO) -> Self {
+ Self { req, io }
+ }
+}
+
+impl<R, IO> Connection<R, IO> {
+ /// Deconstructs into IO and request parts.
+ pub fn into_parts(self) -> (IO, R) {
+ (self.io, self.req)
+ }
+
+ /// Replaces underlying IO, returning old IO and new `Connection`.
+ pub fn replace_io<IO2>(self, io: IO2) -> (IO, Connection<R, IO2>) {
+ (self.io, Connection { io, req: self.req })
+ }
+
+ /// Returns a shared reference to the underlying IO.
+ pub fn io_ref(&self) -> &IO {
+ &self.io
+ }
+
+ /// Returns a mutable reference to the underlying IO.
+ pub fn io_mut(&mut self) -> &mut IO {
+ &mut self.io
+ }
+
+ /// Returns a reference to the connection request.
+ pub fn request(&self) -> &R {
+ &self.req
+ }
+}
+
+impl<R: Host, IO> Connection<R, IO> {
+ /// Returns hostname.
+ pub fn hostname(&self) -> &str {
+ self.req.hostname()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +
use std::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use actix_rt::net::TcpStream;
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::ready;
+
+use super::{
+ error::ConnectError,
+ resolver::{Resolver, ResolverService},
+ tcp::{TcpConnector, TcpConnectorService},
+ ConnectInfo, Connection, Host,
+};
+
+/// Combined resolver and TCP connector service factory.
+///
+/// Used to create [`ConnectorService`]s which receive connection information, resolve DNS if
+/// required, and return a TCP stream.
+#[derive(Clone, Default)]
+pub struct Connector {
+ resolver: Resolver,
+}
+
+impl Connector {
+ /// Constructs new connector factory with the given resolver.
+ pub fn new(resolver: Resolver) -> Self {
+ Connector { resolver }
+ }
+
+ /// Build connector service.
+ pub fn service(&self) -> ConnectorService {
+ ConnectorService {
+ tcp: TcpConnector::default().service(),
+ resolver: self.resolver.service(),
+ }
+ }
+}
+
+impl<R: Host> ServiceFactory<ConnectInfo<R>> for Connector {
+ type Response = Connection<R, TcpStream>;
+ type Error = ConnectError;
+ type Config = ();
+ type Service = ConnectorService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(self.service())
+ }
+}
+
+/// Combined resolver and TCP connector service.
+///
+/// Service implementation receives connection information, resolves DNS if required, and returns
+/// a TCP stream.
+#[derive(Clone, Default)]
+pub struct ConnectorService {
+ tcp: TcpConnectorService,
+ resolver: ResolverService,
+}
+
+impl<R: Host> Service<ConnectInfo<R>> for ConnectorService {
+ type Response = Connection<R, TcpStream>;
+ type Error = ConnectError;
+ type Future = ConnectServiceResponse<R>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, req: ConnectInfo<R>) -> Self::Future {
+ ConnectServiceResponse {
+ fut: ConnectFut::Resolve(self.resolver.call(req)),
+ tcp: self.tcp,
+ }
+ }
+}
+
+/// Chains futures of resolve and connect steps.
+pub(crate) enum ConnectFut<R: Host> {
+ Resolve(<ResolverService as Service<ConnectInfo<R>>>::Future),
+ Connect(<TcpConnectorService as Service<ConnectInfo<R>>>::Future),
+}
+
+/// Container for the intermediate states of [`ConnectFut`].
+pub(crate) enum ConnectFutState<R: Host> {
+ Resolved(ConnectInfo<R>),
+ Connected(Connection<R, TcpStream>),
+}
+
+impl<R: Host> ConnectFut<R> {
+ fn poll_connect(
+ &mut self,
+ cx: &mut Context<'_>,
+ ) -> Poll<Result<ConnectFutState<R>, ConnectError>> {
+ match self {
+ ConnectFut::Resolve(ref mut fut) => {
+ Pin::new(fut).poll(cx).map_ok(ConnectFutState::Resolved)
+ }
+
+ ConnectFut::Connect(ref mut fut) => {
+ Pin::new(fut).poll(cx).map_ok(ConnectFutState::Connected)
+ }
+ }
+ }
+}
+
+pub struct ConnectServiceResponse<R: Host> {
+ fut: ConnectFut<R>,
+ tcp: TcpConnectorService,
+}
+
+impl<R: Host> Future for ConnectServiceResponse<R> {
+ type Output = Result<Connection<R, TcpStream>, ConnectError>;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ loop {
+ match ready!(self.fut.poll_connect(cx))? {
+ ConnectFutState::Resolved(res) => {
+ self.fut = ConnectFut::Connect(self.tcp.call(res));
+ }
+ ConnectFutState::Connected(res) => return Poll::Ready(Ok(res)),
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +
use std::{error::Error, fmt, io};
+
+/// Errors that can result from using a connector service.
+#[derive(Debug)]
+pub enum ConnectError {
+ /// Failed to resolve the hostname.
+ Resolver(Box<dyn std::error::Error>),
+
+ /// No DNS records.
+ NoRecords,
+
+ /// Invalid input.
+ InvalidInput,
+
+ /// Unresolved host name.
+ Unresolved,
+
+ /// Connection IO error.
+ Io(io::Error),
+}
+
+impl fmt::Display for ConnectError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::NoRecords => f.write_str("No DNS records found for the input"),
+ Self::InvalidInput => f.write_str("Invalid input"),
+ Self::Unresolved => {
+ f.write_str("Connector received `Connect` method with unresolved host")
+ }
+ Self::Resolver(_) => f.write_str("Failed to resolve hostname"),
+ Self::Io(_) => f.write_str("I/O error"),
+ }
+ }
+}
+
+impl Error for ConnectError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ Self::Resolver(err) => Some(&**err),
+ Self::Io(err) => Some(err),
+ Self::NoRecords | Self::InvalidInput | Self::Unresolved => None,
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +
//! The [`Host`] trait.
+
+/// An interface for types where host parts (hostname and port) can be derived.
+///
+/// The [WHATWG URL Standard] defines the terminology used for this trait and its methods.
+///
+/// ```plain
+/// +------------------------+
+/// | host |
+/// +-----------------+------+
+/// | hostname | port |
+/// | | |
+/// | sub.example.com : 8080 |
+/// +-----------------+------+
+/// ```
+///
+/// [WHATWG URL Standard]: https://url.spec.whatwg.org/
+pub trait Host: Unpin + 'static {
+ /// Extract hostname.
+ fn hostname(&self) -> &str;
+
+ /// Extract optional port.
+ fn port(&self) -> Option<u16> {
+ None
+ }
+}
+
+impl Host for String {
+ fn hostname(&self) -> &str {
+ self.split_once(':')
+ .map(|(hostname, _)| hostname)
+ .unwrap_or(self)
+ }
+
+ fn port(&self) -> Option<u16> {
+ self.split_once(':').and_then(|(_, port)| port.parse().ok())
+ }
+}
+
+impl Host for &'static str {
+ fn hostname(&self) -> &str {
+ self.split_once(':')
+ .map(|(hostname, _)| hostname)
+ .unwrap_or(self)
+ }
+
+ fn port(&self) -> Option<u16> {
+ self.split_once(':').and_then(|(_, port)| port.parse().ok())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ macro_rules! assert_connection_info_eq {
+ ($req:expr, $hostname:expr, $port:expr) => {{
+ assert_eq!($req.hostname(), $hostname);
+ assert_eq!($req.port(), $port);
+ }};
+ }
+
+ #[test]
+ fn host_parsing() {
+ assert_connection_info_eq!("example.com", "example.com", None);
+ assert_connection_info_eq!("example.com:8080", "example.com", Some(8080));
+ assert_connection_info_eq!("example:8080", "example", Some(8080));
+ assert_connection_info_eq!("example.com:false", "example.com", None);
+ assert_connection_info_eq!("example.com:false:false", "example.com", None);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +
//! Connection info struct.
+
+use std::{
+ collections::VecDeque,
+ fmt,
+ iter::{self, FromIterator as _},
+ mem,
+ net::{IpAddr, SocketAddr},
+};
+
+use super::{
+ connect_addrs::{ConnectAddrs, ConnectAddrsIter},
+ Host,
+};
+
+/// Connection request information.
+///
+/// May contain known/pre-resolved socket address(es) or a host that needs resolving with DNS.
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub struct ConnectInfo<R> {
+ pub(crate) request: R,
+ pub(crate) port: u16,
+ pub(crate) addr: ConnectAddrs,
+ pub(crate) local_addr: Option<IpAddr>,
+}
+
+impl<R: Host> ConnectInfo<R> {
+ /// Constructs new connection info using a request.
+ pub fn new(request: R) -> ConnectInfo<R> {
+ let port = request.port();
+
+ ConnectInfo {
+ request,
+ port: port.unwrap_or(0),
+ addr: ConnectAddrs::None,
+ local_addr: None,
+ }
+ }
+
+ /// Constructs new connection info from request and known socket address.
+ ///
+ /// Since socket address is known, [`Connector`](super::Connector) will skip the DNS
+ /// resolution step.
+ pub fn with_addr(request: R, addr: SocketAddr) -> ConnectInfo<R> {
+ ConnectInfo {
+ request,
+ port: 0,
+ addr: ConnectAddrs::One(addr),
+ local_addr: None,
+ }
+ }
+
+ /// Set connection port.
+ ///
+ /// If request provided a port, this will override it.
+ pub fn set_port(mut self, port: u16) -> Self {
+ self.port = port;
+ self
+ }
+
+ /// Set connection socket address.
+ pub fn set_addr(mut self, addr: impl Into<Option<SocketAddr>>) -> Self {
+ self.addr = ConnectAddrs::from(addr.into());
+ self
+ }
+
+ /// Set list of addresses.
+ pub fn set_addrs<I>(mut self, addrs: I) -> Self
+ where
+ I: IntoIterator<Item = SocketAddr>,
+ {
+ let mut addrs = VecDeque::from_iter(addrs);
+ self.addr = if addrs.len() < 2 {
+ ConnectAddrs::from(addrs.pop_front())
+ } else {
+ ConnectAddrs::Multi(addrs)
+ };
+ self
+ }
+
+ /// Set local address to connection with.
+ ///
+ /// Useful in situations where the IP address bound to a particular network interface is known.
+ /// This would make sure the socket is opened through that interface.
+ pub fn set_local_addr(mut self, addr: impl Into<IpAddr>) -> Self {
+ self.local_addr = Some(addr.into());
+ self
+ }
+
+ /// Returns a reference to the connection request.
+ pub fn request(&self) -> &R {
+ &self.request
+ }
+
+ /// Returns request hostname.
+ pub fn hostname(&self) -> &str {
+ self.request.hostname()
+ }
+
+ /// Returns request port.
+ pub fn port(&self) -> u16 {
+ self.request.port().unwrap_or(self.port)
+ }
+
+ /// Get borrowed iterator of resolved request addresses.
+ ///
+ /// # Examples
+ /// ```
+ /// # use std::net::SocketAddr;
+ /// # use actix_tls::connect::ConnectInfo;
+ /// let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
+ ///
+ /// let conn = ConnectInfo::new("localhost");
+ /// let mut addrs = conn.addrs();
+ /// assert!(addrs.next().is_none());
+ ///
+ /// let conn = ConnectInfo::with_addr("localhost", addr);
+ /// let mut addrs = conn.addrs();
+ /// assert_eq!(addrs.next().unwrap(), addr);
+ /// ```
+ #[allow(clippy::implied_bounds_in_impls)]
+ pub fn addrs(
+ &self,
+ ) -> impl Iterator<Item = SocketAddr>
+ + ExactSizeIterator
+ + iter::FusedIterator
+ + Clone
+ + fmt::Debug
+ + '_ {
+ match self.addr {
+ ConnectAddrs::None => ConnectAddrsIter::None,
+ ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr),
+ ConnectAddrs::Multi(ref addrs) => ConnectAddrsIter::Multi(addrs.iter()),
+ }
+ }
+
+ /// Take owned iterator resolved request addresses.
+ ///
+ /// # Examples
+ /// ```
+ /// # use std::net::SocketAddr;
+ /// # use actix_tls::connect::ConnectInfo;
+ /// let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
+ ///
+ /// let mut conn = ConnectInfo::new("localhost");
+ /// let mut addrs = conn.take_addrs();
+ /// assert!(addrs.next().is_none());
+ ///
+ /// let mut conn = ConnectInfo::with_addr("localhost", addr);
+ /// let mut addrs = conn.take_addrs();
+ /// assert_eq!(addrs.next().unwrap(), addr);
+ /// ```
+ #[allow(clippy::implied_bounds_in_impls)]
+ pub fn take_addrs(
+ &mut self,
+ ) -> impl Iterator<Item = SocketAddr>
+ + ExactSizeIterator
+ + iter::FusedIterator
+ + Clone
+ + fmt::Debug
+ + 'static {
+ match mem::take(&mut self.addr) {
+ ConnectAddrs::None => ConnectAddrsIter::None,
+ ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr),
+ ConnectAddrs::Multi(addrs) => ConnectAddrsIter::MultiOwned(addrs.into_iter()),
+ }
+ }
+}
+
+impl<R: Host> From<R> for ConnectInfo<R> {
+ fn from(addr: R) -> Self {
+ ConnectInfo::new(addr)
+ }
+}
+
+impl<R: Host> fmt::Display for ConnectInfo<R> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}:{}", self.hostname(), self.port())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::net::Ipv4Addr;
+
+ use super::*;
+
+ #[test]
+ fn test_addr_iter_multi() {
+ let localhost = SocketAddr::from((IpAddr::from(Ipv4Addr::LOCALHOST), 8080));
+ let unspecified = SocketAddr::from((IpAddr::from(Ipv4Addr::UNSPECIFIED), 8080));
+
+ let mut addrs = VecDeque::new();
+ addrs.push_back(localhost);
+ addrs.push_back(unspecified);
+
+ let mut iter = ConnectAddrsIter::Multi(addrs.iter());
+ assert_eq!(iter.next(), Some(localhost));
+ assert_eq!(iter.next(), Some(unspecified));
+ assert_eq!(iter.next(), None);
+
+ let mut iter = ConnectAddrsIter::MultiOwned(addrs.into_iter());
+ assert_eq!(iter.next(), Some(localhost));
+ assert_eq!(iter.next(), Some(unspecified));
+ assert_eq!(iter.next(), None);
+ }
+
+ #[test]
+ fn test_addr_iter_single() {
+ let localhost = SocketAddr::from((IpAddr::from(Ipv4Addr::LOCALHOST), 8080));
+
+ let mut iter = ConnectAddrsIter::One(localhost);
+ assert_eq!(iter.next(), Some(localhost));
+ assert_eq!(iter.next(), None);
+
+ let mut iter = ConnectAddrsIter::None;
+ assert_eq!(iter.next(), None);
+ }
+
+ #[test]
+ fn test_local_addr() {
+ let conn = ConnectInfo::new("hello").set_local_addr([127, 0, 0, 1]);
+ assert_eq!(
+ conn.local_addr.unwrap(),
+ IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
+ )
+ }
+
+ #[test]
+ fn request_ref() {
+ let conn = ConnectInfo::new("hello");
+ assert_eq!(conn.request(), &"hello")
+ }
+
+ #[test]
+ fn set_connect_addr_into_option() {
+ let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
+
+ let conn = ConnectInfo::new("hello").set_addr(None);
+ let mut addrs = conn.addrs();
+ assert!(addrs.next().is_none());
+
+ let conn = ConnectInfo::new("hello").set_addr(addr);
+ let mut addrs = conn.addrs();
+ assert_eq!(addrs.next().unwrap(), addr);
+
+ let conn = ConnectInfo::new("hello").set_addr(Some(addr));
+ let mut addrs = conn.addrs();
+ assert_eq!(addrs.next().unwrap(), addr);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +
//! TCP and TLS connector services.
+//!
+//! # Stages of the TCP connector service:
+//! 1. Resolve [`Host`] (if needed) with given [`Resolver`] and collect list of socket addresses.
+//! 1. Establish TCP connection and return [`TcpStream`].
+//!
+//! # Stages of TLS connector services:
+//! 1. Resolve DNS and establish a [`TcpStream`] with the TCP connector service.
+//! 1. Wrap the stream and perform connect handshake with remote peer.
+//! 1. Return wrapped stream type that implements `AsyncRead` and `AsyncWrite`.
+//!
+//! [`TcpStream`]: actix_rt::net::TcpStream
+
+mod connect_addrs;
+mod connection;
+mod connector;
+mod error;
+mod host;
+mod info;
+mod resolve;
+mod resolver;
+pub mod tcp;
+
+#[cfg(feature = "uri")]
+mod uri;
+
+#[cfg(feature = "openssl")]
+pub mod openssl;
+
+#[cfg(any(
+ feature = "rustls-0_20-webpki-roots",
+ feature = "rustls-0_20-native-roots",
+))]
+pub mod rustls_0_20;
+
+#[doc(hidden)]
+#[cfg(any(
+ feature = "rustls-0_20-webpki-roots",
+ feature = "rustls-0_20-native-roots",
+))]
+pub use rustls_0_20 as rustls;
+
+#[cfg(any(
+ feature = "rustls-0_21-webpki-roots",
+ feature = "rustls-0_21-native-roots",
+))]
+pub mod rustls_0_21;
+
+#[cfg(feature = "rustls-0_22")]
+pub mod rustls_0_22;
+
+#[cfg(feature = "rustls-0_23")]
+pub mod rustls_0_23;
+
+#[cfg(feature = "native-tls")]
+pub mod native_tls;
+
+pub use self::{
+ connection::Connection,
+ connector::{Connector, ConnectorService},
+ error::ConnectError,
+ host::Host,
+ info::ConnectInfo,
+ resolve::Resolve,
+ resolver::{Resolver, ResolverService},
+};
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +
//! Native-TLS based connector service.
+//!
+//! See [`TlsConnector`] for main connector service factory docs.
+
+use std::io;
+
+use actix_rt::net::ActixStream;
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::future::LocalBoxFuture;
+use tokio_native_tls::{
+ native_tls::TlsConnector as NativeTlsConnector, TlsConnector as AsyncNativeTlsConnector,
+ TlsStream as AsyncTlsStream,
+};
+use tracing::trace;
+
+use crate::connect::{Connection, Host};
+
+pub mod reexports {
+ //! Re-exports from `native-tls` and `tokio-native-tls` that are useful for connectors.
+
+ pub use tokio_native_tls::{native_tls::TlsConnector, TlsStream as AsyncTlsStream};
+}
+
+/// Connector service and factory using `native-tls`.
+#[derive(Clone)]
+pub struct TlsConnector {
+ connector: AsyncNativeTlsConnector,
+}
+
+impl TlsConnector {
+ /// Constructs new connector service from a `native-tls` connector.
+ ///
+ /// This type is it's own service factory, so it can be used in that setting, too.
+ pub fn new(connector: NativeTlsConnector) -> Self {
+ Self {
+ connector: AsyncNativeTlsConnector::from(connector),
+ }
+ }
+}
+
+impl<R: Host, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
+where
+ IO: ActixStream + 'static,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Config = ();
+ type Service = Self;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(self.clone())
+ }
+}
+
+/// The `native-tls` connector is both it's ServiceFactory and Service impl type.
+/// As the factory and service share the same type and state.
+impl<R, IO> Service<Connection<R, IO>> for TlsConnector
+where
+ R: Host,
+ IO: ActixStream + 'static,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, stream: Connection<R, IO>) -> Self::Future {
+ let (io, stream) = stream.replace_io(());
+ let connector = self.connector.clone();
+
+ Box::pin(async move {
+ trace!("TLS handshake start for: {:?}", stream.hostname());
+ connector
+ .connect(stream.hostname(), io)
+ .await
+ .map(|res| {
+ trace!("TLS handshake success: {:?}", stream.hostname());
+ stream.replace_io(res).1
+ })
+ .map_err(|e| {
+ trace!("TLS handshake error: {:?}", e);
+ io::Error::new(io::ErrorKind::Other, format!("{}", e))
+ })
+ })
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +
//! OpenSSL based connector service.
+//!
+//! See [`TlsConnector`] for main connector service factory docs.
+
+use std::{
+ future::Future,
+ io,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use actix_rt::net::ActixStream;
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::ready;
+use openssl::ssl::SslConnector;
+use tokio_openssl::SslStream as AsyncSslStream;
+use tracing::trace;
+
+use crate::connect::{Connection, Host};
+
+pub mod reexports {
+ //! Re-exports from `openssl` and `tokio-openssl` that are useful for connectors.
+
+ pub use openssl::ssl::{Error, HandshakeError, SslConnector, SslConnectorBuilder, SslMethod};
+ pub use tokio_openssl::SslStream as AsyncSslStream;
+}
+
+/// Connector service factory using `openssl`.
+pub struct TlsConnector {
+ connector: SslConnector,
+}
+
+impl TlsConnector {
+ /// Constructs new connector service factory from an `openssl` connector.
+ pub fn new(connector: SslConnector) -> Self {
+ TlsConnector { connector }
+ }
+
+ /// Constructs new connector service from an `openssl` connector.
+ pub fn service(connector: SslConnector) -> TlsConnectorService {
+ TlsConnectorService { connector }
+ }
+}
+
+impl Clone for TlsConnector {
+ fn clone(&self) -> Self {
+ Self {
+ connector: self.connector.clone(),
+ }
+ }
+}
+
+impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
+where
+ R: Host,
+ IO: ActixStream + 'static,
+{
+ type Response = Connection<R, AsyncSslStream<IO>>;
+ type Error = io::Error;
+ type Config = ();
+ type Service = TlsConnectorService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(TlsConnectorService {
+ connector: self.connector.clone(),
+ })
+ }
+}
+
+/// Connector service using `openssl`.
+pub struct TlsConnectorService {
+ connector: SslConnector,
+}
+
+impl Clone for TlsConnectorService {
+ fn clone(&self) -> Self {
+ Self {
+ connector: self.connector.clone(),
+ }
+ }
+}
+
+impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Response = Connection<R, AsyncSslStream<IO>>;
+ type Error = io::Error;
+ type Future = ConnectFut<R, IO>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, stream: Connection<R, IO>) -> Self::Future {
+ trace!("TLS handshake start for: {:?}", stream.hostname());
+
+ let (io, stream) = stream.replace_io(());
+ let host = stream.hostname();
+
+ let config = self
+ .connector
+ .configure()
+ .expect("SSL connect configuration was invalid.");
+
+ let ssl = config
+ .into_ssl(host)
+ .expect("SSL connect configuration was invalid.");
+
+ ConnectFut {
+ io: Some(AsyncSslStream::new(ssl, io).unwrap()),
+ stream: Some(stream),
+ }
+ }
+}
+
+/// Connect future for OpenSSL service.
+#[doc(hidden)]
+pub struct ConnectFut<R, IO> {
+ io: Option<AsyncSslStream<IO>>,
+ stream: Option<Connection<R, ()>>,
+}
+
+impl<R: Host, IO> Future for ConnectFut<R, IO>
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Output = Result<Connection<R, AsyncSslStream<IO>>, io::Error>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.get_mut();
+
+ match ready!(Pin::new(this.io.as_mut().unwrap()).poll_connect(cx)) {
+ Ok(_) => {
+ let stream = this.stream.take().unwrap();
+ trace!("TLS handshake success: {:?}", stream.hostname());
+ Poll::Ready(Ok(stream.replace_io(this.io.take().unwrap()).1))
+ }
+ Err(err) => {
+ trace!("TLS handshake error: {:?}", err);
+ Poll::Ready(Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("{}", err),
+ )))
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +
//! The [`Resolve`] trait.
+
+use std::{error::Error as StdError, net::SocketAddr};
+
+use futures_core::future::LocalBoxFuture;
+
+/// Custom async DNS resolvers.
+///
+/// # Examples
+/// ```
+/// use std::net::SocketAddr;
+///
+/// use actix_tls::connect::{Resolve, Resolver};
+/// use futures_util::future::LocalBoxFuture;
+///
+/// // use trust-dns async tokio resolver
+/// use trust_dns_resolver::TokioAsyncResolver;
+///
+/// struct MyResolver {
+/// trust_dns: TokioAsyncResolver,
+/// };
+///
+/// // impl Resolve trait and convert given host address str and port to SocketAddr.
+/// impl Resolve for MyResolver {
+/// fn lookup<'a>(
+/// &'a self,
+/// host: &'a str,
+/// port: u16,
+/// ) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>> {
+/// Box::pin(async move {
+/// let res = self
+/// .trust_dns
+/// .lookup_ip(host)
+/// .await?
+/// .iter()
+/// .map(|ip| SocketAddr::new(ip, port))
+/// .collect();
+/// Ok(res)
+/// })
+/// }
+/// }
+///
+/// let my_resolver = MyResolver {
+/// trust_dns: TokioAsyncResolver::tokio_from_system_conf().unwrap(),
+/// };
+///
+/// // wrap custom resolver
+/// let resolver = Resolver::custom(my_resolver);
+///
+/// // resolver can be passed to connector factory where returned service factory
+/// // can be used to construct new connector services for use in clients
+/// let factory = actix_tls::connect::Connector::new(resolver);
+/// let connector = factory.service();
+/// ```
+pub trait Resolve {
+ /// Given DNS lookup information, returns a future that completes with socket information.
+ fn lookup<'a>(
+ &'a self,
+ host: &'a str,
+ port: u16,
+ ) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn StdError>>>;
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +
use std::{
+ future::Future,
+ io,
+ net::SocketAddr,
+ pin::Pin,
+ rc::Rc,
+ task::{Context, Poll},
+ vec::IntoIter,
+};
+
+use actix_rt::task::{spawn_blocking, JoinHandle};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::{future::LocalBoxFuture, ready};
+use tracing::trace;
+
+use super::{ConnectError, ConnectInfo, Host, Resolve};
+
+/// DNS resolver service factory.
+#[derive(Clone, Default)]
+pub struct Resolver {
+ resolver: ResolverService,
+}
+
+impl Resolver {
+ /// Constructs a new resolver factory with a custom resolver.
+ pub fn custom(resolver: impl Resolve + 'static) -> Self {
+ Self {
+ resolver: ResolverService::custom(resolver),
+ }
+ }
+
+ /// Returns a new resolver service.
+ pub fn service(&self) -> ResolverService {
+ self.resolver.clone()
+ }
+}
+
+impl<R: Host> ServiceFactory<ConnectInfo<R>> for Resolver {
+ type Response = ConnectInfo<R>;
+ type Error = ConnectError;
+ type Config = ();
+ type Service = ResolverService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(self.resolver.clone())
+ }
+}
+
+#[derive(Clone)]
+enum ResolverKind {
+ /// Built-in DNS resolver.
+ ///
+ /// See [`std::net::ToSocketAddrs`] trait.
+ Default,
+
+ /// Custom, user-provided DNS resolver.
+ Custom(Rc<dyn Resolve>),
+}
+
+impl Default for ResolverKind {
+ fn default() -> Self {
+ Self::Default
+ }
+}
+
+/// DNS resolver service.
+#[derive(Clone, Default)]
+pub struct ResolverService {
+ kind: ResolverKind,
+}
+
+impl ResolverService {
+ /// Constructor for custom Resolve trait object and use it as resolver.
+ pub fn custom(resolver: impl Resolve + 'static) -> Self {
+ Self {
+ kind: ResolverKind::Custom(Rc::new(resolver)),
+ }
+ }
+
+ /// Resolve DNS with default resolver.
+ fn default_lookup<R: Host>(
+ req: &ConnectInfo<R>,
+ ) -> JoinHandle<io::Result<IntoIter<SocketAddr>>> {
+ // reconstruct host; concatenate hostname and port together
+ let host = format!("{}:{}", req.hostname(), req.port());
+
+ // run blocking DNS lookup in thread pool since DNS lookups can take upwards of seconds on
+ // some platforms if conditions are poor and OS-level cache is not populated
+ spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(&host))
+ }
+}
+
+impl<R: Host> Service<ConnectInfo<R>> for ResolverService {
+ type Response = ConnectInfo<R>;
+ type Error = ConnectError;
+ type Future = ResolverFut<R>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, req: ConnectInfo<R>) -> Self::Future {
+ if req.addr.is_resolved() {
+ // socket address(es) already resolved; return existing connection request
+ ResolverFut::Resolved(Some(req))
+ } else if let Ok(ip) = req.hostname().parse() {
+ // request hostname is valid ip address; add address to request and return
+ let addr = SocketAddr::new(ip, req.port());
+ let req = req.set_addr(Some(addr));
+ ResolverFut::Resolved(Some(req))
+ } else {
+ trace!("DNS resolver: resolving host {:?}", req.hostname());
+
+ match &self.kind {
+ ResolverKind::Default => {
+ let fut = Self::default_lookup(&req);
+ ResolverFut::LookUp(fut, Some(req))
+ }
+
+ ResolverKind::Custom(resolver) => {
+ let resolver = Rc::clone(resolver);
+
+ ResolverFut::LookupCustom(Box::pin(async move {
+ let addrs = resolver
+ .lookup(req.hostname(), req.port())
+ .await
+ .map_err(ConnectError::Resolver)?;
+
+ let req = req.set_addrs(addrs);
+
+ if req.addr.is_unresolved() {
+ Err(ConnectError::NoRecords)
+ } else {
+ Ok(req)
+ }
+ }))
+ }
+ }
+ }
+ }
+}
+
+/// Future for resolver service.
+#[doc(hidden)]
+pub enum ResolverFut<R: Host> {
+ Resolved(Option<ConnectInfo<R>>),
+ LookUp(
+ JoinHandle<io::Result<IntoIter<SocketAddr>>>,
+ Option<ConnectInfo<R>>,
+ ),
+ LookupCustom(LocalBoxFuture<'static, Result<ConnectInfo<R>, ConnectError>>),
+}
+
+impl<R: Host> Future for ResolverFut<R> {
+ type Output = Result<ConnectInfo<R>, ConnectError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.get_mut() {
+ Self::Resolved(conn) => Poll::Ready(Ok(conn
+ .take()
+ .expect("ResolverFuture polled after finished"))),
+
+ Self::LookUp(fut, req) => {
+ let res = match ready!(Pin::new(fut).poll(cx)) {
+ Ok(Ok(res)) => Ok(res),
+ Ok(Err(err)) => Err(ConnectError::Resolver(Box::new(err))),
+ Err(err) => Err(ConnectError::Io(err.into())),
+ };
+
+ let req = req.take().unwrap();
+
+ let addrs = res.map_err(|err| {
+ trace!(
+ "DNS resolver: failed to resolve host {:?} err: {:?}",
+ req.hostname(),
+ err
+ );
+
+ err
+ })?;
+
+ let req = req.set_addrs(addrs);
+
+ trace!(
+ "DNS resolver: host {:?} resolved to {:?}",
+ req.hostname(),
+ req.addrs()
+ );
+
+ if req.addr.is_unresolved() {
+ Poll::Ready(Err(ConnectError::NoRecords))
+ } else {
+ Poll::Ready(Ok(req))
+ }
+ }
+
+ Self::LookupCustom(fut) => fut.as_mut().poll(cx),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +
//! Rustls based connector service.
+//!
+//! See [`TlsConnector`] for main connector service factory docs.
+
+use std::{
+ future::Future,
+ io,
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+};
+
+use actix_rt::net::ActixStream;
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::ready;
+use tokio_rustls::{
+ client::TlsStream as AsyncTlsStream,
+ rustls::{client::ServerName, ClientConfig, RootCertStore},
+ Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
+};
+use tokio_rustls_023 as tokio_rustls;
+
+use crate::connect::{Connection, Host};
+
+pub mod reexports {
+ //! Re-exports from the `rustls` v0.20 ecosystem that are useful for connectors.
+
+ pub use tokio_rustls_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
+ #[cfg(feature = "rustls-0_20-webpki-roots")]
+ pub use webpki_roots_022::TLS_SERVER_ROOTS;
+}
+
+/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
+///
+/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
+///
+/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_06::load_native_certs()
+#[cfg(feature = "rustls-0_20-native-roots")]
+pub fn native_roots_cert_store() -> io::Result<RootCertStore> {
+ let mut root_certs = RootCertStore::empty();
+
+ for cert in rustls_native_certs_06::load_native_certs()? {
+ root_certs
+ .add(&tokio_rustls_023::rustls::Certificate(cert.0))
+ .unwrap();
+ }
+
+ Ok(root_certs)
+}
+
+/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
+#[cfg(feature = "rustls-0_20-webpki-roots")]
+pub fn webpki_roots_cert_store() -> RootCertStore {
+ use tokio_rustls_023::rustls;
+
+ let mut root_certs = RootCertStore::empty();
+
+ for cert in webpki_roots_022::TLS_SERVER_ROOTS.0 {
+ let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
+ cert.subject,
+ cert.spki,
+ cert.name_constraints,
+ );
+ let certs = vec![cert].into_iter();
+ root_certs.add_server_trust_anchors(certs);
+ }
+
+ root_certs
+}
+
+/// Connector service factory using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnector {
+ connector: Arc<ClientConfig>,
+}
+
+impl TlsConnector {
+ /// Constructs new connector service factory from a `rustls` client configuration.
+ pub fn new(connector: Arc<ClientConfig>) -> Self {
+ TlsConnector { connector }
+ }
+
+ /// Constructs new connector service from a `rustls` client configuration.
+ pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
+ TlsConnectorService { connector }
+ }
+}
+
+impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
+where
+ R: Host,
+ IO: ActixStream + 'static,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Config = ();
+ type Service = TlsConnectorService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(TlsConnectorService {
+ connector: self.connector.clone(),
+ })
+ }
+}
+
+/// Connector service using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnectorService {
+ connector: Arc<ClientConfig>,
+}
+
+impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Future = ConnectFut<R, IO>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, connection: Connection<R, IO>) -> Self::Future {
+ tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
+ let (stream, connection) = connection.replace_io(());
+
+ match ServerName::try_from(connection.hostname()) {
+ Ok(host) => ConnectFut::Future {
+ connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
+ .connect(host, stream),
+ connection: Some(connection),
+ },
+ Err(_) => ConnectFut::InvalidDns,
+ }
+ }
+}
+
+/// Connect future for Rustls service.
+#[doc(hidden)]
+#[allow(clippy::large_enum_variant)]
+pub enum ConnectFut<R, IO> {
+ /// See issue <https://github.com/briansmith/webpki/issues/54>
+ InvalidDns,
+ Future {
+ connect: RustlsConnect<IO>,
+ connection: Option<Connection<R, ()>>,
+ },
+}
+
+impl<R, IO> Future for ConnectFut<R, IO>
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.get_mut() {
+ Self::InvalidDns => Poll::Ready(Err(io::Error::new(
+ io::ErrorKind::Other,
+ "Rustls v0.20 can only handle hostname-based connections. Enable the `rustls-0_21` \
+ feature and use the Rustls v0.21 utilities to gain this feature.",
+ ))),
+
+ Self::Future {
+ connect,
+ connection,
+ } => {
+ let stream = ready!(Pin::new(connect).poll(cx))?;
+ let connection = connection.take().unwrap();
+ tracing::trace!("TLS handshake success: {:?}", connection.hostname());
+ Poll::Ready(Ok(connection.replace_io(stream).1))
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +
//! Rustls based connector service.
+//!
+//! See [`TlsConnector`] for main connector service factory docs.
+
+use std::{
+ future::Future,
+ io,
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+};
+
+use actix_rt::net::ActixStream;
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::ready;
+use tokio_rustls::{
+ client::TlsStream as AsyncTlsStream,
+ rustls::{client::ServerName, ClientConfig, RootCertStore},
+ Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
+};
+use tokio_rustls_024 as tokio_rustls;
+
+use crate::connect::{Connection, Host};
+
+pub mod reexports {
+ //! Re-exports from the `rustls` v0.21 ecosystem that are useful for connectors.
+
+ pub use tokio_rustls_024::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
+ #[cfg(feature = "rustls-0_21-webpki-roots")]
+ pub use webpki_roots_025::TLS_SERVER_ROOTS;
+}
+
+/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
+///
+/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
+///
+/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_06::load_native_certs()
+#[cfg(feature = "rustls-0_21-native-roots")]
+pub fn native_roots_cert_store() -> io::Result<RootCertStore> {
+ let mut root_certs = RootCertStore::empty();
+
+ for cert in rustls_native_certs_06::load_native_certs()? {
+ root_certs
+ .add(&tokio_rustls_024::rustls::Certificate(cert.0))
+ .unwrap();
+ }
+
+ Ok(root_certs)
+}
+
+/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
+#[cfg(feature = "rustls-0_21-webpki-roots")]
+pub fn webpki_roots_cert_store() -> RootCertStore {
+ use tokio_rustls_024::rustls;
+
+ let mut root_certs = RootCertStore::empty();
+
+ for cert in webpki_roots_025::TLS_SERVER_ROOTS {
+ let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
+ cert.subject,
+ cert.spki,
+ cert.name_constraints,
+ );
+ let certs = vec![cert].into_iter();
+ root_certs.add_trust_anchors(certs);
+ }
+
+ root_certs
+}
+
+/// Connector service factory using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnector {
+ connector: Arc<ClientConfig>,
+}
+
+impl TlsConnector {
+ /// Constructs new connector service factory from a `rustls` client configuration.
+ pub fn new(connector: Arc<ClientConfig>) -> Self {
+ TlsConnector { connector }
+ }
+
+ /// Constructs new connector service from a `rustls` client configuration.
+ pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
+ TlsConnectorService { connector }
+ }
+}
+
+impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
+where
+ R: Host,
+ IO: ActixStream + 'static,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Config = ();
+ type Service = TlsConnectorService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(TlsConnectorService {
+ connector: self.connector.clone(),
+ })
+ }
+}
+
+/// Connector service using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnectorService {
+ connector: Arc<ClientConfig>,
+}
+
+impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Future = ConnectFut<R, IO>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, connection: Connection<R, IO>) -> Self::Future {
+ tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
+ let (stream, connection) = connection.replace_io(());
+
+ match ServerName::try_from(connection.hostname()) {
+ Ok(host) => ConnectFut::Future {
+ connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
+ .connect(host, stream),
+ connection: Some(connection),
+ },
+ Err(_) => ConnectFut::InvalidServerName,
+ }
+ }
+}
+
+/// Connect future for Rustls service.
+#[doc(hidden)]
+#[allow(clippy::large_enum_variant)]
+pub enum ConnectFut<R, IO> {
+ InvalidServerName,
+ Future {
+ connect: RustlsConnect<IO>,
+ connection: Option<Connection<R, ()>>,
+ },
+}
+
+impl<R, IO> Future for ConnectFut<R, IO>
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.get_mut() {
+ Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "connection parameters specified invalid server name",
+ ))),
+
+ Self::Future {
+ connect,
+ connection,
+ } => {
+ let stream = ready!(Pin::new(connect).poll(cx))?;
+ let connection = connection.take().unwrap();
+ tracing::trace!("TLS handshake success: {:?}", connection.hostname());
+ Poll::Ready(Ok(connection.replace_io(stream).1))
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +
//! Rustls based connector service.
+//!
+//! See [`TlsConnector`] for main connector service factory docs.
+
+use std::{
+ future::Future,
+ io,
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+};
+
+use actix_rt::net::ActixStream;
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::ready;
+use rustls_pki_types_1::ServerName;
+use tokio_rustls::{
+ client::TlsStream as AsyncTlsStream, rustls::ClientConfig, Connect as RustlsConnect,
+ TlsConnector as RustlsTlsConnector,
+};
+use tokio_rustls_025 as tokio_rustls;
+
+use crate::connect::{Connection, Host};
+
+pub mod reexports {
+ //! Re-exports from the `rustls` v0.22 ecosystem that are useful for connectors.
+
+ pub use tokio_rustls_025::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
+ #[cfg(feature = "rustls-0_22-webpki-roots")]
+ pub use webpki_roots_026::TLS_SERVER_ROOTS;
+}
+
+/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
+///
+/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
+///
+/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_07::load_native_certs()
+#[cfg(feature = "rustls-0_22-native-roots")]
+pub fn native_roots_cert_store() -> io::Result<tokio_rustls::rustls::RootCertStore> {
+ let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
+
+ for cert in rustls_native_certs_07::load_native_certs()? {
+ root_certs.add(cert).unwrap();
+ }
+
+ Ok(root_certs)
+}
+
+/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
+#[cfg(feature = "rustls-0_22-webpki-roots")]
+pub fn webpki_roots_cert_store() -> tokio_rustls::rustls::RootCertStore {
+ let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
+ root_certs.extend(webpki_roots_026::TLS_SERVER_ROOTS.to_owned());
+ root_certs
+}
+
+/// Connector service factory using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnector {
+ connector: Arc<ClientConfig>,
+}
+
+impl TlsConnector {
+ /// Constructs new connector service factory from a `rustls` client configuration.
+ pub fn new(connector: Arc<ClientConfig>) -> Self {
+ TlsConnector { connector }
+ }
+
+ /// Constructs new connector service from a `rustls` client configuration.
+ pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
+ TlsConnectorService { connector }
+ }
+}
+
+impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
+where
+ R: Host,
+ IO: ActixStream + 'static,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Config = ();
+ type Service = TlsConnectorService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(TlsConnectorService {
+ connector: self.connector.clone(),
+ })
+ }
+}
+
+/// Connector service using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnectorService {
+ connector: Arc<ClientConfig>,
+}
+
+impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Future = ConnectFut<R, IO>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, connection: Connection<R, IO>) -> Self::Future {
+ tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
+ let (stream, conn) = connection.replace_io(());
+
+ match ServerName::try_from(conn.hostname()) {
+ Ok(host) => ConnectFut::Future {
+ connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
+ .connect(host.to_owned(), stream),
+ connection: Some(conn),
+ },
+ Err(_) => ConnectFut::InvalidServerName,
+ }
+ }
+}
+
+/// Connect future for Rustls service.
+#[doc(hidden)]
+#[allow(clippy::large_enum_variant)]
+pub enum ConnectFut<R, IO> {
+ InvalidServerName,
+ Future {
+ connect: RustlsConnect<IO>,
+ connection: Option<Connection<R, ()>>,
+ },
+}
+
+impl<R, IO> Future for ConnectFut<R, IO>
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.get_mut() {
+ Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "connection parameters specified invalid server name",
+ ))),
+
+ Self::Future {
+ connect,
+ connection,
+ } => {
+ let stream = ready!(Pin::new(connect).poll(cx))?;
+ let connection = connection.take().unwrap();
+ tracing::trace!("TLS handshake success: {:?}", connection.hostname());
+ Poll::Ready(Ok(connection.replace_io(stream).1))
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +
//! Rustls based connector service.
+//!
+//! See [`TlsConnector`] for main connector service factory docs.
+
+use std::{
+ future::Future,
+ io,
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+};
+
+use actix_rt::net::ActixStream;
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::ready;
+use rustls_pki_types_1::ServerName;
+use tokio_rustls::{
+ client::TlsStream as AsyncTlsStream, rustls::ClientConfig, Connect as RustlsConnect,
+ TlsConnector as RustlsTlsConnector,
+};
+use tokio_rustls_026 as tokio_rustls;
+
+use crate::connect::{Connection, Host};
+
+pub mod reexports {
+ //! Re-exports from the `rustls` v0.23 ecosystem that are useful for connectors.
+
+ pub use tokio_rustls_026::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
+ #[cfg(feature = "rustls-0_23-webpki-roots")]
+ pub use webpki_roots_026::TLS_SERVER_ROOTS;
+}
+
+/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
+///
+/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
+///
+/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_07::load_native_certs()
+#[cfg(feature = "rustls-0_23-native-roots")]
+pub fn native_roots_cert_store() -> io::Result<tokio_rustls::rustls::RootCertStore> {
+ let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
+
+ for cert in rustls_native_certs_07::load_native_certs()? {
+ root_certs.add(cert).unwrap();
+ }
+
+ Ok(root_certs)
+}
+
+/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
+#[cfg(feature = "rustls-0_23-webpki-roots")]
+pub fn webpki_roots_cert_store() -> tokio_rustls::rustls::RootCertStore {
+ let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
+ root_certs.extend(webpki_roots_026::TLS_SERVER_ROOTS.to_owned());
+ root_certs
+}
+
+/// Connector service factory using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnector {
+ connector: Arc<ClientConfig>,
+}
+
+impl TlsConnector {
+ /// Constructs new connector service factory from a `rustls` client configuration.
+ pub fn new(connector: Arc<ClientConfig>) -> Self {
+ TlsConnector { connector }
+ }
+
+ /// Constructs new connector service from a `rustls` client configuration.
+ pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
+ TlsConnectorService { connector }
+ }
+}
+
+impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
+where
+ R: Host,
+ IO: ActixStream + 'static,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Config = ();
+ type Service = TlsConnectorService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(TlsConnectorService {
+ connector: self.connector.clone(),
+ })
+ }
+}
+
+/// Connector service using `rustls`.
+#[derive(Clone)]
+pub struct TlsConnectorService {
+ connector: Arc<ClientConfig>,
+}
+
+impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Response = Connection<R, AsyncTlsStream<IO>>;
+ type Error = io::Error;
+ type Future = ConnectFut<R, IO>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, connection: Connection<R, IO>) -> Self::Future {
+ tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
+ let (stream, conn) = connection.replace_io(());
+
+ match ServerName::try_from(conn.hostname()) {
+ Ok(host) => ConnectFut::Future {
+ connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
+ .connect(host.to_owned(), stream),
+ connection: Some(conn),
+ },
+ Err(_) => ConnectFut::InvalidServerName,
+ }
+ }
+}
+
+/// Connect future for Rustls service.
+#[doc(hidden)]
+#[allow(clippy::large_enum_variant)]
+pub enum ConnectFut<R, IO> {
+ InvalidServerName,
+ Future {
+ connect: RustlsConnect<IO>,
+ connection: Option<Connection<R, ()>>,
+ },
+}
+
+impl<R, IO> Future for ConnectFut<R, IO>
+where
+ R: Host,
+ IO: ActixStream,
+{
+ type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.get_mut() {
+ Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "connection parameters specified invalid server name",
+ ))),
+
+ Self::Future {
+ connect,
+ connection,
+ } => {
+ let stream = ready!(Pin::new(connect).poll(cx))?;
+ let connection = connection.take().unwrap();
+ tracing::trace!("TLS handshake success: {:?}", connection.hostname());
+ Poll::Ready(Ok(connection.replace_io(stream).1))
+ }
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +
//! TCP connector service.
+//!
+//! See [`TcpConnector`] for main connector service factory docs.
+
+use std::{
+ collections::VecDeque,
+ future::Future,
+ io,
+ net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6},
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use actix_rt::net::{TcpSocket, TcpStream};
+use actix_service::{Service, ServiceFactory};
+use actix_utils::future::{ok, Ready};
+use futures_core::ready;
+use tokio_util::sync::ReusableBoxFuture;
+use tracing::{error, trace};
+
+use super::{connect_addrs::ConnectAddrs, error::ConnectError, ConnectInfo, Connection, Host};
+
+/// TCP connector service factory.
+#[derive(Debug, Clone, Copy, Default)]
+#[non_exhaustive]
+pub struct TcpConnector;
+
+impl TcpConnector {
+ /// Returns a new TCP connector service.
+ pub fn service(&self) -> TcpConnectorService {
+ TcpConnectorService::default()
+ }
+}
+
+impl<R: Host> ServiceFactory<ConnectInfo<R>> for TcpConnector {
+ type Response = Connection<R, TcpStream>;
+ type Error = ConnectError;
+ type Config = ();
+ type Service = TcpConnectorService;
+ type InitError = ();
+ type Future = Ready<Result<Self::Service, Self::InitError>>;
+
+ fn new_service(&self, _: ()) -> Self::Future {
+ ok(self.service())
+ }
+}
+
+/// TCP connector service.
+#[derive(Debug, Copy, Clone, Default)]
+#[non_exhaustive]
+pub struct TcpConnectorService;
+
+impl<R: Host> Service<ConnectInfo<R>> for TcpConnectorService {
+ type Response = Connection<R, TcpStream>;
+ type Error = ConnectError;
+ type Future = TcpConnectorFut<R>;
+
+ actix_service::always_ready!();
+
+ fn call(&self, req: ConnectInfo<R>) -> Self::Future {
+ let port = req.port();
+
+ let ConnectInfo {
+ request: req,
+ addr,
+ local_addr,
+ ..
+ } = req;
+
+ TcpConnectorFut::new(req, port, local_addr, addr)
+ }
+}
+
+/// Connect future for TCP service.
+#[doc(hidden)]
+pub enum TcpConnectorFut<R> {
+ Response {
+ req: Option<R>,
+ port: u16,
+ local_addr: Option<IpAddr>,
+ addrs: Option<VecDeque<SocketAddr>>,
+ stream: ReusableBoxFuture<'static, Result<TcpStream, io::Error>>,
+ },
+
+ Error(Option<ConnectError>),
+}
+
+impl<R: Host> TcpConnectorFut<R> {
+ pub(crate) fn new(
+ req: R,
+ port: u16,
+ local_addr: Option<IpAddr>,
+ addr: ConnectAddrs,
+ ) -> TcpConnectorFut<R> {
+ if addr.is_unresolved() {
+ error!("TCP connector: unresolved connection address");
+ return TcpConnectorFut::Error(Some(ConnectError::Unresolved));
+ }
+
+ trace!(
+ "TCP connector: connecting to {} on port {}",
+ req.hostname(),
+ port
+ );
+
+ match addr {
+ ConnectAddrs::None => unreachable!("none variant already checked"),
+
+ ConnectAddrs::One(addr) => TcpConnectorFut::Response {
+ req: Some(req),
+ port,
+ local_addr,
+ addrs: None,
+ stream: ReusableBoxFuture::new(connect(addr, local_addr)),
+ },
+
+ // When resolver returns multiple socket addr for request they would be popped from
+ // front end of queue and returns with the first successful TCP connection.
+ ConnectAddrs::Multi(mut addrs) => {
+ let addr = addrs.pop_front().unwrap();
+
+ TcpConnectorFut::Response {
+ req: Some(req),
+ port,
+ local_addr,
+ addrs: Some(addrs),
+ stream: ReusableBoxFuture::new(connect(addr, local_addr)),
+ }
+ }
+ }
+ }
+}
+
+impl<R: Host> Future for TcpConnectorFut<R> {
+ type Output = Result<Connection<R, TcpStream>, ConnectError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.get_mut() {
+ TcpConnectorFut::Error(err) => Poll::Ready(Err(err.take().unwrap())),
+
+ TcpConnectorFut::Response {
+ req,
+ port,
+ local_addr,
+ addrs,
+ stream,
+ } => loop {
+ match ready!(stream.poll(cx)) {
+ Ok(sock) => {
+ let req = req.take().unwrap();
+
+ trace!(
+ "TCP connector: successfully connected to {:?} - {:?}",
+ req.hostname(),
+ sock.peer_addr()
+ );
+
+ return Poll::Ready(Ok(Connection::new(req, sock)));
+ }
+
+ Err(err) => {
+ trace!(
+ "TCP connector: failed to connect to {:?} port: {}",
+ req.as_ref().unwrap().hostname(),
+ port,
+ );
+
+ if let Some(addr) = addrs.as_mut().and_then(|addrs| addrs.pop_front()) {
+ stream.set(connect(addr, *local_addr));
+ } else {
+ return Poll::Ready(Err(ConnectError::Io(err)));
+ }
+ }
+ }
+ },
+ }
+ }
+}
+
+async fn connect(addr: SocketAddr, local_addr: Option<IpAddr>) -> io::Result<TcpStream> {
+ // use local addr if connect asks for it
+ match local_addr {
+ Some(ip_addr) => {
+ let socket = match ip_addr {
+ IpAddr::V4(ip_addr) => {
+ let socket = TcpSocket::new_v4()?;
+ let addr = SocketAddr::V4(SocketAddrV4::new(ip_addr, 0));
+ socket.bind(addr)?;
+ socket
+ }
+ IpAddr::V6(ip_addr) => {
+ let socket = TcpSocket::new_v6()?;
+ let addr = SocketAddr::V6(SocketAddrV6::new(ip_addr, 0, 0, 0));
+ socket.bind(addr)?;
+ socket
+ }
+ };
+
+ socket.connect(addr).await
+ }
+
+ None => TcpStream::connect(addr).await,
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +
use super::Host;
+
+impl Host for http_0_2::Uri {
+ fn hostname(&self) -> &str {
+ self.host().unwrap_or("")
+ }
+
+ fn port(&self) -> Option<u16> {
+ match self.port_u16() {
+ Some(port) => Some(port),
+ None => scheme_to_port(self.scheme_str()),
+ }
+ }
+}
+
+impl Host for http_1::Uri {
+ fn hostname(&self) -> &str {
+ self.host().unwrap_or("")
+ }
+
+ fn port(&self) -> Option<u16> {
+ match self.port_u16() {
+ Some(port) => Some(port),
+ None => scheme_to_port(self.scheme_str()),
+ }
+ }
+}
+
+// Get port from well-known URL schemes.
+fn scheme_to_port(scheme: Option<&str>) -> Option<u16> {
+ match scheme {
+ // HTTP
+ Some("http") => Some(80),
+ Some("https") => Some(443),
+
+ // WebSockets
+ Some("ws") => Some(80),
+ Some("wss") => Some(443),
+
+ // Advanced Message Queuing Protocol (AMQP)
+ Some("amqp") => Some(5672),
+ Some("amqps") => Some(5671),
+
+ // Message Queuing Telemetry Transport (MQTT)
+ Some("mqtt") => Some(1883),
+ Some("mqtts") => Some(8883),
+
+ // File Transfer Protocol (FTP)
+ Some("ftp") => Some(21),
+ Some("ftps") => Some(990),
+
+ // Redis
+ Some("redis") => Some(6379),
+
+ // MySQL
+ Some("mysql") => Some(3306),
+
+ // PostgreSQL
+ Some("postgres") => Some(5432),
+
+ _ => None,
+ }
+}
+
//! TLS acceptor and connector services for the Actix ecosystem.
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible, missing_docs)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+
+#[cfg(feature = "openssl")]
+#[allow(unused_extern_crates)]
+extern crate tls_openssl as openssl;
+
+#[cfg(feature = "accept")]
+pub mod accept;
+
+#[cfg(feature = "connect")]
+pub mod connect;
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +
//! Actix tracing - support for tokio tracing with Actix services.
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+
+use core::marker::PhantomData;
+
+use actix_service::{
+ apply, ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform,
+};
+use actix_utils::future::{ok, Either, Ready};
+use tracing_futures::{Instrument, Instrumented};
+
+/// A `Service` implementation that automatically enters/exits tracing spans
+/// for the wrapped inner service.
+#[derive(Clone)]
+pub struct TracingService<S, F> {
+ inner: S,
+ make_span: F,
+}
+
+impl<S, F> TracingService<S, F> {
+ pub fn new(inner: S, make_span: F) -> Self {
+ TracingService { inner, make_span }
+ }
+}
+
+impl<S, Req, F> Service<Req> for TracingService<S, F>
+where
+ S: Service<Req>,
+ F: Fn(&Req) -> Option<tracing::Span>,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = Either<S::Future, Instrumented<S::Future>>;
+
+ actix_service::forward_ready!(inner);
+
+ fn call(&self, req: Req) -> Self::Future {
+ let span = (self.make_span)(&req);
+ let _enter = span.as_ref().map(|s| s.enter());
+
+ let fut = self.inner.call(req);
+
+ // make a child span to track the future's execution
+ if let Some(span) = span
+ .clone()
+ .map(|span| tracing::span!(parent: &span, tracing::Level::INFO, "future"))
+ {
+ Either::right(fut.instrument(span))
+ } else {
+ Either::left(fut)
+ }
+ }
+}
+
+/// A `Transform` implementation that wraps services with a [`TracingService`].
+pub struct TracingTransform<S, U, F> {
+ make_span: F,
+ _p: PhantomData<fn(S, U)>,
+}
+
+impl<S, U, F> TracingTransform<S, U, F> {
+ pub fn new(make_span: F) -> Self {
+ TracingTransform {
+ make_span,
+ _p: PhantomData,
+ }
+ }
+}
+
+impl<S, Req, U, F> Transform<S, Req> for TracingTransform<S, U, F>
+where
+ S: Service<Req>,
+ U: ServiceFactory<Req, Response = S::Response, Error = S::Error, Service = S>,
+ F: Fn(&Req) -> Option<tracing::Span> + Clone,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Transform = TracingService<S, F>;
+ type InitError = U::InitError;
+ type Future = Ready<Result<Self::Transform, Self::InitError>>;
+
+ fn new_transform(&self, service: S) -> Self::Future {
+ ok(TracingService::new(service, self.make_span.clone()))
+ }
+}
+
+/// Wraps the provided service factory with a transform that automatically
+/// enters/exits the given span.
+///
+/// The span to be entered/exited can be provided via a closure. The closure
+/// is passed in a reference to the request being handled by the service.
+///
+/// For example:
+/// ```ignore
+/// let traced_service = trace(
+/// web_service,
+/// |req: &Request| Some(span!(Level::INFO, "request", req.id))
+/// );
+/// ```
+pub fn trace<S, Req, I, F>(
+ service_factory: I,
+ make_span: F,
+) -> ApplyTransform<TracingTransform<S::Service, S, F>, S, Req>
+where
+ I: IntoServiceFactory<S, Req>,
+ S: ServiceFactory<Req>,
+ F: Fn(&Req) -> Option<tracing::Span> + Clone,
+{
+ apply(
+ TracingTransform::new(make_span),
+ service_factory.into_factory(),
+ )
+}
+
+#[cfg(test)]
+mod test {
+ use std::{
+ cell::RefCell,
+ collections::{BTreeMap, BTreeSet},
+ sync::{Arc, RwLock},
+ };
+
+ use actix_service::{fn_factory, fn_service};
+ use slab::Slab;
+ use tracing::{span, Event, Level, Metadata, Subscriber};
+
+ use super::*;
+
+ thread_local! {
+ static SPAN: RefCell<Vec<span::Id>> = const { RefCell::new(Vec::new()) };
+ }
+
+ #[derive(Default)]
+ struct Stats {
+ entered_spans: BTreeSet<u64>,
+ exited_spans: BTreeSet<u64>,
+ events_count: BTreeMap<u64, usize>,
+ }
+
+ #[derive(Default)]
+ struct Inner {
+ spans: Slab<&'static Metadata<'static>>,
+ stats: Stats,
+ }
+
+ #[derive(Clone, Default)]
+ struct TestSubscriber {
+ inner: Arc<RwLock<Inner>>,
+ }
+
+ impl Subscriber for TestSubscriber {
+ fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
+ true
+ }
+
+ fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
+ let id = self.inner.write().unwrap().spans.insert(span.metadata());
+ span::Id::from_u64(id as u64 + 1)
+ }
+
+ fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {}
+
+ fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {}
+
+ fn event(&self, event: &Event<'_>) {
+ let id = event
+ .parent()
+ .cloned()
+ .or_else(|| SPAN.with(|current_span| current_span.borrow().last().cloned()))
+ .unwrap();
+
+ *self
+ .inner
+ .write()
+ .unwrap()
+ .stats
+ .events_count
+ .entry(id.into_u64())
+ .or_insert(0) += 1;
+ }
+
+ fn enter(&self, span: &span::Id) {
+ self.inner
+ .write()
+ .unwrap()
+ .stats
+ .entered_spans
+ .insert(span.into_u64());
+
+ SPAN.with(|current_span| {
+ current_span.borrow_mut().push(span.clone());
+ });
+ }
+
+ fn exit(&self, span: &span::Id) {
+ self.inner
+ .write()
+ .unwrap()
+ .stats
+ .exited_spans
+ .insert(span.into_u64());
+
+ // we are guaranteed that on any given thread, spans are exited in reverse order
+ SPAN.with(|current_span| {
+ let leaving = current_span
+ .borrow_mut()
+ .pop()
+ .expect("told to exit span when not in span");
+ assert_eq!(
+ &leaving, span,
+ "told to exit span that was not most recently entered"
+ );
+ });
+ }
+ }
+
+ #[actix_rt::test]
+ async fn service_call() {
+ let service_factory = fn_factory(|| {
+ ok::<_, ()>(fn_service(|req: &'static str| {
+ tracing::event!(Level::TRACE, "It's happening - {}!", req);
+ ok::<_, ()>(())
+ }))
+ });
+
+ let subscriber = TestSubscriber::default();
+ let _guard = tracing::subscriber::set_default(subscriber.clone());
+
+ let span_svc = span!(Level::TRACE, "span_svc");
+ let trace_service_factory = trace(service_factory, |_: &&str| Some(span_svc.clone()));
+ let service = trace_service_factory.new_service(()).await.unwrap();
+ service.call("boo").await.unwrap();
+
+ let id = span_svc.id().unwrap().into_u64();
+ assert!(subscriber
+ .inner
+ .read()
+ .unwrap()
+ .stats
+ .entered_spans
+ .contains(&id));
+ assert!(subscriber
+ .inner
+ .read()
+ .unwrap()
+ .stats
+ .exited_spans
+ .contains(&id));
+ assert_eq!(subscriber.inner.read().unwrap().stats.events_count[&id], 1);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +
//! Task-notifying counter.
+
+use core::{cell::Cell, fmt, task};
+use std::rc::Rc;
+
+use local_waker::LocalWaker;
+
+/// Simple counter with ability to notify task on reaching specific number
+///
+/// Counter could be cloned, total n-count is shared across all clones.
+#[derive(Debug, Clone)]
+pub struct Counter(Rc<CounterInner>);
+
+impl Counter {
+ /// Create `Counter` instance with max value.
+ pub fn new(capacity: usize) -> Self {
+ Counter(Rc::new(CounterInner {
+ capacity,
+ count: Cell::new(0),
+ task: LocalWaker::new(),
+ }))
+ }
+
+ /// Create new counter guard, incrementing the counter.
+ #[inline]
+ pub fn get(&self) -> CounterGuard {
+ CounterGuard::new(self.0.clone())
+ }
+
+ /// Returns true if counter is below capacity. Otherwise, register to wake task when it is.
+ #[inline]
+ pub fn available(&self, cx: &task::Context<'_>) -> bool {
+ self.0.available(cx)
+ }
+
+ /// Get total number of acquired guards.
+ #[inline]
+ pub fn total(&self) -> usize {
+ self.0.count.get()
+ }
+}
+
+struct CounterInner {
+ count: Cell<usize>,
+ capacity: usize,
+ task: LocalWaker,
+}
+
+impl CounterInner {
+ fn inc(&self) {
+ self.count.set(self.count.get() + 1);
+ }
+
+ fn dec(&self) {
+ let num = self.count.get();
+ self.count.set(num - 1);
+ if num == self.capacity {
+ self.task.wake();
+ }
+ }
+
+ fn available(&self, cx: &task::Context<'_>) -> bool {
+ if self.count.get() < self.capacity {
+ true
+ } else {
+ self.task.register(cx.waker());
+ false
+ }
+ }
+}
+
+impl fmt::Debug for CounterInner {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Counter")
+ .field("count", &self.count.get())
+ .field("capacity", &self.capacity)
+ .field("task", &self.task)
+ .finish()
+ }
+}
+
+/// An RAII structure that keeps the underlying counter incremented until this guard is dropped.
+#[derive(Debug)]
+pub struct CounterGuard(Rc<CounterInner>);
+
+impl CounterGuard {
+ fn new(inner: Rc<CounterInner>) -> Self {
+ inner.inc();
+ CounterGuard(inner)
+ }
+}
+
+impl Unpin for CounterGuard {}
+
+impl Drop for CounterGuard {
+ fn drop(&mut self) {
+ self.0.dec();
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +
//! A symmetric either future.
+
+use core::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+use pin_project_lite::pin_project;
+
+pin_project! {
+ /// Combines two different futures that have the same output type.
+ ///
+ /// Construct variants with [`Either::left`] and [`Either::right`].
+ ///
+ /// # Examples
+ /// ```
+ /// use actix_utils::future::{ready, Ready, Either};
+ ///
+ /// # async fn run() {
+ /// let res = Either::<_, Ready<usize>>::left(ready(42));
+ /// assert_eq!(res.await, 42);
+ ///
+ /// let res = Either::<Ready<usize>, _>::right(ready(43));
+ /// assert_eq!(res.await, 43);
+ /// # }
+ /// ```
+ #[project = EitherProj]
+ #[derive(Debug, Clone)]
+ pub enum Either<L, R> {
+ /// A value of type `L`.
+ #[allow(missing_docs)]
+ Left { #[pin] value: L },
+
+ /// A value of type `R`.
+ #[allow(missing_docs)]
+ Right { #[pin] value: R },
+ }
+}
+
+impl<L, R> Either<L, R> {
+ /// Creates new `Either` using left variant.
+ #[inline]
+ pub fn left(value: L) -> Either<L, R> {
+ Either::Left { value }
+ }
+
+ /// Creates new `Either` using right variant.
+ #[inline]
+ pub fn right(value: R) -> Either<L, R> {
+ Either::Right { value }
+ }
+}
+
+impl<T> Either<T, T> {
+ /// Unwraps into inner value when left and right have a common type.
+ #[inline]
+ pub fn into_inner(self) -> T {
+ match self {
+ Either::Left { value } => value,
+ Either::Right { value } => value,
+ }
+ }
+}
+
+impl<L, R> Future for Either<L, R>
+where
+ L: Future,
+ R: Future<Output = L::Output>,
+{
+ type Output = L::Output;
+
+ #[inline]
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.project() {
+ EitherProj::Left { value } => value.poll(cx),
+ EitherProj::Right { value } => value.poll(cx),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::future::{ready, Ready};
+
+ #[actix_rt::test]
+ async fn test_either() {
+ let res = Either::<_, Ready<usize>>::left(ready(42));
+ assert_eq!(res.await, 42);
+
+ let res = Either::<Ready<usize>, _>::right(ready(43));
+ assert_eq!(res.await, 43);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +
//! Simple "poll function" future and factory.
+
+use core::{
+ fmt,
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+/// Creates a future driven by the provided function that receives a task context.
+///
+/// # Examples
+/// ```
+/// # use std::task::Poll;
+/// # use actix_utils::future::poll_fn;
+/// # async fn test_poll_fn() {
+/// let res = poll_fn(|_| Poll::Ready(42)).await;
+/// assert_eq!(res, 42);
+///
+/// let mut i = 5;
+/// let res = poll_fn(|cx| {
+/// i -= 1;
+///
+/// if i > 0 {
+/// cx.waker().wake_by_ref();
+/// Poll::Pending
+/// } else {
+/// Poll::Ready(42)
+/// }
+/// })
+/// .await;
+/// assert_eq!(res, 42);
+/// # }
+/// # actix_rt::Runtime::new().unwrap().block_on(test_poll_fn());
+/// ```
+#[inline]
+pub fn poll_fn<F, T>(f: F) -> PollFn<F>
+where
+ F: FnMut(&mut Context<'_>) -> Poll<T>,
+{
+ PollFn { f }
+}
+
+/// Future for the [`poll_fn`] function.
+pub struct PollFn<F> {
+ f: F,
+}
+
+impl<F> fmt::Debug for PollFn<F> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("PollFn").finish()
+ }
+}
+
+impl<F, T> Future for PollFn<F>
+where
+ F: FnMut(&mut Context<'_>) -> Poll<T>,
+{
+ type Output = T;
+
+ #[inline]
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ // SAFETY: we are not moving out of the pinned field
+ // see https://github.com/rust-lang/rust/pull/102737
+ #[allow(clippy::needless_borrow)]
+ (unsafe { &mut self.get_unchecked_mut().f })(cx)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::marker::PhantomPinned;
+
+ use super::*;
+
+ static_assertions::assert_impl_all!(PollFn<()>: Unpin);
+ static_assertions::assert_not_impl_all!(PollFn<PhantomPinned>: Unpin);
+
+ #[actix_rt::test]
+ async fn test_poll_fn() {
+ let res = poll_fn(|_| Poll::Ready(42)).await;
+ assert_eq!(res, 42);
+
+ let mut i = 5;
+ let res = poll_fn(|cx| {
+ i -= 1;
+
+ if i > 0 {
+ cx.waker().wake_by_ref();
+ Poll::Pending
+ } else {
+ Poll::Ready(42)
+ }
+ })
+ .await;
+ assert_eq!(res, 42);
+ }
+
+ // following soundness tests taken from https://github.com/tokio-rs/tokio/pull/5087
+
+ #[allow(dead_code)]
+ fn require_send<T: Send>(_t: &T) {}
+ #[allow(dead_code)]
+ fn require_sync<T: Sync>(_t: &T) {}
+
+ #[allow(unused)]
+ trait AmbiguousIfUnpin<A> {
+ fn some_item(&self) {}
+ }
+ impl<T: ?Sized> AmbiguousIfUnpin<()> for T {}
+ impl<T: ?Sized + Unpin> AmbiguousIfUnpin<[u8; 0]> for T {}
+
+ const _: fn() = || {
+ let pinned = std::marker::PhantomPinned;
+ let f = poll_fn(move |_| {
+ // Use `pinned` to take ownership of it.
+ let _ = &pinned;
+ std::task::Poll::Pending::<()>
+ });
+ require_send(&f);
+ require_sync(&f);
+ AmbiguousIfUnpin::some_item(&f);
+ };
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +
//! When `core::future::Ready` has a `into_inner()` method, this can be deprecated.
+
+use core::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+/// Future for the [`ready`] function.
+///
+/// Panic will occur if polled more than once.
+///
+/// # Examples
+/// ```
+/// use actix_utils::future::ready;
+///
+/// // async
+/// # async fn run() {
+/// let a = ready(1);
+/// assert_eq!(a.await, 1);
+/// # }
+///
+/// // sync
+/// let a = ready(1);
+/// assert_eq!(a.into_inner(), 1);
+/// ```
+#[derive(Debug, Clone)]
+#[must_use = "futures do nothing unless you `.await` or poll them"]
+pub struct Ready<T> {
+ val: Option<T>,
+}
+
+impl<T> Ready<T> {
+ /// Unwraps the value from this immediately ready future.
+ #[inline]
+ pub fn into_inner(mut self) -> T {
+ self.val.take().unwrap()
+ }
+}
+
+impl<T> Unpin for Ready<T> {}
+
+impl<T> Future for Ready<T> {
+ type Output = T;
+
+ #[inline]
+ fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
+ let val = self.val.take().expect("Ready polled after completion");
+ Poll::Ready(val)
+ }
+}
+
+/// Creates a future that is immediately ready with a value.
+///
+/// # Examples
+/// ```no_run
+/// use actix_utils::future::ready;
+///
+/// # async fn run() {
+/// let a = ready(1);
+/// assert_eq!(a.await, 1);
+/// # }
+///
+/// // sync
+/// let a = ready(1);
+/// assert_eq!(a.into_inner(), 1);
+/// ```
+#[inline]
+pub fn ready<T>(val: T) -> Ready<T> {
+ Ready { val: Some(val) }
+}
+
+/// Creates a future that is immediately ready with a success value.
+///
+/// # Examples
+/// ```no_run
+/// use actix_utils::future::ok;
+///
+/// # async fn run() {
+/// let a = ok::<_, ()>(1);
+/// assert_eq!(a.await, Ok(1));
+/// # }
+/// ```
+#[inline]
+pub fn ok<T, E>(val: T) -> Ready<Result<T, E>> {
+ Ready { val: Some(Ok(val)) }
+}
+
+/// Creates a future that is immediately ready with an error value.
+///
+/// # Examples
+/// ```no_run
+/// use actix_utils::future::err;
+///
+/// # async fn run() {
+/// let a = err::<(), _>(1);
+/// assert_eq!(a.await, Err(1));
+/// # }
+/// ```
+#[inline]
+pub fn err<T, E>(err: E) -> Ready<Result<T, E>> {
+ Ready {
+ val: Some(Err(err)),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::rc::Rc;
+
+ use futures_util::task::noop_waker;
+ use static_assertions::{assert_impl_all, assert_not_impl_any};
+
+ use super::*;
+
+ assert_impl_all!(Ready<()>: Send, Sync, Unpin, Clone);
+ assert_impl_all!(Ready<Rc<()>>: Unpin, Clone);
+ assert_not_impl_any!(Ready<Rc<()>>: Send, Sync);
+
+ #[test]
+ #[should_panic]
+ fn multiple_poll_panics() {
+ let waker = noop_waker();
+ let mut cx = Context::from_waker(&waker);
+
+ let mut ready = ready(1);
+ assert_eq!(Pin::new(&mut ready).poll(&mut cx), Poll::Ready(1));
+
+ // panic!
+ let _ = Pin::new(&mut ready).poll(&mut cx);
+ }
+}
+
//! Various utilities used in the Actix ecosystem.
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible, missing_docs)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+
+pub mod counter;
+pub mod future;
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +
//! A UTF-8 encoded read-only string using `Bytes` as storage.
+//!
+//! See docs for [`ByteString`].
+
+#![no_std]
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible, missing_docs)]
+
+extern crate alloc;
+
+use alloc::{
+ boxed::Box,
+ string::{String, ToString},
+ vec::Vec,
+};
+use core::{borrow::Borrow, fmt, hash, ops, str};
+
+use bytes::Bytes;
+
+/// An immutable UTF-8 encoded string with [`Bytes`] as a storage.
+#[derive(Clone, Default, Eq, PartialOrd, Ord)]
+pub struct ByteString(Bytes);
+
+impl ByteString {
+ /// Creates a new empty `ByteString`.
+ pub const fn new() -> Self {
+ ByteString(Bytes::new())
+ }
+
+ /// Get a reference to the underlying `Bytes` object.
+ pub fn as_bytes(&self) -> &Bytes {
+ &self.0
+ }
+
+ /// Unwraps this `ByteString` into the underlying `Bytes` object.
+ pub fn into_bytes(self) -> Bytes {
+ self.0
+ }
+
+ /// Creates a new `ByteString` from a `&'static str`.
+ pub const fn from_static(src: &'static str) -> ByteString {
+ Self(Bytes::from_static(src.as_bytes()))
+ }
+
+ /// Creates a new `ByteString` from a Bytes.
+ ///
+ /// # Safety
+ /// This function is unsafe because it does not check the bytes passed to it are valid UTF-8.
+ /// If this constraint is violated, it may cause memory unsafety issues with future users of
+ /// the `ByteString`, as we assume that `ByteString`s are valid UTF-8. However, the most likely
+ /// issue is that the data gets corrupted.
+ pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString {
+ Self(src)
+ }
+
+ /// Returns a new byte string that is equivalent to the given `subset`.
+ ///
+ /// When processing a `ByteString` buffer with other tools, one often gets a `&str` which is in
+ /// fact a slice of the original `ByteString`; i.e., a subset of it. This function turns that
+ /// `&str` into another `ByteString`, as if one had sliced the `ByteString` with the offsets
+ /// that correspond to `subset`.
+ ///
+ /// Corresponds to [`Bytes::slice_ref`].
+ ///
+ /// This operation is `O(1)`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `subset` is not a sub-slice of this byte string.
+ ///
+ /// Note that strings which are only subsets from an equality perspective do not uphold this
+ /// requirement; see examples.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use bytestring::ByteString;
+ /// let string = ByteString::from_static(" foo ");
+ /// let subset = string.trim();
+ /// let substring = string.slice_ref(subset);
+ /// assert_eq!(substring, "foo");
+ /// ```
+ ///
+ /// ```should_panic
+ /// # use bytestring::ByteString;
+ /// // panics because the given slice is not derived from the original byte string, despite
+ /// // being a logical subset of the string
+ /// ByteString::from_static("foo bar").slice_ref("foo");
+ /// ```
+ pub fn slice_ref(&self, subset: &str) -> Self {
+ Self(self.0.slice_ref(subset.as_bytes()))
+ }
+}
+
+impl PartialEq<str> for ByteString {
+ fn eq(&self, other: &str) -> bool {
+ &self[..] == other
+ }
+}
+
+impl<T: AsRef<str>> PartialEq<T> for ByteString {
+ fn eq(&self, other: &T) -> bool {
+ &self[..] == other.as_ref()
+ }
+}
+
+impl AsRef<ByteString> for ByteString {
+ fn as_ref(&self) -> &ByteString {
+ self
+ }
+}
+
+impl AsRef<[u8]> for ByteString {
+ fn as_ref(&self) -> &[u8] {
+ self.0.as_ref()
+ }
+}
+
+impl AsRef<str> for ByteString {
+ fn as_ref(&self) -> &str {
+ self
+ }
+}
+
+impl hash::Hash for ByteString {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ (**self).hash(state);
+ }
+}
+
+impl ops::Deref for ByteString {
+ type Target = str;
+
+ #[inline]
+ fn deref(&self) -> &str {
+ let bytes = self.0.as_ref();
+ // SAFETY: UTF-8 validity is guaranteed during construction.
+ unsafe { str::from_utf8_unchecked(bytes) }
+ }
+}
+
+impl Borrow<str> for ByteString {
+ fn borrow(&self) -> &str {
+ self
+ }
+}
+
+impl From<String> for ByteString {
+ #[inline]
+ fn from(value: String) -> Self {
+ Self(Bytes::from(value))
+ }
+}
+
+impl From<&str> for ByteString {
+ #[inline]
+ fn from(value: &str) -> Self {
+ Self(Bytes::copy_from_slice(value.as_ref()))
+ }
+}
+
+impl From<Box<str>> for ByteString {
+ #[inline]
+ fn from(value: Box<str>) -> Self {
+ Self(Bytes::from(value.into_boxed_bytes()))
+ }
+}
+
+impl From<ByteString> for String {
+ #[inline]
+ fn from(value: ByteString) -> Self {
+ value.to_string()
+ }
+}
+
+impl TryFrom<&[u8]> for ByteString {
+ type Error = str::Utf8Error;
+
+ #[inline]
+ fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+ let _ = str::from_utf8(value)?;
+ Ok(ByteString(Bytes::copy_from_slice(value)))
+ }
+}
+
+impl TryFrom<Vec<u8>> for ByteString {
+ type Error = str::Utf8Error;
+
+ #[inline]
+ fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+ let buf = String::from_utf8(value).map_err(|err| err.utf8_error())?;
+ Ok(ByteString(Bytes::from(buf)))
+ }
+}
+
+impl TryFrom<Bytes> for ByteString {
+ type Error = str::Utf8Error;
+
+ #[inline]
+ fn try_from(value: Bytes) -> Result<Self, Self::Error> {
+ let _ = str::from_utf8(value.as_ref())?;
+ Ok(ByteString(value))
+ }
+}
+
+impl TryFrom<bytes::BytesMut> for ByteString {
+ type Error = str::Utf8Error;
+
+ #[inline]
+ fn try_from(value: bytes::BytesMut) -> Result<Self, Self::Error> {
+ let _ = str::from_utf8(&value)?;
+ Ok(ByteString(value.freeze()))
+ }
+}
+
+macro_rules! array_impls {
+ ($($len:expr)+) => {
+ $(
+ impl TryFrom<[u8; $len]> for ByteString {
+ type Error = str::Utf8Error;
+
+ #[inline]
+ fn try_from(value: [u8; $len]) -> Result<Self, Self::Error> {
+ ByteString::try_from(&value[..])
+ }
+ }
+
+ impl TryFrom<&[u8; $len]> for ByteString {
+ type Error = str::Utf8Error;
+
+ #[inline]
+ fn try_from(value: &[u8; $len]) -> Result<Self, Self::Error> {
+ ByteString::try_from(&value[..])
+ }
+ }
+ )+
+ }
+}
+
+array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32);
+
+impl fmt::Debug for ByteString {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(fmt)
+ }
+}
+
+impl fmt::Display for ByteString {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(fmt)
+ }
+}
+
+#[cfg(feature = "serde")]
+mod serde {
+ use alloc::string::String;
+
+ use serde::{
+ de::{Deserialize, Deserializer},
+ ser::{Serialize, Serializer},
+ };
+
+ use super::ByteString;
+
+ impl Serialize for ByteString {
+ #[inline]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(self.as_ref())
+ }
+ }
+
+ impl<'de> Deserialize<'de> for ByteString {
+ #[inline]
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ String::deserialize(deserializer).map(ByteString::from)
+ }
+ }
+
+ #[cfg(test)]
+ mod serde_impl_tests {
+ use serde::de::DeserializeOwned;
+ use static_assertions::assert_impl_all;
+
+ use super::*;
+
+ assert_impl_all!(ByteString: Serialize, DeserializeOwned);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use alloc::{borrow::ToOwned, format, vec};
+ use core::{
+ hash::{Hash, Hasher},
+ panic::{RefUnwindSafe, UnwindSafe},
+ };
+
+ use ahash::AHasher;
+ use static_assertions::assert_impl_all;
+
+ use super::*;
+
+ assert_impl_all!(ByteString: Send, Sync, Unpin, Sized);
+ assert_impl_all!(ByteString: Clone, Default, Eq, PartialOrd, Ord);
+ assert_impl_all!(ByteString: fmt::Debug, fmt::Display);
+ assert_impl_all!(ByteString: UnwindSafe, RefUnwindSafe);
+
+ #[test]
+ fn eq() {
+ let s: ByteString = ByteString::from_static("test");
+ assert_eq!(s, "test");
+ assert_eq!(s, *"test");
+ assert_eq!(s, "test".to_owned());
+ }
+
+ #[test]
+ fn new() {
+ let _: ByteString = ByteString::new();
+ }
+
+ #[test]
+ fn as_bytes() {
+ let buf = ByteString::new();
+ assert!(buf.as_bytes().is_empty());
+
+ let buf = ByteString::from("hello");
+ assert_eq!(buf.as_bytes(), "hello");
+ }
+
+ #[test]
+ fn from_bytes_unchecked() {
+ let buf = unsafe { ByteString::from_bytes_unchecked(Bytes::new()) };
+ assert!(buf.is_empty());
+
+ let buf = unsafe { ByteString::from_bytes_unchecked(Bytes::from("hello")) };
+ assert_eq!(buf, "hello");
+ }
+
+ #[test]
+ fn as_ref() {
+ let buf = ByteString::new();
+
+ let _: &ByteString = buf.as_ref();
+ let _: &[u8] = buf.as_ref();
+ }
+
+ #[test]
+ fn borrow() {
+ let buf = ByteString::new();
+
+ let _: &str = buf.borrow();
+ }
+
+ #[test]
+ fn hash() {
+ let mut hasher1 = AHasher::default();
+ "str".hash(&mut hasher1);
+
+ let mut hasher2 = AHasher::default();
+ let s = ByteString::from_static("str");
+ s.hash(&mut hasher2);
+ assert_eq!(hasher1.finish(), hasher2.finish());
+ }
+
+ #[test]
+ fn from_string() {
+ let s: ByteString = "hello".to_owned().into();
+ assert_eq!(&s, "hello");
+ let t: &str = s.as_ref();
+ assert_eq!(t, "hello");
+ }
+
+ #[test]
+ fn from_str() {
+ let _: ByteString = "str".into();
+ let _: ByteString = "str".to_owned().into_boxed_str().into();
+ }
+
+ #[test]
+ fn to_string() {
+ let buf = ByteString::from("foo");
+ assert_eq!(String::from(buf), "foo");
+ }
+
+ #[test]
+ fn from_static_str() {
+ static _S: ByteString = ByteString::from_static("hello");
+ let _ = ByteString::from_static("str");
+ }
+
+ #[test]
+ fn try_from_slice() {
+ let _ = ByteString::try_from(b"nice bytes").unwrap();
+ }
+
+ #[test]
+ fn try_from_array() {
+ assert_eq!(
+ ByteString::try_from([b'h', b'i']).unwrap(),
+ ByteString::from_static("hi")
+ );
+ }
+
+ #[test]
+ fn try_from_vec() {
+ let _ = ByteString::try_from(vec![b'f', b'o', b'o']).unwrap();
+ ByteString::try_from(vec![0, 159, 146, 150]).unwrap_err();
+ }
+
+ #[test]
+ fn try_from_bytes() {
+ let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap();
+ }
+
+ #[test]
+ fn try_from_bytes_mut() {
+ let _ = ByteString::try_from(bytes::BytesMut::from(&b"nice bytes"[..])).unwrap();
+ }
+
+ #[test]
+ fn display() {
+ let buf = ByteString::from("bar");
+ assert_eq!(format!("{buf}"), "bar");
+ }
+
+ #[test]
+ fn debug() {
+ let buf = ByteString::from("baz");
+ assert_eq!(format!("{buf:?}"), r#""baz""#);
+ }
+
+ #[cfg(feature = "serde")]
+ #[test]
+ fn serialize() {
+ let s: ByteString = serde_json::from_str(r#""nice bytes""#).unwrap();
+ assert_eq!(s, "nice bytes");
+ }
+
+ #[cfg(feature = "serde")]
+ #[test]
+ fn deserialize() {
+ let s = serde_json::to_string(&ByteString::from_static("nice bytes")).unwrap();
+ assert_eq!(s, r#""nice bytes""#);
+ }
+
+ #[test]
+ fn slice_ref() {
+ let string = ByteString::from_static(" foo ");
+ let subset = string.trim();
+ // subset is derived from original byte string
+ let substring = string.slice_ref(subset);
+ assert_eq!(substring, "foo");
+ }
+
+ #[test]
+ #[should_panic]
+ fn slice_ref_catches_not_a_subset() {
+ // panics because the given slice is not derived from the original byte string, despite
+ // being a logical subset of the string
+ ByteString::from_static("foo bar").slice_ref("foo");
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +
//! A non-thread-safe multi-producer, single-consumer, futures-aware, FIFO queue.
+
+use alloc::{collections::VecDeque, rc::Rc};
+use core::{
+ cell::RefCell,
+ fmt,
+ future::poll_fn,
+ pin::Pin,
+ task::{Context, Poll},
+};
+use std::error::Error;
+
+use futures_core::stream::Stream;
+use futures_sink::Sink;
+use local_waker::LocalWaker;
+
+/// Creates a unbounded in-memory channel with buffered storage.
+///
+/// [Sender]s and [Receiver]s are `!Send`.
+pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
+ let shared = Rc::new(RefCell::new(Shared {
+ has_receiver: true,
+ buffer: VecDeque::new(),
+ blocked_recv: LocalWaker::new(),
+ }));
+
+ let sender = Sender {
+ shared: shared.clone(),
+ };
+
+ let receiver = Receiver { shared };
+
+ (sender, receiver)
+}
+
+#[derive(Debug)]
+struct Shared<T> {
+ buffer: VecDeque<T>,
+ blocked_recv: LocalWaker,
+ has_receiver: bool,
+}
+
+/// The transmission end of a channel.
+///
+/// This is created by the `channel` function.
+#[derive(Debug)]
+pub struct Sender<T> {
+ shared: Rc<RefCell<Shared<T>>>,
+}
+
+impl<T> Unpin for Sender<T> {}
+
+impl<T> Sender<T> {
+ /// Sends the provided message along this channel.
+ pub fn send(&self, item: T) -> Result<(), SendError<T>> {
+ let mut shared = self.shared.borrow_mut();
+
+ if !shared.has_receiver {
+ // receiver was dropped
+ return Err(SendError(item));
+ };
+
+ shared.buffer.push_back(item);
+ shared.blocked_recv.wake();
+
+ Ok(())
+ }
+
+ /// Closes the sender half.
+ ///
+ /// This prevents any further messages from being sent on the channel, by any sender, while
+ /// still enabling the receiver to drain messages that are already buffered.
+ pub fn close(&mut self) {
+ self.shared.borrow_mut().has_receiver = false;
+ }
+}
+
+impl<T> Clone for Sender<T> {
+ fn clone(&self) -> Self {
+ Sender {
+ shared: self.shared.clone(),
+ }
+ }
+}
+
+impl<T> Sink<T> for Sender<T> {
+ type Error = SendError<T>;
+
+ fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ Poll::Ready(Ok(()))
+ }
+
+ fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), SendError<T>> {
+ self.send(item)
+ }
+
+ fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), SendError<T>>> {
+ Poll::Ready(Ok(()))
+ }
+
+ fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ Poll::Ready(Ok(()))
+ }
+}
+
+impl<T> Drop for Sender<T> {
+ fn drop(&mut self) {
+ let count = Rc::strong_count(&self.shared);
+ let shared = self.shared.borrow_mut();
+
+ // check is last sender is about to drop
+ if shared.has_receiver && count == 2 {
+ // Wake up receiver as its stream has ended
+ shared.blocked_recv.wake();
+ }
+ }
+}
+
+/// The receiving end of a channel which implements the `Stream` trait.
+///
+/// This is created by the [`channel`] function.
+#[derive(Debug)]
+pub struct Receiver<T> {
+ shared: Rc<RefCell<Shared<T>>>,
+}
+
+impl<T> Receiver<T> {
+ /// Receive the next value.
+ ///
+ /// Returns `None` if the channel is empty and has been [closed](Sender::close) explicitly or
+ /// when all senders have been dropped and, therefore, no more values can ever be sent though
+ /// this channel.
+ pub async fn recv(&mut self) -> Option<T> {
+ let mut this = Pin::new(self);
+ poll_fn(|cx| this.as_mut().poll_next(cx)).await
+ }
+
+ /// Create an associated [Sender].
+ pub fn sender(&self) -> Sender<T> {
+ Sender {
+ shared: self.shared.clone(),
+ }
+ }
+}
+
+impl<T> Unpin for Receiver<T> {}
+
+impl<T> Stream for Receiver<T> {
+ type Item = T;
+
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ let mut shared = self.shared.borrow_mut();
+
+ if Rc::strong_count(&self.shared) == 1 {
+ // All senders have been dropped, so drain the buffer and end the stream.
+ return Poll::Ready(shared.buffer.pop_front());
+ }
+
+ if let Some(msg) = shared.buffer.pop_front() {
+ Poll::Ready(Some(msg))
+ } else {
+ shared.blocked_recv.register(cx.waker());
+ Poll::Pending
+ }
+ }
+}
+
+impl<T> Drop for Receiver<T> {
+ fn drop(&mut self) {
+ let mut shared = self.shared.borrow_mut();
+ shared.buffer.clear();
+ shared.has_receiver = false;
+ }
+}
+
+/// Error returned when attempting to send after the channels' [Receiver] is dropped or closed.
+///
+/// Allows access to message that failed to send with [`into_inner`](Self::into_inner).
+pub struct SendError<T>(pub T);
+
+impl<T> SendError<T> {
+ /// Returns the message that was attempted to be sent but failed.
+ pub fn into_inner(self) -> T {
+ self.0
+ }
+}
+
+impl<T> fmt::Debug for SendError<T> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt.debug_tuple("SendError").field(&"...").finish()
+ }
+}
+
+impl<T> fmt::Display for SendError<T> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(fmt, "send failed because receiver is gone")
+ }
+}
+
+impl<T> Error for SendError<T> {}
+
+#[cfg(test)]
+mod tests {
+ use futures_util::{future::lazy, StreamExt as _};
+
+ use super::*;
+
+ #[tokio::test]
+ async fn test_mpsc() {
+ let (tx, mut rx) = channel();
+ tx.send("test").unwrap();
+ assert_eq!(rx.next().await.unwrap(), "test");
+
+ let tx2 = tx.clone();
+ tx2.send("test2").unwrap();
+ assert_eq!(rx.next().await.unwrap(), "test2");
+
+ assert_eq!(
+ lazy(|cx| Pin::new(&mut rx).poll_next(cx)).await,
+ Poll::Pending
+ );
+ drop(tx2);
+ assert_eq!(
+ lazy(|cx| Pin::new(&mut rx).poll_next(cx)).await,
+ Poll::Pending
+ );
+ drop(tx);
+ assert_eq!(rx.next().await, None);
+
+ let (tx, rx) = channel();
+ tx.send("test").unwrap();
+ drop(rx);
+ assert!(tx.send("test").is_err());
+
+ let (mut tx, _) = channel();
+ let tx2 = tx.clone();
+ tx.close();
+ assert!(tx.send("test").is_err());
+ assert!(tx2.send("test").is_err());
+ }
+
+ #[tokio::test]
+ async fn test_recv() {
+ let (tx, mut rx) = channel();
+ tx.send("test").unwrap();
+ assert_eq!(rx.recv().await.unwrap(), "test");
+ drop(tx);
+
+ let (tx, mut rx) = channel();
+ tx.send("test").unwrap();
+ assert_eq!(rx.recv().await.unwrap(), "test");
+ drop(tx);
+ assert!(rx.recv().await.is_none());
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +
//! A synchronization primitive for thread-local task wakeup.
+//!
+//! See docs for [`LocalWaker`].
+
+#![no_std]
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible, missing_docs)]
+
+use core::{cell::Cell, fmt, marker::PhantomData, task::Waker};
+
+/// A synchronization primitive for task wakeup.
+///
+/// Sometimes the task interested in a given event will change over time. A `LocalWaker` can
+/// coordinate concurrent notifications with the consumer, potentially "updating" the underlying
+/// task to wake up. This is useful in scenarios where a computation completes in another task and
+/// wants to notify the consumer, but the consumer is in the process of being migrated to a new
+/// logical task.
+///
+/// Consumers should call [`register`] before checking the result of a computation and producers
+/// should call [`wake`] after producing the computation (this differs from the usual `thread::park`
+/// pattern). It is also permitted for [`wake`] to be called _before_ [`register`]. This results in
+/// a no-op.
+///
+/// A single `LocalWaker` may be reused for any number of calls to [`register`] or [`wake`].
+///
+/// [`register`]: LocalWaker::register
+/// [`wake`]: LocalWaker::wake
+#[derive(Default)]
+pub struct LocalWaker {
+ pub(crate) waker: Cell<Option<Waker>>,
+ // mark LocalWaker as a !Send type.
+ _phantom: PhantomData<*const ()>,
+}
+
+impl LocalWaker {
+ /// Creates a new, empty `LocalWaker`.
+ pub fn new() -> Self {
+ LocalWaker::default()
+ }
+
+ /// Registers the waker to be notified on calls to `wake`.
+ ///
+ /// Returns `true` if waker was registered before.
+ #[inline]
+ pub fn register(&self, waker: &Waker) -> bool {
+ let last_waker = self.waker.replace(Some(waker.clone()));
+ last_waker.is_some()
+ }
+
+ /// Calls `wake` on the last `Waker` passed to `register`.
+ ///
+ /// If `register` has not been called yet, then this does nothing.
+ #[inline]
+ pub fn wake(&self) {
+ if let Some(waker) = self.take() {
+ waker.wake();
+ }
+ }
+
+ /// Returns the last `Waker` passed to `register`, so that the user can wake it.
+ ///
+ /// If a waker has not been registered, this returns `None`.
+ #[inline]
+ pub fn take(&self) -> Option<Waker> {
+ self.waker.take()
+ }
+}
+
+impl fmt::Debug for LocalWaker {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "LocalWaker")
+ }
+}
+
fn:
) to \
+ restrict the search to a given item kind.","Accepted kinds are: fn
, mod
, struct
, \
+ enum
, trait
, type
, macro
, \
+ and const
.","Search functions by type signature (e.g., vec -> usize
or \
+ -> vec
or String, enum:Cow -> bool
)","You can look for items with an exact name by putting double quotes around \
+ your request: \"string\"
","Look for functions that accept or return \
+ slices and \
+ arrays by writing \
+ square brackets (e.g., -> [u8]
or [] -> Option
)","Look for items inside another one by searching for a path: vec::Vec
",].map(x=>""+x+"
").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="${value.replaceAll(" ", " ")}
`}else{error[index]=value}});output+=`async_fn_traits
)AsyncFn
, returning a future which may borrow from the called closure.async_fn_traits
)AsyncFnMut::async_call_mut
and AsyncFn::async_call
.async_fn_traits
)AsyncFnMut
, returning a future which may borrow from the called closure.async_fn_traits
)async_fn_traits
)AsyncFnOnce::async_call_once
.async_fn_traits
)AsyncFnOnce
, returning a future which may move out of the called closure.async_iterator
)async_iterator
)None
if the async iterator is exhausted. Read moreAllocates memory on the heap and then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
let five = Box::new(5);
new_uninit
)Constructs a new box with uninitialized contents.
\n#![feature(new_uninit)]\n\nlet mut five = Box::<u32>::new_uninit();\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
new_uninit
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit)]\n\nlet zero = Box::<u32>::new_zeroed();\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
Constructs a new Pin<Box<T>>
. If T
does not implement Unpin
, then\nx
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin(x)
\ndoes the same as Box::into_pin(Box::new(x))
. Consider using\ninto_pin
if you already have a Box<T>
, or if you want to\nconstruct a (pinned) Box
in a different way than with Box::new
.
allocator_api
)Allocates memory on the heap then places x
into it,\nreturning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nlet five = Box::try_new(5)?;
allocator_api
)Constructs a new box with uninitialized contents on the heap,\nreturning an error if the allocation fails
\n#![feature(allocator_api, new_uninit)]\n\nlet mut five = Box::<u32>::try_new_uninit()?;\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes on the heap
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nlet zero = Box::<u32>::try_new_zeroed()?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
Constructs a box from a raw pointer.
\nAfter calling this function, the raw pointer is owned by the\nresulting Box
. Specifically, the Box
destructor will call\nthe destructor of T
and free the allocated memory. For this\nto be safe, the memory must have been allocated in accordance\nwith the memory layout used by Box
.
This function is unsafe because improper use may lead to\nmemory problems. For example, a double-free may occur if the\nfunction is called twice on the same raw pointer.
\nThe safety conditions are described in the memory layout section.
\nRecreate a Box
which was previously converted to a raw pointer\nusing Box::into_raw
:
let x = Box::new(5);\nlet ptr = Box::into_raw(x);\nlet x = unsafe { Box::from_raw(ptr) };
Manually create a Box
from scratch by using the global allocator:
use std::alloc::{alloc, Layout};\n\nunsafe {\n let ptr = alloc(Layout::new::<i32>()) as *mut i32;\n // In general .write is required to avoid attempting to destruct\n // the (uninitialized) previous contents of `ptr`, though for this\n // simple example `*ptr = 5` would have worked as well.\n ptr.write(5);\n let x = Box::from_raw(ptr);\n}
allocator_api
)Allocates memory in the given allocator then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet five = Box::new_in(5, System);
allocator_api
)Allocates memory in the given allocator then places x
into it,\nreturning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet five = Box::try_new_in(5, System)?;
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator.
\n#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet mut five = Box::<u32, _>::new_uninit_in(System);\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator,\nreturning an error if the allocation fails
\n#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet mut five = Box::<u32, _>::try_new_uninit_in(System)?;\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes in the provided allocator.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet zero = Box::<u32, _>::new_zeroed_in(System);\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes in the provided allocator,\nreturning an error if the allocation fails,
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet zero = Box::<u32, _>::try_new_zeroed_in(System)?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Constructs a new Pin<Box<T, A>>
. If T
does not implement Unpin
, then\nx
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin_in(x, alloc)
\ndoes the same as Box::into_pin(Box::new_in(x, alloc))
. Consider using\ninto_pin
if you already have a Box<T, A>
, or if you want to\nconstruct a (pinned) Box
in a different way than with Box::new_in
.
box_into_boxed_slice
)Converts a Box<T>
into a Box<[T]>
This conversion does not allocate on the heap and happens in place.
\nbox_into_inner
)Consumes the Box
, returning the wrapped value.
#![feature(box_into_inner)]\n\nlet c = Box::new(5);\n\nassert_eq!(Box::into_inner(c), 5);
allocator_api
)Constructs a box from a raw pointer in the given allocator.
\nAfter calling this function, the raw pointer is owned by the\nresulting Box
. Specifically, the Box
destructor will call\nthe destructor of T
and free the allocated memory. For this\nto be safe, the memory must have been allocated in accordance\nwith the memory layout used by Box
.
This function is unsafe because improper use may lead to\nmemory problems. For example, a double-free may occur if the\nfunction is called twice on the same raw pointer.
\nRecreate a Box
which was previously converted to a raw pointer\nusing Box::into_raw_with_allocator
:
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet x = Box::new_in(5, System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nlet x = unsafe { Box::from_raw_in(ptr, alloc) };
Manually create a Box
from scratch by using the system allocator:
#![feature(allocator_api, slice_ptr_get)]\n\nuse std::alloc::{Allocator, Layout, System};\n\nunsafe {\n let ptr = System.allocate(Layout::new::<i32>())?.as_mut_ptr() as *mut i32;\n // In general .write is required to avoid attempting to destruct\n // the (uninitialized) previous contents of `ptr`, though for this\n // simple example `*ptr = 5` would have worked as well.\n ptr.write(5);\n let x = Box::from_raw_in(ptr, System);\n}
Consumes the Box
, returning a wrapped raw pointer.
The pointer will be properly aligned and non-null.
\nAfter calling this function, the caller is responsible for the\nmemory previously managed by the Box
. In particular, the\ncaller should properly destroy T
and release the memory, taking\ninto account the memory layout used by Box
. The easiest way to\ndo this is to convert the raw pointer back into a Box
with the\nBox::from_raw
function, allowing the Box
destructor to perform\nthe cleanup.
Note: this is an associated function, which means that you have\nto call it as Box::into_raw(b)
instead of b.into_raw()
. This\nis so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw
\nfor automatic cleanup:
let x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nlet x = unsafe { Box::from_raw(ptr) };
Manual cleanup by explicitly running the destructor and deallocating\nthe memory:
\n\nuse std::alloc::{dealloc, Layout};\nuse std::ptr;\n\nlet x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nunsafe {\n ptr::drop_in_place(ptr);\n dealloc(ptr as *mut u8, Layout::new::<String>());\n}
Note: This is equivalent to the following:
\n\nlet x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nunsafe {\n drop(Box::from_raw(ptr));\n}
allocator_api
)Consumes the Box
, returning a wrapped raw pointer and the allocator.
The pointer will be properly aligned and non-null.
\nAfter calling this function, the caller is responsible for the\nmemory previously managed by the Box
. In particular, the\ncaller should properly destroy T
and release the memory, taking\ninto account the memory layout used by Box
. The easiest way to\ndo this is to convert the raw pointer back into a Box
with the\nBox::from_raw_in
function, allowing the Box
destructor to perform\nthe cleanup.
Note: this is an associated function, which means that you have\nto call it as Box::into_raw_with_allocator(b)
instead of b.into_raw_with_allocator()
. This\nis so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw_in
\nfor automatic cleanup:
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet x = Box::new_in(String::from(\"Hello\"), System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nlet x = unsafe { Box::from_raw_in(ptr, alloc) };
Manual cleanup by explicitly running the destructor and deallocating\nthe memory:
\n\n#![feature(allocator_api)]\n\nuse std::alloc::{Allocator, Layout, System};\nuse std::ptr::{self, NonNull};\n\nlet x = Box::new_in(String::from(\"Hello\"), System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nunsafe {\n ptr::drop_in_place(ptr);\n let non_null = NonNull::new_unchecked(ptr);\n alloc.deallocate(non_null.cast(), Layout::new::<String>());\n}
allocator_api
)Returns a reference to the underlying allocator.
\nNote: this is an associated function, which means that you have\nto call it as Box::allocator(&b)
instead of b.allocator()
. This\nis so that there is no conflict with a method on the inner type.
Consumes and leaks the Box
, returning a mutable reference,\n&'a mut T
. Note that the type T
must outlive the chosen lifetime\n'a
. If the type has only static references, or none at all, then this\nmay be chosen to be 'static
.
This function is mainly useful for data that lives for the remainder of\nthe program’s life. Dropping the returned reference will cause a memory\nleak. If this is not acceptable, the reference should first be wrapped\nwith the Box::from_raw
function producing a Box
. This Box
can\nthen be dropped which will properly destroy T
and release the\nallocated memory.
Note: this is an associated function, which means that you have\nto call it as Box::leak(b)
instead of b.leak()
. This\nis so that there is no conflict with a method on the inner type.
Simple usage:
\n\nlet x = Box::new(41);\nlet static_ref: &'static mut usize = Box::leak(x);\n*static_ref += 1;\nassert_eq!(*static_ref, 42);
Unsized data:
\n\nlet x = vec![1, 2, 3].into_boxed_slice();\nlet static_ref = Box::leak(x);\nstatic_ref[0] = 4;\nassert_eq!(*static_ref, [4, 2, 3]);
Converts a Box<T>
into a Pin<Box<T>>
. If T
does not implement Unpin
, then\n*boxed
will be pinned in memory and unable to be moved.
This conversion does not allocate on the heap and happens in place.
\nThis is also available via From
.
Constructing and pinning a Box
with Box::into_pin(Box::new(x))
\ncan also be written more concisely using Box::pin(x)
.\nThis into_pin
method is useful if you already have a Box<T>
, or you are\nconstructing a (pinned) Box
in a different way than with Box::new
.
It’s not recommended that crates add an impl like From<Box<T>> for Pin<T>
,\nas it’ll introduce an ambiguity when calling Pin::from
.\nA demonstration of such a poor impl is shown below.
struct Foo; // A type defined in this crate.\nimpl From<Box<()>> for Pin<Foo> {\n fn from(_: Box<()>) -> Pin<Foo> {\n Pin::new(Foo)\n }\n}\n\nlet foo = Box::new(());\nlet bar = Pin::from(foo);
amt
bytes have been consumed from the buffer,\nso they should no longer be returned in calls to read
. Read more0xA
byte) is reached, and append\nthem to the provided String
buffer. Read morebuf_read_has_data_left
)Read
has any data left to be read. Read morebufread_skip_until
)byte
or EOF is reached. Read moreReturns a new box with a clone()
of this box’s contents.
let x = Box::new(5);\nlet y = x.clone();\n\n// The value is the same\nassert_eq!(x, y);\n\n// But they are unique objects\nassert_ne!(&*x as *const i32, &*y as *const i32);
Copies source
’s contents into self
without creating a new allocation.
let x = Box::new(5);\nlet mut y = Box::new(10);\nlet yp: *const i32 = &*y;\n\ny.clone_from(&x);\n\n// The value is the same\nassert_eq!(x, y);\n\n// And no allocation occurred\nassert_eq!(yp, &*y);
coroutine_trait
)n
th element from the end of the iterator. Read moreiter_advance_by
)n
elements. Read moreIterator::try_fold()
: it takes\nelements starting from the back of the iterator. Read moretrue
if the underlying future should no longer be polled.true
if the stream should no longer be polled.u128
into this hasher.usize
into this hasher.i128
into this hasher.isize
into this hasher.hasher_prefixfree_extras
)n
th element of the iterator. Read moreiter_next_chunk
)N
values. Read moreiter_advance_by
)n
elements. Read moreiter_intersperse
)separator
\nbetween adjacent items of the original iterator. Read moren
elements. Read moren
elements, or fewer\nif the underlying iterator ends sooner. Read moreiter_map_windows
)f
for each contiguous window of size N
over\nself
and returns an iterator over the outputs of f
. Like slice::windows()
,\nthe windows during mapping overlap as well. Read moreiter_collect_into
)iter_is_partitioned
)true
precede all those that return false
. Read moreiterator_try_reduce
)try_find
)iter_array_chunks
)N
elements of the iterator at a time. Read moreiter_order_by
)Iterator
with those\nof another with respect to the specified comparison function. Read morePartialOrd
elements of\nthis Iterator
with those of another. The comparison works like short-circuit\nevaluation, returning a result without comparing the remaining elements.\nAs soon as an order can be determined, the evaluation stops and a result is returned. Read moreiter_order_by
)Iterator
with those\nof another with respect to the specified comparison function. Read moreiter_order_by
)Iterator
are lexicographically\nless than those of another. Read moreIterator
are lexicographically\nless or equal to those of another. Read moreIterator
are lexicographically\ngreater than those of another. Read moreIterator
are lexicographically\ngreater than or equal to those of another. Read moreis_sorted
)is_sorted
)self
and other
) and is used by the <=
\noperator. Read moreread_buf
)read
, except that it reads into a slice of buffers. Read morecan_vector
)buf
. Read morebuf
. Read morebuf
. Read moreread_buf
)cursor
. Read moreRead
. Read morecan_vector
)Makes a clone of the Rc
pointer.
This creates another pointer to the same allocation, increasing the\nstrong reference count.
\nuse std::rc::Rc;\n\nlet five = Rc::new(5);\n\nlet _ = Rc::clone(&five);
source
. Read moreDrops the Rc
.
This will decrement the strong reference count. If the strong reference\ncount reaches zero then the only other references (if any) are\nWeak
, so we drop
the inner value.
use std::rc::Rc;\n\nstruct Foo;\n\nimpl Drop for Foo {\n fn drop(&mut self) {\n println!(\"dropped!\");\n }\n}\n\nlet foo = Rc::new(Foo);\nlet foo2 = Rc::clone(&foo);\n\ndrop(foo); // Doesn't print anything\ndrop(foo2); // Prints \"dropped!\"
Comparison for two Rc
s.
The two are compared by calling cmp()
on their inner values.
use std::rc::Rc;\nuse std::cmp::Ordering;\n\nlet five = Rc::new(5);\n\nassert_eq!(Ordering::Less, five.cmp(&Rc::new(6)));
Equality for two Rc
s.
Two Rc
s are equal if their inner values are equal, even if they are\nstored in different allocation.
If T
also implements Eq
(implying reflexivity of equality),\ntwo Rc
s that point to the same allocation are\nalways equal.
use std::rc::Rc;\n\nlet five = Rc::new(5);\n\nassert!(five == Rc::new(5));
Inequality for two Rc
s.
Two Rc
s are not equal if their inner values are not equal.
If T
also implements Eq
(implying reflexivity of equality),\ntwo Rc
s that point to the same allocation are\nalways equal.
use std::rc::Rc;\n\nlet five = Rc::new(5);\n\nassert!(five != Rc::new(6));
Partial comparison for two Rc
s.
The two are compared by calling partial_cmp()
on their inner values.
use std::rc::Rc;\nuse std::cmp::Ordering;\n\nlet five = Rc::new(5);\n\nassert_eq!(Some(Ordering::Less), five.partial_cmp(&Rc::new(6)));
Less-than comparison for two Rc
s.
The two are compared by calling <
on their inner values.
use std::rc::Rc;\n\nlet five = Rc::new(5);\n\nassert!(five < Rc::new(6));
‘Less than or equal to’ comparison for two Rc
s.
The two are compared by calling <=
on their inner values.
use std::rc::Rc;\n\nlet five = Rc::new(5);\n\nassert!(five <= Rc::new(5));
Constructs an Rc<T>
from a raw pointer.
The raw pointer must have been previously returned by a call to\nRc<U>::into_raw
with the following requirements:
U
is sized, it must have the same size and alignment as T
. This\nis trivially true if U
is T
.U
is unsized, its data pointer must have the same size and\nalignment as T
. This is trivially true if Rc<U>
was constructed\nthrough Rc<T>
and then converted to Rc<U>
through an unsized\ncoercion.Note that if U
or U
’s data pointer is not T
but has the same size\nand alignment, this is basically like transmuting references of\ndifferent types. See mem::transmute
for more information\non what restrictions apply in this case.
The raw pointer must point to a block of memory allocated by the global allocator
\nThe user of from_raw
has to make sure a specific value of T
is only\ndropped once.
This function is unsafe because improper use may lead to memory unsafety,\neven if the returned Rc<T>
is never accessed.
use std::rc::Rc;\n\nlet x = Rc::new(\"hello\".to_owned());\nlet x_ptr = Rc::into_raw(x);\n\nunsafe {\n // Convert back to an `Rc` to prevent leak.\n let x = Rc::from_raw(x_ptr);\n assert_eq!(&*x, \"hello\");\n\n // Further calls to `Rc::from_raw(x_ptr)` would be memory-unsafe.\n}\n\n// The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
Convert a slice back into its original array:
\n\nuse std::rc::Rc;\n\nlet x: Rc<[u32]> = Rc::new([1, 2, 3]);\nlet x_ptr: *const [u32] = Rc::into_raw(x);\n\nunsafe {\n let x: Rc<[u32; 3]> = Rc::from_raw(x_ptr.cast::<[u32; 3]>());\n assert_eq!(&*x, &[1, 2, 3]);\n}
Increments the strong reference count on the Rc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Rc::into_raw
, the\nassociated Rc
instance must be valid (i.e. the strong count must be at\nleast 1) for the duration of this method, and ptr
must point to a block of memory\nallocated by the global allocator.
use std::rc::Rc;\n\nlet five = Rc::new(5);\n\nunsafe {\n let ptr = Rc::into_raw(five);\n Rc::increment_strong_count(ptr);\n\n let five = Rc::from_raw(ptr);\n assert_eq!(2, Rc::strong_count(&five));\n}
Decrements the strong reference count on the Rc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Rc::into_raw
, the\nassociated Rc
instance must be valid (i.e. the strong count must be at\nleast 1) when invoking this method, and ptr
must point to a block of memory\nallocated by the global allocator. This method can be used to release the final Rc
and\nbacking storage, but should not be called after the final Rc
has been released.
use std::rc::Rc;\n\nlet five = Rc::new(5);\n\nunsafe {\n let ptr = Rc::into_raw(five);\n Rc::increment_strong_count(ptr);\n\n let five = Rc::from_raw(ptr);\n assert_eq!(2, Rc::strong_count(&five));\n Rc::decrement_strong_count(ptr);\n assert_eq!(1, Rc::strong_count(&five));\n}
Constructs a new Rc<T>
while giving you a Weak<T>
to the allocation,\nto allow you to construct a T
which holds a weak pointer to itself.
Generally, a structure circularly referencing itself, either directly or\nindirectly, should not hold a strong reference to itself to prevent a memory leak.\nUsing this function, you get access to the weak pointer during the\ninitialization of T
, before the Rc<T>
is created, such that you can\nclone and store it inside the T
.
new_cyclic
first allocates the managed allocation for the Rc<T>
,\nthen calls your closure, giving it a Weak<T>
to this allocation,\nand only afterwards completes the construction of the Rc<T>
by placing\nthe T
returned from your closure into the allocation.
Since the new Rc<T>
is not fully-constructed until Rc<T>::new_cyclic
\nreturns, calling upgrade
on the weak reference inside your closure will\nfail and result in a None
value.
If data_fn
panics, the panic is propagated to the caller, and the\ntemporary Weak<T>
is dropped normally.
use std::rc::{Rc, Weak};\n\nstruct Gadget {\n me: Weak<Gadget>,\n}\n\nimpl Gadget {\n /// Construct a reference counted Gadget.\n fn new() -> Rc<Self> {\n // `me` is a `Weak<Gadget>` pointing at the new allocation of the\n // `Rc` we're constructing.\n Rc::new_cyclic(|me| {\n // Create the actual struct here.\n Gadget { me: me.clone() }\n })\n }\n\n /// Return a reference counted pointer to Self.\n fn me(&self) -> Rc<Self> {\n self.me.upgrade().unwrap()\n }\n}
new_uninit
)Constructs a new Rc
with uninitialized contents.
#![feature(new_uninit)]\n#![feature(get_mut_unchecked)]\n\nuse std::rc::Rc;\n\nlet mut five = Rc::<u32>::new_uninit();\n\n// Deferred initialization:\nRc::get_mut(&mut five).unwrap().write(5);\n\nlet five = unsafe { five.assume_init() };\n\nassert_eq!(*five, 5)
new_uninit
)Constructs a new Rc
with uninitialized contents, with the memory\nbeing filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and\nincorrect usage of this method.
#![feature(new_uninit)]\n\nuse std::rc::Rc;\n\nlet zero = Rc::<u32>::new_zeroed();\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
allocator_api
)Constructs a new Rc<T>
, returning an error if the allocation fails
#![feature(allocator_api)]\nuse std::rc::Rc;\n\nlet five = Rc::try_new(5);
allocator_api
)Constructs a new Rc
with uninitialized contents, returning an error if the allocation fails
#![feature(allocator_api, new_uninit)]\n#![feature(get_mut_unchecked)]\n\nuse std::rc::Rc;\n\nlet mut five = Rc::<u32>::try_new_uninit()?;\n\n// Deferred initialization:\nRc::get_mut(&mut five).unwrap().write(5);\n\nlet five = unsafe { five.assume_init() };\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Rc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, returning an error if the allocation fails
See MaybeUninit::zeroed
for examples of correct and\nincorrect usage of this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::rc::Rc;\n\nlet zero = Rc::<u32>::try_new_zeroed()?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
Consumes the Rc
, returning the wrapped pointer.
To avoid a memory leak the pointer must be converted back to an Rc
using\nRc::from_raw
.
use std::rc::Rc;\n\nlet x = Rc::new(\"hello\".to_owned());\nlet x_ptr = Rc::into_raw(x);\nassert_eq!(unsafe { &*x_ptr }, \"hello\");
allocator_api
)Consumes the Rc
, returning the wrapped pointer and allocator.
To avoid a memory leak the pointer must be converted back to an Rc
using\nRc::from_raw_in
.
#![feature(allocator_api)]\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet x = Rc::new_in(\"hello\".to_owned(), System);\nlet (ptr, alloc) = Rc::into_raw_with_allocator(x);\nassert_eq!(unsafe { &*ptr }, \"hello\");\nlet x = unsafe { Rc::from_raw_in(ptr, alloc) };\nassert_eq!(&*x, \"hello\");
Provides a raw pointer to the data.
\nThe counts are not affected in any way and the Rc
is not consumed. The pointer is valid\nfor as long there are strong counts in the Rc
.
use std::rc::Rc;\n\nlet x = Rc::new(\"hello\".to_owned());\nlet y = Rc::clone(&x);\nlet x_ptr = Rc::as_ptr(&x);\nassert_eq!(x_ptr, Rc::as_ptr(&y));\nassert_eq!(unsafe { &*x_ptr }, \"hello\");
allocator_api
)Constructs an Rc<T, A>
from a raw pointer in the provided allocator.
The raw pointer must have been previously returned by a call to Rc<U, A>::into_raw
with the following requirements:
U
is sized, it must have the same size and alignment as T
. This\nis trivially true if U
is T
.U
is unsized, its data pointer must have the same size and\nalignment as T
. This is trivially true if Rc<U>
was constructed\nthrough Rc<T>
and then converted to Rc<U>
through an unsized\ncoercion.Note that if U
or U
’s data pointer is not T
but has the same size\nand alignment, this is basically like transmuting references of\ndifferent types. See mem::transmute
for more information\non what restrictions apply in this case.
The raw pointer must point to a block of memory allocated by alloc
The user of from_raw
has to make sure a specific value of T
is only\ndropped once.
This function is unsafe because improper use may lead to memory unsafety,\neven if the returned Rc<T>
is never accessed.
#![feature(allocator_api)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet x = Rc::new_in(\"hello\".to_owned(), System);\nlet x_ptr = Rc::into_raw(x);\n\nunsafe {\n // Convert back to an `Rc` to prevent leak.\n let x = Rc::from_raw_in(x_ptr, System);\n assert_eq!(&*x, \"hello\");\n\n // Further calls to `Rc::from_raw(x_ptr)` would be memory-unsafe.\n}\n\n// The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
Convert a slice back into its original array:
\n\n#![feature(allocator_api)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet x: Rc<[u32], _> = Rc::new_in([1, 2, 3], System);\nlet x_ptr: *const [u32] = Rc::into_raw(x);\n\nunsafe {\n let x: Rc<[u32; 3], _> = Rc::from_raw_in(x_ptr.cast::<[u32; 3]>(), System);\n assert_eq!(&*x, &[1, 2, 3]);\n}
Gets the number of strong (Rc
) pointers to this allocation.
use std::rc::Rc;\n\nlet five = Rc::new(5);\nlet _also_five = Rc::clone(&five);\n\nassert_eq!(2, Rc::strong_count(&five));
allocator_api
)Increments the strong reference count on the Rc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Rc::into_raw
, the\nassociated Rc
instance must be valid (i.e. the strong count must be at\nleast 1) for the duration of this method, and ptr
must point to a block of memory\nallocated by alloc
#![feature(allocator_api)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet five = Rc::new_in(5, System);\n\nunsafe {\n let ptr = Rc::into_raw(five);\n Rc::increment_strong_count_in(ptr, System);\n\n let five = Rc::from_raw_in(ptr, System);\n assert_eq!(2, Rc::strong_count(&five));\n}
allocator_api
)Decrements the strong reference count on the Rc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Rc::into_raw
, the\nassociated Rc
instance must be valid (i.e. the strong count must be at\nleast 1) when invoking this method, and ptr
must point to a block of memory\nallocated by alloc
. This method can be used to release the final Rc
and backing storage,\nbut should not be called after the final Rc
has been released.
#![feature(allocator_api)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet five = Rc::new_in(5, System);\n\nunsafe {\n let ptr = Rc::into_raw(five);\n Rc::increment_strong_count_in(ptr, System);\n\n let five = Rc::from_raw_in(ptr, System);\n assert_eq!(2, Rc::strong_count(&five));\n Rc::decrement_strong_count_in(ptr, System);\n assert_eq!(1, Rc::strong_count(&five));\n}
Returns a mutable reference into the given Rc
, if there are\nno other Rc
or Weak
pointers to the same allocation.
Returns None
otherwise, because it is not safe to\nmutate a shared value.
See also make_mut
, which will clone
\nthe inner value when there are other Rc
pointers.
use std::rc::Rc;\n\nlet mut x = Rc::new(3);\n*Rc::get_mut(&mut x).unwrap() = 4;\nassert_eq!(*x, 4);\n\nlet _y = Rc::clone(&x);\nassert!(Rc::get_mut(&mut x).is_none());
get_mut_unchecked
)Returns a mutable reference into the given Rc
,\nwithout any check.
See also get_mut
, which is safe and does appropriate checks.
If any other Rc
or Weak
pointers to the same allocation exist, then\nthey must not be dereferenced or have active borrows for the duration\nof the returned borrow, and their inner type must be exactly the same as the\ninner type of this Rc (including lifetimes). This is trivially the case if no\nsuch pointers exist, for example immediately after Rc::new
.
#![feature(get_mut_unchecked)]\n\nuse std::rc::Rc;\n\nlet mut x = Rc::new(String::new());\nunsafe {\n Rc::get_mut_unchecked(&mut x).push_str(\"foo\")\n}\nassert_eq!(*x, \"foo\");
Other Rc
pointers to the same allocation must be to the same type.
#![feature(get_mut_unchecked)]\n\nuse std::rc::Rc;\n\nlet x: Rc<str> = Rc::from(\"Hello, world!\");\nlet mut y: Rc<[u8]> = x.clone().into();\nunsafe {\n // this is Undefined Behavior, because x's inner type is str, not [u8]\n Rc::get_mut_unchecked(&mut y).fill(0xff); // 0xff is invalid in UTF-8\n}\nprintln!(\"{}\", &*x); // Invalid UTF-8 in a str
Other Rc
pointers to the same allocation must be to the exact same type, including lifetimes.
#![feature(get_mut_unchecked)]\n\nuse std::rc::Rc;\n\nlet x: Rc<&str> = Rc::new(\"Hello, world!\");\n{\n let s = String::from(\"Oh, no!\");\n let mut y: Rc<&str> = x.clone().into();\n unsafe {\n // this is Undefined Behavior, because x's inner type\n // is &'long str, not &'short str\n *Rc::get_mut_unchecked(&mut y) = &s;\n }\n}\nprintln!(\"{}\", &*x); // Use-after-free
Returns true
if the two Rc
s point to the same allocation in a vein similar to\nptr::eq
. This function ignores the metadata of dyn Trait
pointers.
use std::rc::Rc;\n\nlet five = Rc::new(5);\nlet same_five = Rc::clone(&five);\nlet other_five = Rc::new(5);\n\nassert!(Rc::ptr_eq(&five, &same_five));\nassert!(!Rc::ptr_eq(&five, &other_five));
Makes a mutable reference into the given Rc
.
If there are other Rc
pointers to the same allocation, then make_mut
will\nclone
the inner value to a new allocation to ensure unique ownership. This is also\nreferred to as clone-on-write.
However, if there are no other Rc
pointers to this allocation, but some Weak
\npointers, then the Weak
pointers will be disassociated and the inner value will not\nbe cloned.
See also get_mut
, which will fail rather than cloning the inner value\nor disassociating Weak
pointers.
use std::rc::Rc;\n\nlet mut data = Rc::new(5);\n\n*Rc::make_mut(&mut data) += 1; // Won't clone anything\nlet mut other_data = Rc::clone(&data); // Won't clone inner data\n*Rc::make_mut(&mut data) += 1; // Clones inner data\n*Rc::make_mut(&mut data) += 1; // Won't clone anything\n*Rc::make_mut(&mut other_data) *= 2; // Won't clone anything\n\n// Now `data` and `other_data` point to different allocations.\nassert_eq!(*data, 8);\nassert_eq!(*other_data, 12);
Weak
pointers will be disassociated:
use std::rc::Rc;\n\nlet mut data = Rc::new(75);\nlet weak = Rc::downgrade(&data);\n\nassert!(75 == *data);\nassert!(75 == *weak.upgrade().unwrap());\n\n*Rc::make_mut(&mut data) += 1;\n\nassert!(76 == *data);\nassert!(weak.upgrade().is_none());
If we have the only reference to T
then unwrap it. Otherwise, clone T
and return the\nclone.
Assuming rc_t
is of type Rc<T>
, this function is functionally equivalent to\n(*rc_t).clone()
, but will avoid cloning the inner value where possible.
let inner = String::from(\"test\");\nlet ptr = inner.as_ptr();\n\nlet rc = Rc::new(inner);\nlet inner = Rc::unwrap_or_clone(rc);\n// The inner value was not cloned\nassert!(ptr::eq(ptr, inner.as_ptr()));\n\nlet rc = Rc::new(inner);\nlet rc2 = rc.clone();\nlet inner = Rc::unwrap_or_clone(rc);\n// Because there were 2 references, we had to clone the inner value.\nassert!(!ptr::eq(ptr, inner.as_ptr()));\n// `rc2` is the last reference, so when we unwrap it we get back\n// the original `String`.\nlet inner = Rc::unwrap_or_clone(rc2);\nassert!(ptr::eq(ptr, inner.as_ptr()));
allocator_api
)Returns a reference to the underlying allocator.
\nNote: this is an associated function, which means that you have\nto call it as Rc::allocator(&r)
instead of r.allocator()
. This\nis so that there is no conflict with a method on the inner type.
allocator_api
)Constructs a new Rc
in the provided allocator.
#![feature(allocator_api)]\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet five = Rc::new_in(5, System);
allocator_api
)Constructs a new Rc
with uninitialized contents in the provided allocator.
#![feature(new_uninit)]\n#![feature(get_mut_unchecked)]\n#![feature(allocator_api)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet mut five = Rc::<u32, _>::new_uninit_in(System);\n\nlet five = unsafe {\n // Deferred initialization:\n Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
allocator_api
)Constructs a new Rc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, in the provided allocator.
See MaybeUninit::zeroed
for examples of correct and\nincorrect usage of this method.
#![feature(new_uninit)]\n#![feature(allocator_api)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet zero = Rc::<u32, _>::new_zeroed_in(System);\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
allocator_api
)Constructs a new Rc<T>
in the provided allocator, returning an error if the allocation\nfails
#![feature(allocator_api)]\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet five = Rc::try_new_in(5, System);
allocator_api
)Constructs a new Rc
with uninitialized contents, in the provided allocator, returning an\nerror if the allocation fails
#![feature(allocator_api, new_uninit)]\n#![feature(get_mut_unchecked)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet mut five = Rc::<u32, _>::try_new_uninit_in(System)?;\n\nlet five = unsafe {\n // Deferred initialization:\n Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Rc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, in the provided allocator, returning an error if the allocation\nfails
See MaybeUninit::zeroed
for examples of correct and\nincorrect usage of this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::rc::Rc;\nuse std::alloc::System;\n\nlet zero = Rc::<u32, _>::try_new_zeroed_in(System)?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Constructs a new Pin<Rc<T>>
in the provided allocator. If T
does not implement Unpin
, then\nvalue
will be pinned in memory and unable to be moved.
Returns the inner value, if the Rc
has exactly one strong reference.
Otherwise, an Err
is returned with the same Rc
that was\npassed in.
This will succeed even if there are outstanding weak references.
\nuse std::rc::Rc;\n\nlet x = Rc::new(3);\nassert_eq!(Rc::try_unwrap(x), Ok(3));\n\nlet x = Rc::new(4);\nlet _y = Rc::clone(&x);\nassert_eq!(*Rc::try_unwrap(x).unwrap_err(), 4);
Returns the inner value, if the Rc
has exactly one strong reference.
Otherwise, None
is returned and the Rc
is dropped.
This will succeed even if there are outstanding weak references.
\nIf Rc::into_inner
is called on every clone of this Rc
,\nit is guaranteed that exactly one of the calls returns the inner value.\nThis means in particular that the inner value is not dropped.
Rc::try_unwrap
is conceptually similar to Rc::into_inner
.\nAnd while they are meant for different use-cases, Rc::into_inner(this)
\nis in fact equivalent to Rc::try_unwrap(this).ok()
.\n(Note that the same kind of equivalence does not hold true for\nArc
, due to race conditions that do not apply to Rc
!)
use std::rc::Rc;\n\nlet x = Rc::new(3);\nassert_eq!(Rc::into_inner(x), Some(3));\n\nlet x = Rc::new(4);\nlet y = Rc::clone(&x);\n\nassert_eq!(Rc::into_inner(y), None);\nassert_eq!(Rc::into_inner(x), Some(4));
Service
created by this factory.Service
instance.gTransformService
value created by this factoryasync_iterator
)async_iterator
)None
if the async iterator is exhausted. Read morecoroutine_trait
)Converts a Box<T>
into a Pin<Box<T>>
. If T
does not implement Unpin
, then\n*boxed
will be pinned in memory and unable to be moved.
This conversion does not allocate on the heap and happens in place.
\nThis is also available via Box::into_pin
.
Constructing and pinning a Box
with <Pin<Box<T>>>::from(Box::new(x))
\ncan also be written more concisely using Box::pin(x)
.\nThis From
implementation is useful if you already have a Box<T>
, or you are\nconstructing a (pinned) Box
in a different way than with Box::new
.
true
if the underlying future should no longer be polled.true
if the stream should no longer be polled.Construct a new Pin<Ptr>
around a pointer to some data of a type that\nimplements Unpin
.
Unlike Pin::new_unchecked
, this method is safe because the pointer\nPtr
dereferences to an Unpin
type, which cancels the pinning guarantees.
use std::pin::Pin;\n\nlet mut val: u8 = 5;\n\n// Since `val` doesn't care about being moved, we can safely create a \"facade\" `Pin`\n// which will allow `val` to participate in `Pin`-bound apis without checking that\n// pinning guarantees are actually upheld.\nlet mut pinned: Pin<&mut u8> = Pin::new(&mut val);
Unwraps this Pin<Ptr>
, returning the underlying pointer.
Doing this operation safely requires that the data pointed at by this pinning pointer\nimplements Unpin
so that we can ignore the pinning invariants when unwrapping it.
use std::pin::Pin;\n\nlet mut val: u8 = 5;\nlet pinned: Pin<&mut u8> = Pin::new(&mut val);\n\n// Unwrap the pin to get the underlying mutable reference to the value. We can do\n// this because `val` doesn't care about being moved, so the `Pin` was just\n// a \"facade\" anyway.\nlet r = Pin::into_inner(pinned);\nassert_eq!(*r, 5);
Construct a new Pin<Ptr>
around a reference to some data of a type that\nmay or may not implement Unpin
.
If pointer
dereferences to an Unpin
type, Pin::new
should be used\ninstead.
This constructor is unsafe because we cannot guarantee that the data\npointed to by pointer
is pinned. At its core, pinning a value means making the\nguarantee that the value’s data will not be moved nor have its storage invalidated until\nit gets dropped. For a more thorough explanation of pinning, see the pin
module docs.
If the caller that is constructing this Pin<Ptr>
does not ensure that the data Ptr
\npoints to is pinned, that is a violation of the API contract and may lead to undefined\nbehavior in later (even safe) operations.
By using this method, you are also making a promise about the Deref
and\nDerefMut
implementations of Ptr
, if they exist. Most importantly, they\nmust not move out of their self
arguments: Pin::as_mut
and Pin::as_ref
\nwill call DerefMut::deref_mut
and Deref::deref
on the pointer type Ptr
\nand expect these methods to uphold the pinning invariants.\nMoreover, by calling this method you promise that the reference Ptr
\ndereferences to will not be moved out of again; in particular, it\nmust not be possible to obtain a &mut Ptr::Target
and then\nmove out of that reference (using, for example mem::swap
).
For example, calling Pin::new_unchecked
on an &'a mut T
is unsafe because\nwhile you are able to pin it for the given lifetime 'a
, you have no control\nover whether it is kept pinned once 'a
ends, and therefore cannot uphold the\nguarantee that a value, once pinned, remains pinned until it is dropped:
use std::mem;\nuse std::pin::Pin;\n\nfn move_pinned_ref<T>(mut a: T, mut b: T) {\n unsafe {\n let p: Pin<&mut T> = Pin::new_unchecked(&mut a);\n // This should mean the pointee `a` can never move again.\n }\n mem::swap(&mut a, &mut b); // Potential UB down the road ⚠️\n // The address of `a` changed to `b`'s stack slot, so `a` got moved even\n // though we have previously pinned it! We have violated the pinning API contract.\n}
A value, once pinned, must remain pinned until it is dropped (unless its type implements\nUnpin
). Because Pin<&mut T>
does not own the value, dropping the Pin
will not drop\nthe value and will not end the pinning contract. So moving the value after dropping the\nPin<&mut T>
is still a violation of the API contract.
Similarly, calling Pin::new_unchecked
on an Rc<T>
is unsafe because there could be\naliases to the same data that are not subject to the pinning restrictions:
use std::rc::Rc;\nuse std::pin::Pin;\n\nfn move_pinned_rc<T>(mut x: Rc<T>) {\n // This should mean the pointee can never move again.\n let pin = unsafe { Pin::new_unchecked(Rc::clone(&x)) };\n {\n let p: Pin<&T> = pin.as_ref();\n // ...\n }\n drop(pin);\n\n let content = Rc::get_mut(&mut x).unwrap(); // Potential UB down the road ⚠️\n // Now, if `x` was the only reference, we have a mutable reference to\n // data that we pinned above, which we could use to move it as we have\n // seen in the previous example. We have violated the pinning API contract.\n }
Particular care is required when using Pin::new_unchecked
in a closure:\nPin::new_unchecked(&mut var)
where var
is a by-value (moved) closure capture\nimplicitly makes the promise that the closure itself is pinned, and that all uses\nof this closure capture respect that pinning.
use std::pin::Pin;\nuse std::task::Context;\nuse std::future::Future;\n\nfn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) {\n // Create a closure that moves `x`, and then internally uses it in a pinned way.\n let mut closure = move || unsafe {\n let _ignore = Pin::new_unchecked(&mut x).poll(cx);\n };\n // Call the closure, so the future can assume it has been pinned.\n closure();\n // Move the closure somewhere else. This also moves `x`!\n let mut moved = closure;\n // Calling it again means we polled the future from two different locations,\n // violating the pinning API contract.\n moved(); // Potential UB ⚠️\n}
When passing a closure to another API, it might be moving the closure any time, so\nPin::new_unchecked
on closure captures may only be used if the API explicitly documents\nthat the closure is pinned.
The better alternative is to avoid all that trouble and do the pinning in the outer function\ninstead (here using the pin!
macro):
use std::pin::pin;\nuse std::task::Context;\nuse std::future::Future;\n\nfn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) {\n let mut x = pin!(x);\n // Create a closure that captures `x: Pin<&mut _>`, which is safe to move.\n let mut closure = move || {\n let _ignore = x.as_mut().poll(cx);\n };\n // Call the closure, so the future can assume it has been pinned.\n closure();\n // Move the closure somewhere else.\n let mut moved = closure;\n // Calling it again here is fine (except that we might be polling a future that already\n // returned `Poll::Ready`, but that is a separate problem).\n moved();\n}
Gets a shared reference to the pinned value this Pin
points to.
This is a generic method to go from &Pin<Pointer<T>>
to Pin<&T>
.\nIt is safe because, as part of the contract of Pin::new_unchecked
,\nthe pointee cannot move after Pin<Pointer<T>>
got created.\n“Malicious” implementations of Pointer::Deref
are likewise\nruled out by the contract of Pin::new_unchecked
.
Unwraps this Pin<Ptr>
, returning the underlying Ptr
.
This function is unsafe. You must guarantee that you will continue to\ntreat the pointer Ptr
as pinned after you call this function, so that\nthe invariants on the Pin
type can be upheld. If the code using the\nresulting Ptr
does not continue to maintain the pinning invariants that\nis a violation of the API contract and may lead to undefined behavior in\nlater (safe) operations.
Note that you must be able to guarantee that the data pointed to by Ptr
\nwill be treated as pinned all the way until its drop
handler is complete!
For more information, see the pin
module docs
If the underlying data is Unpin
, Pin::into_inner
should be used\ninstead.
Gets a mutable reference to the pinned value this Pin<Ptr>
points to.
This is a generic method to go from &mut Pin<Pointer<T>>
to Pin<&mut T>
.\nIt is safe because, as part of the contract of Pin::new_unchecked
,\nthe pointee cannot move after Pin<Pointer<T>>
got created.\n“Malicious” implementations of Pointer::DerefMut
are likewise\nruled out by the contract of Pin::new_unchecked
.
This method is useful when doing multiple calls to functions that consume the\npinning pointer.
\nuse std::pin::Pin;\n\nimpl Type {\n fn method(self: Pin<&mut Self>) {\n // do something\n }\n\n fn call_method_twice(mut self: Pin<&mut Self>) {\n // `method` consumes `self`, so reborrow the `Pin<&mut Self>` via `as_mut`.\n self.as_mut().method();\n self.as_mut().method();\n }\n}
Assigns a new value to the memory location pointed to by the Pin<Ptr>
.
This overwrites pinned data, but that is okay: the original pinned value’s destructor gets\nrun before being overwritten and the new value is also a valid value of the same type, so\nno pinning invariant is violated. See the pin
module documentation\nfor more information on how this upholds the pinning invariants.
use std::pin::Pin;\n\nlet mut val: u8 = 5;\nlet mut pinned: Pin<&mut u8> = Pin::new(&mut val);\nprintln!(\"{}\", pinned); // 5\npinned.set(10);\nprintln!(\"{}\", pinned); // 10