fix payload documentation

This commit is contained in:
Rob Ede 2021-12-24 10:30:51 +00:00
parent 02ae28581a
commit 8c7e4cd6d5
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
7 changed files with 69 additions and 59 deletions

View File

@ -1,9 +1,12 @@
//! Payload stream
use std::cell::RefCell;
use std::collections::VecDeque;
use std::pin::Pin;
use std::rc::{Rc, Weak};
use std::task::{Context, Poll, Waker};
use std::{
cell::RefCell,
collections::VecDeque,
pin::Pin,
rc::{Rc, Weak},
task::{Context, Poll, Waker},
};
use bytes::Bytes;
use futures_core::Stream;
@ -22,39 +25,32 @@ pub enum PayloadStatus {
/// Buffered stream of bytes chunks
///
/// Payload stores chunks in a vector. First chunk can be received with `.readany()` method.
/// Payload stream is not thread safe. Payload does not notify current task when new data
/// is available.
/// Payload stores chunks in a vector. First chunk can be received with `poll_next`. Payload does
/// not notify current task when new data is available.
///
/// Payload stream can be used as `Response` body stream.
/// Payload can be used as `Response` body stream.
#[derive(Debug)]
pub struct Payload {
inner: Rc<RefCell<Inner>>,
}
impl Payload {
/// Create payload stream.
/// Creates a payload stream.
///
/// This method construct two objects responsible for bytes stream
/// generation.
///
/// * `PayloadSender` - *Sender* side of the stream
///
/// * `Payload` - *Receiver* side of the stream
/// This method construct two objects responsible for bytes stream generation:
/// - `PayloadSender` - *Sender* side of the stream
/// - `Payload` - *Receiver* side of the stream
pub fn create(eof: bool) -> (PayloadSender, Payload) {
let shared = Rc::new(RefCell::new(Inner::new(eof)));
(
PayloadSender {
inner: Rc::downgrade(&shared),
},
PayloadSender::new(Rc::downgrade(&shared)),
Payload { inner: shared },
)
}
/// Create empty payload
#[doc(hidden)]
pub fn empty() -> Payload {
/// Creates an empty payload.
pub(crate) fn empty() -> Payload {
Payload {
inner: Rc::new(RefCell::new(Inner::new(true))),
}
@ -86,7 +82,7 @@ impl Stream for Payload {
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, PayloadError>>> {
self.inner.borrow_mut().readany(cx)
Pin::new(&mut *self.inner.borrow_mut()).poll_next(cx)
}
}
@ -96,6 +92,10 @@ pub struct PayloadSender {
}
impl PayloadSender {
fn new(inner: Weak<RefCell<Inner>>) -> Self {
Self { inner }
}
#[inline]
pub fn set_error(&mut self, err: PayloadError) {
if let Some(shared) = self.inner.upgrade() {
@ -219,7 +219,10 @@ impl Inner {
self.len
}
fn readany(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, PayloadError>>> {
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, PayloadError>>> {
if let Some(data) = self.items.pop_front() {
self.len -= data.len();
self.need_read = self.len < MAX_BUFFER_SIZE;
@ -249,12 +252,18 @@ impl Inner {
#[cfg(test)]
mod tests {
use std::panic::{RefUnwindSafe, UnwindSafe};
use actix_utils::future::poll_fn;
use static_assertions::assert_impl_all;
use static_assertions::{assert_impl_all, assert_not_impl_any};
use super::*;
assert_impl_all!(Payload: Unpin);
assert_not_impl_any!(Payload: Send, Sync, UnwindSafe, RefUnwindSafe);
assert_impl_all!(Inner: Unpin, Send, Sync);
assert_not_impl_any!(Inner: UnwindSafe, RefUnwindSafe);
#[actix_rt::test]
async fn test_unread_data() {

View File

@ -23,7 +23,7 @@ pin_project_lite::pin_project! {
H1 { payload: crate::h1::Payload },
H2 { payload: crate::h2::Payload },
Stream { #[pin] payload: S },
}
}
}
impl<S> From<crate::h1::Payload> for Payload<S> {

View File

@ -120,7 +120,7 @@ impl TestRequest {
}
/// Set request payload.
pub fn set_payload<B: Into<Bytes>>(&mut self, data: B) -> &mut Self {
pub fn set_payload(&mut self, data: impl Into<Bytes>) -> &mut Self {
let mut payload = crate::h1::Payload::empty();
payload.unread_data(data.into());
parts(&mut self.0).payload = Some(payload.into());

View File

@ -3,8 +3,11 @@
## Unreleased - 2021-xx-xx
- Rename `Connector::{ssl => openssl}`. [#2503]
- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503]
- `ClientResponse` is no longer `Unpin`. [#2545]
- `impl Stream` for `ClientResponse` no longer requires the body type be `Unpin`. [#2545]
[#2503]: https://github.com/actix/actix-web/pull/2503
[#2545]: https://github.com/actix/actix-web/pull/2545
## 3.0.0-beta.14 - 2021-12-17

View File

@ -65,7 +65,7 @@ impl TestResponse {
/// Set response's payload
pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
let mut payload = h1::Payload::empty();
let (_, mut payload) = h1::Payload::create(true);
payload.unread_data(data.into());
self.payload = Some(payload.into());
self
@ -90,7 +90,8 @@ impl TestResponse {
if let Some(pl) = self.payload {
ClientResponse::new(head, pl)
} else {
ClientResponse::new(head, h1::Payload::empty().into())
let (_, payload) = h1::Payload::create(true);
ClientResponse::new(head, payload.into())
}
}
}

View File

@ -429,9 +429,12 @@ mod tests {
use actix_http::body;
use super::*;
use crate::http::{
header::{self, HeaderValue, CONTENT_TYPE},
StatusCode,
use crate::{
http::{
header::{self, HeaderValue, CONTENT_TYPE},
StatusCode,
},
test::assert_body_eq,
};
#[test]
@ -472,32 +475,23 @@ mod tests {
#[actix_rt::test]
async fn test_json() {
let resp = HttpResponse::Ok().json(vec!["v1", "v2", "v3"]);
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
let res = HttpResponse::Ok().json(vec!["v1", "v2", "v3"]);
let ct = res.headers().get(CONTENT_TYPE).unwrap();
assert_eq!(ct, HeaderValue::from_static("application/json"));
assert_eq!(
body::to_bytes(resp.into_body()).await.unwrap().as_ref(),
br#"["v1","v2","v3"]"#
);
assert_body_eq!(res, br#"["v1","v2","v3"]"#);
let resp = HttpResponse::Ok().json(&["v1", "v2", "v3"]);
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
let res = HttpResponse::Ok().json(&["v1", "v2", "v3"]);
let ct = res.headers().get(CONTENT_TYPE).unwrap();
assert_eq!(ct, HeaderValue::from_static("application/json"));
assert_eq!(
body::to_bytes(resp.into_body()).await.unwrap().as_ref(),
br#"["v1","v2","v3"]"#
);
assert_body_eq!(res, br#"["v1","v2","v3"]"#);
// content type override
let resp = HttpResponse::Ok()
let res = HttpResponse::Ok()
.insert_header((CONTENT_TYPE, "text/json"))
.json(&vec!["v1", "v2", "v3"]);
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
let ct = res.headers().get(CONTENT_TYPE).unwrap();
assert_eq!(ct, HeaderValue::from_static("text/json"));
assert_eq!(
body::to_bytes(resp.into_body()).await.unwrap().as_ref(),
br#"["v1","v2","v3"]"#
);
assert_body_eq!(res, br#"["v1","v2","v3"]"#);
}
#[actix_rt::test]

View File

@ -174,25 +174,28 @@ impl TestRequest {
}
/// Set request payload.
pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
pub fn set_payload(mut self, data: impl Into<Bytes>) -> Self {
self.req.set_payload(data);
self
}
/// Serialize `data` to a URL encoded form and set it as the request payload. The `Content-Type`
/// header is set to `application/x-www-form-urlencoded`.
pub fn set_form<T: Serialize>(mut self, data: &T) -> Self {
let bytes = serde_urlencoded::to_string(data)
/// Serialize `data` to a URL encoded form and set it as the request payload.
///
/// The `Content-Type` header is set to `application/x-www-form-urlencoded`.
pub fn set_form(mut self, data: impl Serialize) -> Self {
let bytes = serde_urlencoded::to_string(&data)
.expect("Failed to serialize test data as a urlencoded form");
self.req.set_payload(bytes);
self.req.insert_header(ContentType::form_url_encoded());
self
}
/// Serialize `data` to JSON and set it as the request payload. The `Content-Type` header is
/// set to `application/json`.
pub fn set_json<T: Serialize>(mut self, data: &T) -> Self {
let bytes = serde_json::to_string(data).expect("Failed to serialize test data to json");
/// Serialize `data` to JSON and set it as the request payload.
///
/// The `Content-Type` header is set to `application/json`.
pub fn set_json(mut self, data: impl Serialize) -> Self {
let bytes =
serde_json::to_string(&data).expect("Failed to serialize test data to json");
self.req.set_payload(bytes);
self.req.insert_header(ContentType::json());
self