Merge remote-tracking branch 'upstream/main' into conduwuit-changes
This commit is contained in:
commit
9900d06765
@ -23,7 +23,9 @@ use ruma_events::{
|
|||||||
};
|
};
|
||||||
use serde::{de::Error as _, Deserialize, Serialize};
|
use serde::{de::Error as _, Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{v5, DeviceLists, UnreadNotificationsCount};
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
|
use super::v5;
|
||||||
|
use super::{DeviceLists, UnreadNotificationsCount};
|
||||||
|
|
||||||
const METADATA: Metadata = metadata! {
|
const METADATA: Metadata = metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
@ -928,6 +930,7 @@ impl Typing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::Request> for Request {
|
impl From<v5::Request> for Request {
|
||||||
fn from(value: v5::Request) -> Self {
|
fn from(value: v5::Request) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -952,6 +955,7 @@ impl From<v5::Request> for Request {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::List> for SyncRequestList {
|
impl From<v5::request::List> for SyncRequestList {
|
||||||
fn from(value: v5::request::List) -> Self {
|
fn from(value: v5::request::List) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -974,12 +978,14 @@ impl From<v5::request::List> for SyncRequestList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::RoomDetails> for RoomDetailsConfig {
|
impl From<v5::request::RoomDetails> for RoomDetailsConfig {
|
||||||
fn from(value: v5::request::RoomDetails) -> Self {
|
fn from(value: v5::request::RoomDetails) -> Self {
|
||||||
Self { required_state: value.required_state, timeline_limit: value.timeline_limit }
|
Self { required_state: value.required_state, timeline_limit: value.timeline_limit }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::ListFilters> for SyncRequestListFilters {
|
impl From<v5::request::ListFilters> for SyncRequestListFilters {
|
||||||
fn from(value: v5::request::ListFilters) -> Self {
|
fn from(value: v5::request::ListFilters) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -990,6 +996,7 @@ impl From<v5::request::ListFilters> for SyncRequestListFilters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::RoomSubscription> for RoomSubscription {
|
impl From<v5::request::RoomSubscription> for RoomSubscription {
|
||||||
fn from(value: v5::request::RoomSubscription) -> Self {
|
fn from(value: v5::request::RoomSubscription) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -1000,6 +1007,7 @@ impl From<v5::request::RoomSubscription> for RoomSubscription {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::Extensions> for ExtensionsConfig {
|
impl From<v5::request::Extensions> for ExtensionsConfig {
|
||||||
fn from(value: v5::request::Extensions) -> Self {
|
fn from(value: v5::request::Extensions) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -1014,6 +1022,7 @@ impl From<v5::request::Extensions> for ExtensionsConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::ToDevice> for ToDeviceConfig {
|
impl From<v5::request::ToDevice> for ToDeviceConfig {
|
||||||
fn from(value: v5::request::ToDevice) -> Self {
|
fn from(value: v5::request::ToDevice) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -1026,18 +1035,21 @@ impl From<v5::request::ToDevice> for ToDeviceConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::E2EE> for E2EEConfig {
|
impl From<v5::request::E2EE> for E2EEConfig {
|
||||||
fn from(value: v5::request::E2EE) -> Self {
|
fn from(value: v5::request::E2EE) -> Self {
|
||||||
Self { enabled: value.enabled }
|
Self { enabled: value.enabled }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::AccountData> for AccountDataConfig {
|
impl From<v5::request::AccountData> for AccountDataConfig {
|
||||||
fn from(value: v5::request::AccountData) -> Self {
|
fn from(value: v5::request::AccountData) -> Self {
|
||||||
Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }
|
Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::Receipts> for ReceiptsConfig {
|
impl From<v5::request::Receipts> for ReceiptsConfig {
|
||||||
fn from(value: v5::request::Receipts) -> Self {
|
fn from(value: v5::request::Receipts) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -1048,6 +1060,7 @@ impl From<v5::request::Receipts> for ReceiptsConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::ReceiptsRoom> for RoomReceiptConfig {
|
impl From<v5::request::ReceiptsRoom> for RoomReceiptConfig {
|
||||||
fn from(value: v5::request::ReceiptsRoom) -> Self {
|
fn from(value: v5::request::ReceiptsRoom) -> Self {
|
||||||
match value {
|
match value {
|
||||||
@ -1057,6 +1070,7 @@ impl From<v5::request::ReceiptsRoom> for RoomReceiptConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc4186")]
|
||||||
impl From<v5::request::Typing> for TypingConfig {
|
impl From<v5::request::Typing> for TypingConfig {
|
||||||
fn from(value: v5::request::Typing) -> Self {
|
fn from(value: v5::request::Typing) -> Self {
|
||||||
Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }
|
Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }
|
||||||
|
@ -18,6 +18,11 @@ Improvements:
|
|||||||
- Stabilize support for muting in VoIP calls, according to Matrix 1.11
|
- Stabilize support for muting in VoIP calls, according to Matrix 1.11
|
||||||
- All the root `Any*EventContent` types now have a `EventContentFromType` implementations
|
- All the root `Any*EventContent` types now have a `EventContentFromType` implementations
|
||||||
automatically derived by the `event_enum!` macro.
|
automatically derived by the `event_enum!` macro.
|
||||||
|
- `CallMemberEventContent` now supports two different formats: Session memberships and Legacy memberships.
|
||||||
|
The new format (Session) is required to reliably display the call member count (reliable call member events).
|
||||||
|
`CallMemberEventContent` is now an enum to model the two different formats.
|
||||||
|
- `CallMemberStateKey` (instead of `OwnedUserId`) is now used as the state key type for `CallMemberEventContent`.
|
||||||
|
This guarantees correct formatting of the event key.
|
||||||
|
|
||||||
Breaking changes:
|
Breaking changes:
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ mod member_state_key;
|
|||||||
pub use focus::*;
|
pub use focus::*;
|
||||||
pub use member_data::*;
|
pub use member_data::*;
|
||||||
pub use member_state_key::*;
|
pub use member_state_key::*;
|
||||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
use ruma_common::{MilliSecondsSinceUnixEpoch, OwnedDeviceId};
|
||||||
use ruma_macros::{EventContent, StringEnum};
|
use ruma_macros::{EventContent, StringEnum};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ impl CallMemberEventContent {
|
|||||||
/// Creates a new [`CallMemberEventContent`] with [`SessionMembershipData`].
|
/// Creates a new [`CallMemberEventContent`] with [`SessionMembershipData`].
|
||||||
pub fn new(
|
pub fn new(
|
||||||
application: Application,
|
application: Application,
|
||||||
device_id: String,
|
device_id: OwnedDeviceId,
|
||||||
focus_active: ActiveFocus,
|
focus_active: ActiveFocus,
|
||||||
foci_preferred: Vec<Focus>,
|
foci_preferred: Vec<Focus>,
|
||||||
created_ts: Option<MilliSecondsSinceUnixEpoch>,
|
created_ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||||
@ -234,8 +234,8 @@ mod tests {
|
|||||||
|
|
||||||
use assert_matches2::assert_matches;
|
use assert_matches2::assert_matches;
|
||||||
use ruma_common::{
|
use ruma_common::{
|
||||||
device_id, user_id, MilliSecondsSinceUnixEpoch as TS, OwnedEventId, OwnedRoomId,
|
device_id, owned_device_id, user_id, MilliSecondsSinceUnixEpoch as TS, OwnedEventId,
|
||||||
OwnedUserId,
|
OwnedRoomId, OwnedUserId,
|
||||||
};
|
};
|
||||||
use serde_json::{from_value as from_json_value, json, Value as JsonValue};
|
use serde_json::{from_value as from_json_value, json, Value as JsonValue};
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ mod tests {
|
|||||||
call_id: "123456".to_owned(),
|
call_id: "123456".to_owned(),
|
||||||
scope: CallScope::Room,
|
scope: CallScope::Room,
|
||||||
}),
|
}),
|
||||||
device_id: "ABCDE".to_owned(),
|
device_id: owned_device_id!("ABCDE"),
|
||||||
expires: Duration::from_secs(3600),
|
expires: Duration::from_secs(3600),
|
||||||
foci_active: vec![Focus::Livekit(LivekitFocus {
|
foci_active: vec![Focus::Livekit(LivekitFocus {
|
||||||
alias: "1".to_owned(),
|
alias: "1".to_owned(),
|
||||||
@ -274,7 +274,7 @@ mod tests {
|
|||||||
call_id: "123456".to_owned(),
|
call_id: "123456".to_owned(),
|
||||||
scope: CallScope::Room,
|
scope: CallScope::Room,
|
||||||
}),
|
}),
|
||||||
"ABCDE".to_owned(),
|
owned_device_id!("ABCDE"),
|
||||||
ActiveFocus::Livekit(ActiveLivekitFocus {
|
ActiveFocus::Livekit(ActiveLivekitFocus {
|
||||||
focus_selection: FocusSelection::OldestMembership,
|
focus_selection: FocusSelection::OldestMembership,
|
||||||
}),
|
}),
|
||||||
@ -354,7 +354,7 @@ mod tests {
|
|||||||
call_id: "123456".to_owned(),
|
call_id: "123456".to_owned(),
|
||||||
scope: CallScope::Room,
|
scope: CallScope::Room,
|
||||||
}),
|
}),
|
||||||
"THIS_DEVICE".to_owned(),
|
owned_device_id!("THIS_DEVICE"),
|
||||||
ActiveFocus::Livekit(ActiveLivekitFocus {
|
ActiveFocus::Livekit(ActiveLivekitFocus {
|
||||||
focus_selection: FocusSelection::OldestMembership,
|
focus_selection: FocusSelection::OldestMembership,
|
||||||
}),
|
}),
|
||||||
@ -404,7 +404,7 @@ mod tests {
|
|||||||
call_id: "123456".to_owned(),
|
call_id: "123456".to_owned(),
|
||||||
scope: CallScope::Room,
|
scope: CallScope::Room,
|
||||||
}),
|
}),
|
||||||
device_id: "THIS_DEVICE".to_owned(),
|
device_id: owned_device_id!("THIS_DEVICE"),
|
||||||
expires: Duration::from_secs(3600),
|
expires: Duration::from_secs(3600),
|
||||||
foci_active: vec![Focus::Livekit(LivekitFocus {
|
foci_active: vec![Focus::Livekit(LivekitFocus {
|
||||||
alias: "room1".to_owned(),
|
alias: "room1".to_owned(),
|
||||||
@ -418,7 +418,7 @@ mod tests {
|
|||||||
call_id: "".to_owned(),
|
call_id: "".to_owned(),
|
||||||
scope: CallScope::Room,
|
scope: CallScope::Room,
|
||||||
}),
|
}),
|
||||||
device_id: "OTHER_DEVICE".to_owned(),
|
device_id: owned_device_id!("OTHER_DEVICE"),
|
||||||
expires: Duration::from_secs(3600),
|
expires: Duration::from_secs(3600),
|
||||||
foci_active: vec![Focus::Livekit(LivekitFocus {
|
foci_active: vec![Focus::Livekit(LivekitFocus {
|
||||||
alias: "room2".to_owned(),
|
alias: "room2".to_owned(),
|
||||||
@ -526,7 +526,7 @@ mod tests {
|
|||||||
call_id: "".to_owned(),
|
call_id: "".to_owned(),
|
||||||
scope: CallScope::Room,
|
scope: CallScope::Room,
|
||||||
}),
|
}),
|
||||||
device_id: "THIS_DEVICE".to_owned(),
|
device_id: owned_device_id!("THIS_DEVICE"),
|
||||||
foci_preferred: [Focus::Livekit(LivekitFocus {
|
foci_preferred: [Focus::Livekit(LivekitFocus {
|
||||||
alias: "room1".to_owned(),
|
alias: "room1".to_owned(),
|
||||||
service_url: "https://livekit1.com".to_owned(),
|
service_url: "https://livekit1.com".to_owned(),
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use as_variant::as_variant;
|
use as_variant::as_variant;
|
||||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
use ruma_common::{DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId};
|
||||||
use ruma_macros::StringEnum;
|
use ruma_macros::StringEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
@ -41,7 +41,7 @@ impl<'a> MembershipData<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The device id of this membership.
|
/// The device id of this membership.
|
||||||
pub fn device_id(&self) -> &String {
|
pub fn device_id(&self) -> &DeviceId {
|
||||||
match self {
|
match self {
|
||||||
MembershipData::Legacy(data) => &data.device_id,
|
MembershipData::Legacy(data) => &data.device_id,
|
||||||
MembershipData::Session(data) => &data.device_id,
|
MembershipData::Session(data) => &data.device_id,
|
||||||
@ -121,7 +121,7 @@ pub struct LegacyMembershipData {
|
|||||||
/// The device id of this membership.
|
/// The device id of this membership.
|
||||||
///
|
///
|
||||||
/// The same user can join with their phone/computer.
|
/// The same user can join with their phone/computer.
|
||||||
pub device_id: String,
|
pub device_id: OwnedDeviceId,
|
||||||
|
|
||||||
/// The duration in milliseconds relative to the time this membership joined
|
/// The duration in milliseconds relative to the time this membership joined
|
||||||
/// during which the membership is valid.
|
/// during which the membership is valid.
|
||||||
@ -190,7 +190,7 @@ pub struct LegacyMembershipDataInit {
|
|||||||
/// The device id of this membership.
|
/// The device id of this membership.
|
||||||
///
|
///
|
||||||
/// The same user can join with their phone/computer.
|
/// The same user can join with their phone/computer.
|
||||||
pub device_id: String,
|
pub device_id: OwnedDeviceId,
|
||||||
|
|
||||||
/// The duration in milliseconds relative to the time this membership joined
|
/// The duration in milliseconds relative to the time this membership joined
|
||||||
/// during which the membership is valid.
|
/// during which the membership is valid.
|
||||||
@ -236,7 +236,7 @@ pub struct SessionMembershipData {
|
|||||||
/// The device id of this membership.
|
/// The device id of this membership.
|
||||||
///
|
///
|
||||||
/// The same user can join with their phone/computer.
|
/// The same user can join with their phone/computer.
|
||||||
pub device_id: String,
|
pub device_id: OwnedDeviceId,
|
||||||
|
|
||||||
/// A list of the foci that this membership proposes to use.
|
/// A list of the foci that this membership proposes to use.
|
||||||
pub foci_preferred: Vec<Focus>,
|
pub foci_preferred: Vec<Focus>,
|
||||||
|
@ -16,11 +16,11 @@ pub struct CallMemberStateKey {
|
|||||||
|
|
||||||
impl CallMemberStateKey {
|
impl CallMemberStateKey {
|
||||||
/// Constructs a new CallMemberStateKey there are three possible formats:
|
/// Constructs a new CallMemberStateKey there are three possible formats:
|
||||||
/// - "_{UserId}_{DeviceId}" example: "_@test:user.org_DEVICE". `device_id`: Some`, `underscore:
|
/// - `_{UserId}_{DeviceId}` example: `_@test:user.org_DEVICE`. `device_id: Some`, `underscore:
|
||||||
/// true`
|
/// true`
|
||||||
/// - "{UserId}_{DeviceId}" example: "@test:user.org_DEVICE". `device_id`: Some`, `underscore:
|
/// - `{UserId}_{DeviceId}` example: `@test:user.org_DEVICE`. `device_id: Some`, `underscore:
|
||||||
/// false`
|
/// false`
|
||||||
/// - "{UserId}" example example: "@test:user.org". `device_id`: None`, underscore is ignored:
|
/// - `{UserId}` example: `@test:user.org`. `device_id: None`, underscore is ignored:
|
||||||
/// `underscore: false|true`
|
/// `underscore: false|true`
|
||||||
///
|
///
|
||||||
/// Dependent on the parameters the correct CallMemberStateKey will be constructed.
|
/// Dependent on the parameters the correct CallMemberStateKey will be constructed.
|
||||||
|
@ -174,12 +174,20 @@ fn try_from_multipart_mixed_response<T: AsRef<[u8]>>(
|
|||||||
let mut full_boundary = Vec::with_capacity(boundary.len() + 4);
|
let mut full_boundary = Vec::with_capacity(boundary.len() + 4);
|
||||||
full_boundary.extend_from_slice(b"\r\n--");
|
full_boundary.extend_from_slice(b"\r\n--");
|
||||||
full_boundary.extend_from_slice(boundary);
|
full_boundary.extend_from_slice(boundary);
|
||||||
|
let full_boundary_no_crlf = full_boundary.strip_prefix(b"\r\n").unwrap();
|
||||||
|
|
||||||
let mut boundaries = memchr::memmem::find_iter(body, &full_boundary);
|
let mut boundaries = memchr::memmem::find_iter(body, &full_boundary);
|
||||||
|
|
||||||
let metadata_start = boundaries.next().ok_or_else(|| {
|
let metadata_start = if body.starts_with(full_boundary_no_crlf) {
|
||||||
MultipartMixedDeserializationError::MissingBodyParts { expected: 2, found: 0 }
|
// If there is no preamble before the first boundary, it may omit the
|
||||||
})? + full_boundary.len();
|
// preceding CRLF.
|
||||||
|
full_boundary_no_crlf.len()
|
||||||
|
} else {
|
||||||
|
boundaries.next().ok_or_else(|| MultipartMixedDeserializationError::MissingBodyParts {
|
||||||
|
expected: 2,
|
||||||
|
found: 0,
|
||||||
|
})? + full_boundary.len()
|
||||||
|
};
|
||||||
let metadata_end = boundaries.next().ok_or_else(|| {
|
let metadata_end = boundaries.next().ok_or_else(|| {
|
||||||
MultipartMixedDeserializationError::MissingBodyParts { expected: 2, found: 0 }
|
MultipartMixedDeserializationError::MissingBodyParts { expected: 2, found: 0 }
|
||||||
})?;
|
})?;
|
||||||
@ -417,6 +425,15 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
try_from_multipart_mixed_response(response).unwrap_err();
|
try_from_multipart_mixed_response(response).unwrap_err();
|
||||||
|
|
||||||
|
// Boundary without CRLF with preamble.
|
||||||
|
let body = "foo--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
|
||||||
|
let response = http::Response::builder()
|
||||||
|
.header(http::header::CONTENT_TYPE, "multipart/mixed; boundary=abcdef")
|
||||||
|
.body(body)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
try_from_multipart_mixed_response(response).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -483,6 +500,36 @@ mod tests {
|
|||||||
assert_eq!(file_content.content_type.unwrap(), "text/plain");
|
assert_eq!(file_content.content_type.unwrap(), "text/plain");
|
||||||
assert_eq!(file_content.content_disposition, None);
|
assert_eq!(file_content.content_disposition, None);
|
||||||
|
|
||||||
|
// No leading CRLF (and no preamble)
|
||||||
|
let body = "--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
|
||||||
|
let response = http::Response::builder()
|
||||||
|
.header(http::header::CONTENT_TYPE, "multipart/mixed; boundary=abcdef")
|
||||||
|
.body(body)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (_metadata, content) = try_from_multipart_mixed_response(response).unwrap();
|
||||||
|
|
||||||
|
assert_matches!(content, FileOrLocation::File(file_content));
|
||||||
|
assert_eq!(file_content.file, b"some plain text");
|
||||||
|
assert_eq!(file_content.content_type, None);
|
||||||
|
assert_eq!(file_content.content_disposition, None);
|
||||||
|
|
||||||
|
// Boundary text in preamble, but no leading CRLF, so it should be
|
||||||
|
// ignored.
|
||||||
|
let body =
|
||||||
|
"foo--abcdef\r\n--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
|
||||||
|
let response = http::Response::builder()
|
||||||
|
.header(http::header::CONTENT_TYPE, "multipart/mixed; boundary=abcdef")
|
||||||
|
.body(body)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (_metadata, content) = try_from_multipart_mixed_response(response).unwrap();
|
||||||
|
|
||||||
|
assert_matches!(content, FileOrLocation::File(file_content));
|
||||||
|
assert_eq!(file_content.file, b"some plain text");
|
||||||
|
assert_eq!(file_content.content_type, None);
|
||||||
|
assert_eq!(file_content.content_disposition, None);
|
||||||
|
|
||||||
// No body part headers.
|
// No body part headers.
|
||||||
let body = "\r\n--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
|
let body = "\r\n--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
|
||||||
let response = http::Response::builder()
|
let response = http::Response::builder()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user