feat: drop Dart HTTP server

This commit is contained in:
Tien Do Nam
2026-07-20 16:16:58 +02:00
parent 1ec28463b5
commit 9e8a51d003
38 changed files with 2360 additions and 1546 deletions
+1
View File
@@ -2493,6 +2493,7 @@ dependencies = [
"bytes",
"futures-core",
"futures-sink",
"futures-util",
"pin-project-lite",
"tokio",
]
+1 -1
View File
@@ -30,7 +30,7 @@ tokio = { version = "1.49.0", features = ["full"] }
tokio-rustls = { version = "0.26.4", default-features = false, features = ["ring", "tls12"], optional = true }
tokio-stream = "0.1.18"
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"], optional = true }
tokio-util = { version = "0.7.16", optional = true }
tokio-util = { version = "0.7.16", features = ["rt"], optional = true }
tracing = "0.1.44"
tracing-subscriber = { version = "0.3.20" }
tungstenite = "0.28.0"
+28 -3
View File
@@ -35,6 +35,10 @@ pub enum FileUploadTarget {
/// Channel on which the server reports whether the file was saved successfully.
result_tx: oneshot::Sender<Result<(), String>>,
/// Optional channel on which the server reports the number of bytes
/// written so far. Events are dropped when the channel is full.
progress_tx: Option<mpsc::Sender<u64>>,
},
/// The server writes the file to this raw file descriptor (Android only)
@@ -47,6 +51,10 @@ pub enum FileUploadTarget {
/// Channel on which the server reports whether the file was saved successfully.
result_tx: oneshot::Sender<Result<(), String>>,
/// Optional channel on which the server reports the number of bytes
/// written so far. Events are dropped when the channel is full.
progress_tx: Option<mpsc::Sender<u64>>,
},
}
@@ -61,7 +69,11 @@ pub(crate) async fn save_req_to_target(
binary_tx,
result_rx,
} => (binary_tx, result_rx),
FileUploadTarget::Path { path, result_tx } => spawn_file_writer(
FileUploadTarget::Path {
path,
result_tx,
progress_tx,
} => spawn_file_writer(
async move {
tokio::fs::File::create(&path)
.await
@@ -69,9 +81,14 @@ pub(crate) async fn save_req_to_target(
},
file_size,
result_tx,
progress_tx,
),
#[cfg(target_os = "android")]
FileUploadTarget::Fd { fd, result_tx } => spawn_file_writer(
FileUploadTarget::Fd {
fd,
result_tx,
progress_tx,
} => spawn_file_writer(
async move {
use std::os::fd::FromRawFd;
@@ -82,6 +99,7 @@ pub(crate) async fn save_req_to_target(
},
file_size,
result_tx,
progress_tx,
),
};
@@ -136,12 +154,14 @@ fn spawn_file_writer(
open: impl Future<Output = Result<tokio::fs::File, String>> + Send + 'static,
expected_size: u64,
result_tx: oneshot::Sender<Result<(), String>>,
progress_tx: Option<mpsc::Sender<u64>>,
) -> (mpsc::Sender<Bytes>, oneshot::Receiver<Result<(), String>>) {
let (binary_tx, mut binary_rx) = mpsc::channel::<Bytes>(UPLOAD_CHANNEL_CAPACITY);
let (internal_tx, internal_rx) = oneshot::channel::<Result<(), String>>();
tokio::spawn(async move {
let result = write_file_from_receiver(open, expected_size, &mut binary_rx).await;
let result =
write_file_from_receiver(open, expected_size, &mut binary_rx, progress_tx).await;
// Unblock the request handler if it is still sending chunks.
binary_rx.close();
let _ = result_tx.send(result.clone());
@@ -159,6 +179,7 @@ async fn write_file_from_receiver(
open: impl Future<Output = Result<tokio::fs::File, String>>,
expected_size: u64,
rx: &mut mpsc::Receiver<Bytes>,
progress_tx: Option<mpsc::Sender<u64>>,
) -> Result<(), String> {
use tokio::io::AsyncWriteExt;
@@ -174,6 +195,10 @@ async fn write_file_from_receiver(
file.write_all(&chunk)
.await
.map_err(|e| format!("Failed to write file: {e}"))?;
if let Some(progress_tx) = &progress_tx {
// Progress is best-effort: drop the event when the consumer lags.
let _ = progress_tx.try_send(written);
}
}
file.flush()
.await
+137 -60
View File
@@ -27,6 +27,8 @@ use std::num::NonZeroUsize;
use std::ops::Deref;
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot, Mutex};
use tokio_util::sync::CancellationToken;
use tokio_util::task::TaskTracker;
use web::WebPageState;
/// Configuration for the v2 (legacy) protocol endpoints.
@@ -108,6 +110,51 @@ impl AppState {
}
}
/// A handle to a running server for interactions initiated by the application
/// (as opposed to the event channels which are driven by incoming requests).
pub struct ServerHandle {
v2: Option<Arc<V2State>>,
/// The task running the accept loops. Completes after a stop has been
/// requested, the listeners have been dropped and all connections have
/// been closed.
task: Mutex<Option<tokio::task::JoinHandle<()>>>,
}
impl ServerHandle {
/// Waits until the server task has terminated, the listeners are closed
/// and all connections have been dropped, so that the port can be bound again.
/// Must be called after requesting a stop via the stop channel.
pub async fn wait_stopped(&self) {
if let Some(task) = self.task.lock().await.take() {
let _ = task.await;
}
}
/// Cancels the active v2 upload session if it matches `session_id`,
/// e.g. because the user aborted the transfer on the receiving side.
///
/// Uploads that are already in progress still run to completion, but new
/// upload requests are rejected and a new session can be created.
/// No [ServerEventV2::SessionEnd] is emitted: the application initiated
/// the cancellation itself.
///
/// Returns `true` when a session was cancelled.
pub async fn cancel_v2_session(&self, session_id: &str) -> bool {
let Some(v2) = &self.v2 else {
return false;
};
let mut slot = v2.session.lock().await;
match slot.as_ref() {
Some(SessionStateV2::Active(session)) if session.session_id == session_id => {
*slot = None;
true
}
_ => false,
}
}
}
/// Binds the server to the specified port on both IPv4 and IPv6 addresses.
pub async fn start_with_port(
port: u16,
@@ -117,7 +164,7 @@ pub async fn start_with_port(
v2_config: Option<ServerConfigV2>,
web_send_config: Option<WebSendConfig>,
stop_rx: oneshot::Receiver<()>,
) -> anyhow::Result<()> {
) -> anyhow::Result<ServerHandle> {
let ipv4_socket_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port);
let ipv6_socket_addr = SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), port);
let info = Arc::new(Mutex::new(info));
@@ -132,16 +179,21 @@ pub async fn start_with_port(
}
};
tokio::spawn({
let cancel = CancellationToken::new();
let connections = TaskTracker::new();
let task = tokio::spawn({
let state = state.clone();
let cancel = cancel.clone();
let connections = connections.clone();
async move {
tokio::select! {
_ = start_server_with_listener(ipv4_listener, tls_config.clone(), state.clone()) => {
_ = start_server_with_listener(ipv4_listener, tls_config.clone(), state.clone(), cancel.clone(), connections.clone()) => {
tracing::info!("Server stopped on: {}", ipv4_socket_addr);
}
_ = async {
if let Some(listener) = ipv6_listener {
let _ = start_server_with_listener(listener, tls_config, state).await;
let _ = start_server_with_listener(listener, tls_config, state, cancel.clone(), connections.clone()).await;
}
// Keep the future running forever, so we continue using "ipv4 only" even if ipv6 fails.
@@ -149,10 +201,19 @@ pub async fn start_with_port(
} => {}
_ = stop_rx => {}
}
// Hard-drop connections that are still being served, so that no
// client keeps talking to the stopped server.
cancel.cancel();
connections.close();
connections.wait().await;
}
});
Ok(())
Ok(ServerHandle {
v2: state.v2.clone(),
task: Mutex::new(Some(task)),
})
}
/// Binds an IPv6 listener with `IPV6_V6ONLY` enabled.
@@ -184,6 +245,8 @@ async fn start_server_with_listener(
incoming: tokio::net::TcpListener,
tls_config: Option<TlsConfig>,
app_state: AppState,
cancel: CancellationToken,
connections: TaskTracker,
) -> anyhow::Result<()> {
let _ = rustls::crypto::ring::default_provider().install_default();
@@ -205,64 +268,78 @@ async fn start_server_with_listener(
let tls_acceptor = tls_acceptor.clone();
let app_state = app_state.clone();
tokio::spawn(async move {
let res = match tls_acceptor {
Some(tls_acceptor) => {
let tls_stream = match tls_acceptor.accept(tcp_stream).await {
Ok(tls_stream) => tls_stream,
Err(err) => {
tracing::warn!("TLS handshake error: {err:#}");
return;
}
};
let cancel = cancel.clone();
connections.spawn(async move {
let serve = serve_connection(tcp_stream, remote_addr, tls_acceptor, app_state);
tokio::select! {
_ = serve => {}
// Hard-drop the connection when the server is stopped.
_ = cancel.cancelled() => {}
}
});
}
}
let client_info = {
let (_, server_connection) = tls_stream.get_ref();
RequestClientInfo {
ip: remote_addr.ip(),
cert: server_connection
.deref()
.deref()
.peer_certificates()
.map(|cert| cert.get(0).unwrap().to_vec()),
}
};
Builder::new(TokioExecutor::new())
.serve_connection(
TokioIo::new(tls_stream),
hyper::service::service_fn(move |mut req: Request<Incoming>| {
req.extensions_mut()
.insert::<RequestClientInfo>(client_info.clone());
req.extensions_mut().insert::<AppState>(app_state.clone());
handle_request(req)
}),
)
.await
}
None => {
Builder::new(TokioExecutor::new())
.serve_connection(
TokioIo::new(tcp_stream),
hyper::service::service_fn(move |mut req: Request<Incoming>| {
req.extensions_mut().insert::<RequestClientInfo>(
RequestClientInfo {
ip: remote_addr.ip(),
cert: None,
},
);
req.extensions_mut().insert::<AppState>(app_state.clone());
handle_request(req)
}),
)
.await
async fn serve_connection(
tcp_stream: tokio::net::TcpStream,
remote_addr: SocketAddr,
tls_acceptor: Option<tokio_rustls::TlsAcceptor>,
app_state: AppState,
) {
let res = match tls_acceptor {
Some(tls_acceptor) => {
let tls_stream = match tls_acceptor.accept(tcp_stream).await {
Ok(tls_stream) => tls_stream,
Err(err) => {
tracing::warn!("TLS handshake error: {err:#}");
return;
}
};
if let Err(err) = res {
tracing::warn!("Failed to serve connection: {err:#}");
}
});
let client_info = {
let (_, server_connection) = tls_stream.get_ref();
RequestClientInfo {
ip: remote_addr.ip(),
cert: server_connection
.deref()
.deref()
.peer_certificates()
.map(|cert| cert.get(0).unwrap().to_vec()),
}
};
Builder::new(TokioExecutor::new())
.serve_connection(
TokioIo::new(tls_stream),
hyper::service::service_fn(move |mut req: Request<Incoming>| {
req.extensions_mut()
.insert::<RequestClientInfo>(client_info.clone());
req.extensions_mut().insert::<AppState>(app_state.clone());
handle_request(req)
}),
)
.await
}
None => {
Builder::new(TokioExecutor::new())
.serve_connection(
TokioIo::new(tcp_stream),
hyper::service::service_fn(move |mut req: Request<Incoming>| {
req.extensions_mut()
.insert::<RequestClientInfo>(RequestClientInfo {
ip: remote_addr.ip(),
cert: None,
});
req.extensions_mut().insert::<AppState>(app_state.clone());
handle_request(req)
}),
)
.await
}
};
if let Err(err) = res {
tracing::warn!("Failed to serve connection: {err:#}");
}
}
@@ -372,7 +449,7 @@ async fn handle_request_inner(mut req: Request<Incoming>) -> Result<Response<Box
return Err(AppError::Status(StatusCode::NOT_FOUND));
}
v2::cancel(req, state).await
v2::cancel(req, state, client_info).await
}
// The versioned path is retained for compatibility, but this endpoint is internal.
(&Method::POST, "/api/localsend/v2/show") => internal::show(req, state).await,
+62 -5
View File
@@ -38,6 +38,11 @@ pub enum ServerEventV2 {
/// The application must answer on `decision_tx`.
/// Dropping `decision_tx` results in a 500 response.
PrepareUpload {
/// The session ID the upload session will have when the request is
/// accepted. Pre-generated so the application can track the session
/// consistently from the start.
session_id: String,
/// The IP address of the sender.
ip: IpAddr,
@@ -78,6 +83,30 @@ pub enum ServerEventV2 {
/// Why the session ended.
reason: SessionEndReasonV2,
},
/// A prepare-upload request was aborted before a session was created,
/// e.g. the sender disconnected while the application was still deciding.
/// The `decision_tx` of the [ServerEventV2::PrepareUpload] with the same
/// session ID is dead; answering it has no effect.
PrepareUploadAborted {
/// The session ID of the aborted prepare-upload request.
session_id: String,
},
/// `POST /api/localsend/v2/cancel` was received for a session this server
/// does not manage. This happens when the remote device cancels a transfer
/// that this application is currently *sending* to it: the session ID is
/// the one issued by the remote device during prepare-upload.
///
/// The application must verify that `ip` matches the target of the
/// send session before cancelling it.
CancelReceived {
/// The IP address of the remote device requesting the cancellation.
ip: IpAddr,
/// The session ID as known by the remote device.
session_id: String,
},
}
/// The application's decision for a prepare-upload request.
@@ -182,11 +211,14 @@ pub(crate) async fn prepare_upload(
*slot = Some(SessionStateV2::Pending);
}
let session_id = Uuid::new_v4().to_string();
// Frees the slot again if this request is aborted before a session is created.
let mut pending_guard = PendingSessionGuard::new(v2.clone());
let mut pending_guard = PendingSessionGuard::new(v2.clone(), session_id.clone());
let (decision_tx, decision_rx) = oneshot::channel();
let event = ServerEventV2::PrepareUpload {
session_id: session_id.clone(),
ip: client_info.ip,
info: payload.info,
files: payload.files.clone(),
@@ -233,7 +265,6 @@ pub(crate) async fn prepare_upload(
return Ok(res);
}
let session_id = Uuid::new_v4().to_string();
let tokens: HashMap<String, String> = files
.iter()
.map(|(id, file)| (id.clone(), file.token.clone()))
@@ -334,6 +365,7 @@ pub(crate) async fn upload(
pub(crate) async fn cancel(
req: Request<Incoming>,
state: AppState,
client_info: RequestClientInfo,
) -> Result<Response<BoxedBody>, AppError> {
let v2 = require_v2(&state)?;
let query = parse_query(req.uri().query());
@@ -342,7 +374,10 @@ pub(crate) async fn cancel(
let cancelled = {
let mut slot = v2.session.lock().await;
match slot.as_ref() {
Some(SessionStateV2::Active(session)) if session.session_id == *session_id => {
Some(SessionStateV2::Active(session))
if session.session_id == *session_id
&& session.sender_ip == client_info.ip =>
{
*slot = None;
true
}
@@ -359,6 +394,16 @@ pub(crate) async fn cancel(
reason: SessionEndReasonV2::Cancelled,
})
.await;
} else {
// Not one of our upload sessions: the remote device may be
// cancelling a transfer this application is sending to it.
let _ = v2
.event_tx
.send(ServerEventV2::CancelReceived {
ip: client_info.ip,
session_id: session_id.clone(),
})
.await;
}
}
@@ -386,12 +431,17 @@ fn invalid_token_error() -> AppError {
/// while the application was still deciding).
struct PendingSessionGuard {
v2: Arc<V2State>,
session_id: String,
armed: bool,
}
impl PendingSessionGuard {
fn new(v2: Arc<V2State>) -> Self {
Self { v2, armed: true }
fn new(v2: Arc<V2State>, session_id: String) -> Self {
Self {
v2,
session_id,
armed: true,
}
}
/// Disarms the guard after the pending slot was replaced by an active session.
@@ -412,8 +462,15 @@ impl Drop for PendingSessionGuard {
return;
}
let v2 = self.v2.clone();
let session_id = std::mem::take(&mut self.session_id);
tokio::spawn(async move {
clear_pending_session(&v2).await;
// The application may still be waiting for a decision; tell it
// that answering is pointless now.
let _ = v2
.event_tx
.send(ServerEventV2::PrepareUploadAborted { session_id })
.await;
});
}
}
+3
View File
@@ -90,6 +90,7 @@ async fn start_test_server(
let _ = target_tx.send(FileUploadTarget::Path {
path: path.clone(),
result_tx,
progress_tx: None,
});
tokio::spawn(async move {
if let Ok(Ok(())) = result_rx.await {
@@ -103,6 +104,8 @@ async fn start_test_server(
ServerEventV2::SessionEnd { session_id, reason } => {
session_ends.lock().await.push((session_id, reason));
}
ServerEventV2::PrepareUploadAborted { .. } => {}
ServerEventV2::CancelReceived { .. } => {}
}
}
}
+8 -1
View File
@@ -1,11 +1,18 @@
export 'package:localsend_isolates/src/isolate/child/server_isolate.dart'
show
HttpServerCancelReceivedEvent,
HttpServerEvent,
HttpServerFileUploadEvent,
HttpServerFileUploadProgressEvent,
HttpServerFileUploadResultEvent,
HttpServerPrepareUploadAbortedEvent,
HttpServerPrepareUploadEvent,
HttpServerRegisterEvent,
HttpServerSessionEndEvent;
HttpServerSessionEndEvent,
HttpServerShowEvent,
HttpServerStartedEvent,
HttpServerWebFileDownloadEvent,
HttpServerWebPrepareDownloadEvent;
export 'package:localsend_isolates/src/isolate/child/sync_provider.dart';
export 'package:localsend_isolates/src/isolate/child/upload_isolate.dart'
show
@@ -49,6 +49,15 @@ Future<RsHttpServer> startServer({
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsHttpServer>>
abstract class RsHttpServer implements RustOpaqueInterface {
/// Cancels the active upload session, e.g. because the user aborted the
/// transfer on the receiving side.
///
/// Uploads that are already in progress still run to completion, but new
/// upload requests are rejected and a new session can be created.
/// No [RsServerEvent::SessionEnd] is emitted: the application initiated
/// the cancellation itself.
Future<void> cancelSession({required String sessionId});
/// Emits server events until the server is stopped.
/// Can only be listened to once.
///
@@ -56,6 +65,20 @@ abstract class RsHttpServer implements RustOpaqueInterface {
/// events are all emitted on the same stream.
Stream<RsServerEvent> listen();
/// Rejects the pending [RsServerEvent::WebFileDownload] event, e.g. because
/// the application failed to resolve a source for the file content.
///
/// The download request fails with an error response.
/// Does nothing if the download was already answered.
Future<void> rejectFileDownload({required String sessionId, required String fileId});
/// Rejects the pending [RsServerEvent::FileUpload] event, e.g. because
/// the application failed to prepare a save target for the file.
///
/// The upload request fails with an error response and the file is marked
/// as failed. Does nothing if the upload was already answered.
Future<void> rejectFileUpload({required String sessionId, required String fileId});
/// Answers the pending [RsServerEvent::WebFileDownload] event with the source
/// the file content should be read from (either a path or a file descriptor).
///
@@ -65,7 +88,10 @@ abstract class RsHttpServer implements RustOpaqueInterface {
/// Answers the pending [RsServerEvent::FileUpload] event with the target
/// the file should be saved to (either a path or a file descriptor)
/// and waits until the file has been received completely.
Future<void> respondFileUpload({required String sessionId, required String fileId, String? path, int? fileDescriptor});
///
/// The progress (fraction of [file_size]) is emitted on [sink]
/// while the file is being received.
Stream<double> respondFileUpload({required String sessionId, required String fileId, String? path, int? fileDescriptor, required BigInt fileSize});
/// Answers the pending [RsServerEvent::WebPrepareDownload] event.
///
@@ -79,6 +105,7 @@ abstract class RsHttpServer implements RustOpaqueInterface {
Future<void> respondPrepareUpload({List<String>? acceptedFileIds});
/// Stops the server.
/// Returns after the listeners are closed, so the port can be bound again.
Future<void> stop();
}
@@ -146,6 +173,8 @@ sealed class RsServerEvent with _$RsServerEvent {
/// A sender requests to upload files via `POST /api/localsend/v2/prepare-upload`.
const factory RsServerEvent.prepareUpload({
/// The session ID the upload session will have when the request is accepted.
required String sessionId,
required String ip,
required RegisterDtoV2 info,
required Map<String, FileDto> files,
@@ -164,6 +193,23 @@ sealed class RsServerEvent with _$RsServerEvent {
required SessionEndReasonV2 reason,
}) = RsServerEvent_SessionEnd;
/// A prepare-upload request was aborted before a session was created,
/// e.g. the sender disconnected while the application was still deciding.
/// The [RsServerEvent::PrepareUpload] with the same session ID
/// no longer needs to be answered.
const factory RsServerEvent.prepareUploadAborted({
required String sessionId,
}) = RsServerEvent_PrepareUploadAborted;
/// `POST /api/localsend/v2/cancel` was received for a session this server
/// does not manage: the remote device cancels a transfer this application
/// is currently *sending* to it. The application must verify that [ip]
/// matches the target of the send session before cancelling it.
const factory RsServerEvent.cancelReceived({
required String ip,
required String sessionId,
}) = RsServerEvent_CancelReceived;
/// A web client requests to download the shared files via `POST /api/localsend/v2/prepare-download`.
///
/// Must be answered with [RsHttpServer::respond_prepare_download].
@@ -55,14 +55,16 @@ extension RsServerEventPatterns on RsServerEvent {
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( RsServerEvent_Register value)? register,TResult Function( RsServerEvent_PrepareUpload value)? prepareUpload,TResult Function( RsServerEvent_FileUpload value)? fileUpload,TResult Function( RsServerEvent_SessionEnd value)? sessionEnd,TResult Function( RsServerEvent_WebPrepareDownload value)? webPrepareDownload,TResult Function( RsServerEvent_WebFileDownload value)? webFileDownload,TResult Function( RsServerEvent_Show value)? show_,required TResult orElse(),}){
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( RsServerEvent_Register value)? register,TResult Function( RsServerEvent_PrepareUpload value)? prepareUpload,TResult Function( RsServerEvent_FileUpload value)? fileUpload,TResult Function( RsServerEvent_SessionEnd value)? sessionEnd,TResult Function( RsServerEvent_PrepareUploadAborted value)? prepareUploadAborted,TResult Function( RsServerEvent_CancelReceived value)? cancelReceived,TResult Function( RsServerEvent_WebPrepareDownload value)? webPrepareDownload,TResult Function( RsServerEvent_WebFileDownload value)? webFileDownload,TResult Function( RsServerEvent_Show value)? show_,required TResult orElse(),}){
final _that = this;
switch (_that) {
case RsServerEvent_Register() when register != null:
return register(_that);case RsServerEvent_PrepareUpload() when prepareUpload != null:
return prepareUpload(_that);case RsServerEvent_FileUpload() when fileUpload != null:
return fileUpload(_that);case RsServerEvent_SessionEnd() when sessionEnd != null:
return sessionEnd(_that);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return sessionEnd(_that);case RsServerEvent_PrepareUploadAborted() when prepareUploadAborted != null:
return prepareUploadAborted(_that);case RsServerEvent_CancelReceived() when cancelReceived != null:
return cancelReceived(_that);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return webPrepareDownload(_that);case RsServerEvent_WebFileDownload() when webFileDownload != null:
return webFileDownload(_that);case RsServerEvent_Show() when show_ != null:
return show_(_that);case _:
@@ -83,14 +85,16 @@ return show_(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( RsServerEvent_Register value) register,required TResult Function( RsServerEvent_PrepareUpload value) prepareUpload,required TResult Function( RsServerEvent_FileUpload value) fileUpload,required TResult Function( RsServerEvent_SessionEnd value) sessionEnd,required TResult Function( RsServerEvent_WebPrepareDownload value) webPrepareDownload,required TResult Function( RsServerEvent_WebFileDownload value) webFileDownload,required TResult Function( RsServerEvent_Show value) show_,}){
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( RsServerEvent_Register value) register,required TResult Function( RsServerEvent_PrepareUpload value) prepareUpload,required TResult Function( RsServerEvent_FileUpload value) fileUpload,required TResult Function( RsServerEvent_SessionEnd value) sessionEnd,required TResult Function( RsServerEvent_PrepareUploadAborted value) prepareUploadAborted,required TResult Function( RsServerEvent_CancelReceived value) cancelReceived,required TResult Function( RsServerEvent_WebPrepareDownload value) webPrepareDownload,required TResult Function( RsServerEvent_WebFileDownload value) webFileDownload,required TResult Function( RsServerEvent_Show value) show_,}){
final _that = this;
switch (_that) {
case RsServerEvent_Register():
return register(_that);case RsServerEvent_PrepareUpload():
return prepareUpload(_that);case RsServerEvent_FileUpload():
return fileUpload(_that);case RsServerEvent_SessionEnd():
return sessionEnd(_that);case RsServerEvent_WebPrepareDownload():
return sessionEnd(_that);case RsServerEvent_PrepareUploadAborted():
return prepareUploadAborted(_that);case RsServerEvent_CancelReceived():
return cancelReceived(_that);case RsServerEvent_WebPrepareDownload():
return webPrepareDownload(_that);case RsServerEvent_WebFileDownload():
return webFileDownload(_that);case RsServerEvent_Show():
return show_(_that);}
@@ -107,14 +111,16 @@ return show_(_that);}
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( RsServerEvent_Register value)? register,TResult? Function( RsServerEvent_PrepareUpload value)? prepareUpload,TResult? Function( RsServerEvent_FileUpload value)? fileUpload,TResult? Function( RsServerEvent_SessionEnd value)? sessionEnd,TResult? Function( RsServerEvent_WebPrepareDownload value)? webPrepareDownload,TResult? Function( RsServerEvent_WebFileDownload value)? webFileDownload,TResult? Function( RsServerEvent_Show value)? show_,}){
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( RsServerEvent_Register value)? register,TResult? Function( RsServerEvent_PrepareUpload value)? prepareUpload,TResult? Function( RsServerEvent_FileUpload value)? fileUpload,TResult? Function( RsServerEvent_SessionEnd value)? sessionEnd,TResult? Function( RsServerEvent_PrepareUploadAborted value)? prepareUploadAborted,TResult? Function( RsServerEvent_CancelReceived value)? cancelReceived,TResult? Function( RsServerEvent_WebPrepareDownload value)? webPrepareDownload,TResult? Function( RsServerEvent_WebFileDownload value)? webFileDownload,TResult? Function( RsServerEvent_Show value)? show_,}){
final _that = this;
switch (_that) {
case RsServerEvent_Register() when register != null:
return register(_that);case RsServerEvent_PrepareUpload() when prepareUpload != null:
return prepareUpload(_that);case RsServerEvent_FileUpload() when fileUpload != null:
return fileUpload(_that);case RsServerEvent_SessionEnd() when sessionEnd != null:
return sessionEnd(_that);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return sessionEnd(_that);case RsServerEvent_PrepareUploadAborted() when prepareUploadAborted != null:
return prepareUploadAborted(_that);case RsServerEvent_CancelReceived() when cancelReceived != null:
return cancelReceived(_that);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return webPrepareDownload(_that);case RsServerEvent_WebFileDownload() when webFileDownload != null:
return webFileDownload(_that);case RsServerEvent_Show() when show_ != null:
return show_(_that);case _:
@@ -134,13 +140,15 @@ return show_(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( String ip, RegisterDtoV2 info)? register,TResult Function( String ip, RegisterDtoV2 info, Map<String, FileDto> files)? prepareUpload,TResult Function( String sessionId, String fileId, FileDto file)? fileUpload,TResult Function( String sessionId, SessionEndReasonV2 reason)? sessionEnd,TResult Function( String ip, String sessionId, String? userAgent)? webPrepareDownload,TResult Function( String sessionId, String fileId, FileDto file)? webFileDownload,TResult Function( List<String> args)? show_,required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( String ip, RegisterDtoV2 info)? register,TResult Function( String sessionId, String ip, RegisterDtoV2 info, Map<String, FileDto> files)? prepareUpload,TResult Function( String sessionId, String fileId, FileDto file)? fileUpload,TResult Function( String sessionId, SessionEndReasonV2 reason)? sessionEnd,TResult Function( String sessionId)? prepareUploadAborted,TResult Function( String ip, String sessionId)? cancelReceived,TResult Function( String ip, String sessionId, String? userAgent)? webPrepareDownload,TResult Function( String sessionId, String fileId, FileDto file)? webFileDownload,TResult Function( List<String> args)? show_,required TResult orElse(),}) {final _that = this;
switch (_that) {
case RsServerEvent_Register() when register != null:
return register(_that.ip,_that.info);case RsServerEvent_PrepareUpload() when prepareUpload != null:
return prepareUpload(_that.ip,_that.info,_that.files);case RsServerEvent_FileUpload() when fileUpload != null:
return prepareUpload(_that.sessionId,_that.ip,_that.info,_that.files);case RsServerEvent_FileUpload() when fileUpload != null:
return fileUpload(_that.sessionId,_that.fileId,_that.file);case RsServerEvent_SessionEnd() when sessionEnd != null:
return sessionEnd(_that.sessionId,_that.reason);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return sessionEnd(_that.sessionId,_that.reason);case RsServerEvent_PrepareUploadAborted() when prepareUploadAborted != null:
return prepareUploadAborted(_that.sessionId);case RsServerEvent_CancelReceived() when cancelReceived != null:
return cancelReceived(_that.ip,_that.sessionId);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return webPrepareDownload(_that.ip,_that.sessionId,_that.userAgent);case RsServerEvent_WebFileDownload() when webFileDownload != null:
return webFileDownload(_that.sessionId,_that.fileId,_that.file);case RsServerEvent_Show() when show_ != null:
return show_(_that.args);case _:
@@ -161,13 +169,15 @@ return show_(_that.args);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( String ip, RegisterDtoV2 info) register,required TResult Function( String ip, RegisterDtoV2 info, Map<String, FileDto> files) prepareUpload,required TResult Function( String sessionId, String fileId, FileDto file) fileUpload,required TResult Function( String sessionId, SessionEndReasonV2 reason) sessionEnd,required TResult Function( String ip, String sessionId, String? userAgent) webPrepareDownload,required TResult Function( String sessionId, String fileId, FileDto file) webFileDownload,required TResult Function( List<String> args) show_,}) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( String ip, RegisterDtoV2 info) register,required TResult Function( String sessionId, String ip, RegisterDtoV2 info, Map<String, FileDto> files) prepareUpload,required TResult Function( String sessionId, String fileId, FileDto file) fileUpload,required TResult Function( String sessionId, SessionEndReasonV2 reason) sessionEnd,required TResult Function( String sessionId) prepareUploadAborted,required TResult Function( String ip, String sessionId) cancelReceived,required TResult Function( String ip, String sessionId, String? userAgent) webPrepareDownload,required TResult Function( String sessionId, String fileId, FileDto file) webFileDownload,required TResult Function( List<String> args) show_,}) {final _that = this;
switch (_that) {
case RsServerEvent_Register():
return register(_that.ip,_that.info);case RsServerEvent_PrepareUpload():
return prepareUpload(_that.ip,_that.info,_that.files);case RsServerEvent_FileUpload():
return prepareUpload(_that.sessionId,_that.ip,_that.info,_that.files);case RsServerEvent_FileUpload():
return fileUpload(_that.sessionId,_that.fileId,_that.file);case RsServerEvent_SessionEnd():
return sessionEnd(_that.sessionId,_that.reason);case RsServerEvent_WebPrepareDownload():
return sessionEnd(_that.sessionId,_that.reason);case RsServerEvent_PrepareUploadAborted():
return prepareUploadAborted(_that.sessionId);case RsServerEvent_CancelReceived():
return cancelReceived(_that.ip,_that.sessionId);case RsServerEvent_WebPrepareDownload():
return webPrepareDownload(_that.ip,_that.sessionId,_that.userAgent);case RsServerEvent_WebFileDownload():
return webFileDownload(_that.sessionId,_that.fileId,_that.file);case RsServerEvent_Show():
return show_(_that.args);}
@@ -184,13 +194,15 @@ return show_(_that.args);}
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( String ip, RegisterDtoV2 info)? register,TResult? Function( String ip, RegisterDtoV2 info, Map<String, FileDto> files)? prepareUpload,TResult? Function( String sessionId, String fileId, FileDto file)? fileUpload,TResult? Function( String sessionId, SessionEndReasonV2 reason)? sessionEnd,TResult? Function( String ip, String sessionId, String? userAgent)? webPrepareDownload,TResult? Function( String sessionId, String fileId, FileDto file)? webFileDownload,TResult? Function( List<String> args)? show_,}) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( String ip, RegisterDtoV2 info)? register,TResult? Function( String sessionId, String ip, RegisterDtoV2 info, Map<String, FileDto> files)? prepareUpload,TResult? Function( String sessionId, String fileId, FileDto file)? fileUpload,TResult? Function( String sessionId, SessionEndReasonV2 reason)? sessionEnd,TResult? Function( String sessionId)? prepareUploadAborted,TResult? Function( String ip, String sessionId)? cancelReceived,TResult? Function( String ip, String sessionId, String? userAgent)? webPrepareDownload,TResult? Function( String sessionId, String fileId, FileDto file)? webFileDownload,TResult? Function( List<String> args)? show_,}) {final _that = this;
switch (_that) {
case RsServerEvent_Register() when register != null:
return register(_that.ip,_that.info);case RsServerEvent_PrepareUpload() when prepareUpload != null:
return prepareUpload(_that.ip,_that.info,_that.files);case RsServerEvent_FileUpload() when fileUpload != null:
return prepareUpload(_that.sessionId,_that.ip,_that.info,_that.files);case RsServerEvent_FileUpload() when fileUpload != null:
return fileUpload(_that.sessionId,_that.fileId,_that.file);case RsServerEvent_SessionEnd() when sessionEnd != null:
return sessionEnd(_that.sessionId,_that.reason);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return sessionEnd(_that.sessionId,_that.reason);case RsServerEvent_PrepareUploadAborted() when prepareUploadAborted != null:
return prepareUploadAborted(_that.sessionId);case RsServerEvent_CancelReceived() when cancelReceived != null:
return cancelReceived(_that.ip,_that.sessionId);case RsServerEvent_WebPrepareDownload() when webPrepareDownload != null:
return webPrepareDownload(_that.ip,_that.sessionId,_that.userAgent);case RsServerEvent_WebFileDownload() when webFileDownload != null:
return webFileDownload(_that.sessionId,_that.fileId,_that.file);case RsServerEvent_Show() when show_ != null:
return show_(_that.args);case _:
@@ -273,9 +285,11 @@ as RegisterDtoV2,
class RsServerEvent_PrepareUpload extends RsServerEvent {
const RsServerEvent_PrepareUpload({required this.ip, required this.info, required final Map<String, FileDto> files}): _files = files,super._();
const RsServerEvent_PrepareUpload({required this.sessionId, required this.ip, required this.info, required final Map<String, FileDto> files}): _files = files,super._();
/// The session ID the upload session will have when the request is accepted.
final String sessionId;
final String ip;
final RegisterDtoV2 info;
final Map<String, FileDto> _files;
@@ -296,16 +310,16 @@ $RsServerEvent_PrepareUploadCopyWith<RsServerEvent_PrepareUpload> get copyWith =
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RsServerEvent_PrepareUpload&&(identical(other.ip, ip) || other.ip == ip)&&(identical(other.info, info) || other.info == info)&&const DeepCollectionEquality().equals(other._files, _files));
return identical(this, other) || (other.runtimeType == runtimeType&&other is RsServerEvent_PrepareUpload&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId)&&(identical(other.ip, ip) || other.ip == ip)&&(identical(other.info, info) || other.info == info)&&const DeepCollectionEquality().equals(other._files, _files));
}
@override
int get hashCode => Object.hash(runtimeType,ip,info,const DeepCollectionEquality().hash(_files));
int get hashCode => Object.hash(runtimeType,sessionId,ip,info,const DeepCollectionEquality().hash(_files));
@override
String toString() {
return 'RsServerEvent.prepareUpload(ip: $ip, info: $info, files: $files)';
return 'RsServerEvent.prepareUpload(sessionId: $sessionId, ip: $ip, info: $info, files: $files)';
}
@@ -316,7 +330,7 @@ abstract mixin class $RsServerEvent_PrepareUploadCopyWith<$Res> implements $RsSe
factory $RsServerEvent_PrepareUploadCopyWith(RsServerEvent_PrepareUpload value, $Res Function(RsServerEvent_PrepareUpload) _then) = _$RsServerEvent_PrepareUploadCopyWithImpl;
@useResult
$Res call({
String ip, RegisterDtoV2 info, Map<String, FileDto> files
String sessionId, String ip, RegisterDtoV2 info, Map<String, FileDto> files
});
@@ -333,9 +347,10 @@ class _$RsServerEvent_PrepareUploadCopyWithImpl<$Res>
/// Create a copy of RsServerEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? ip = null,Object? info = null,Object? files = null,}) {
@pragma('vm:prefer-inline') $Res call({Object? sessionId = null,Object? ip = null,Object? info = null,Object? files = null,}) {
return _then(RsServerEvent_PrepareUpload(
ip: null == ip ? _self.ip : ip // ignore: cast_nullable_to_non_nullable
sessionId: null == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String,ip: null == ip ? _self.ip : ip // ignore: cast_nullable_to_non_nullable
as String,info: null == info ? _self.info : info // ignore: cast_nullable_to_non_nullable
as RegisterDtoV2,files: null == files ? _self._files : files // ignore: cast_nullable_to_non_nullable
as Map<String, FileDto>,
@@ -486,6 +501,140 @@ as SessionEndReasonV2,
/// @nodoc
class RsServerEvent_PrepareUploadAborted extends RsServerEvent {
const RsServerEvent_PrepareUploadAborted({required this.sessionId}): super._();
final String sessionId;
/// Create a copy of RsServerEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$RsServerEvent_PrepareUploadAbortedCopyWith<RsServerEvent_PrepareUploadAborted> get copyWith => _$RsServerEvent_PrepareUploadAbortedCopyWithImpl<RsServerEvent_PrepareUploadAborted>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RsServerEvent_PrepareUploadAborted&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId));
}
@override
int get hashCode => Object.hash(runtimeType,sessionId);
@override
String toString() {
return 'RsServerEvent.prepareUploadAborted(sessionId: $sessionId)';
}
}
/// @nodoc
abstract mixin class $RsServerEvent_PrepareUploadAbortedCopyWith<$Res> implements $RsServerEventCopyWith<$Res> {
factory $RsServerEvent_PrepareUploadAbortedCopyWith(RsServerEvent_PrepareUploadAborted value, $Res Function(RsServerEvent_PrepareUploadAborted) _then) = _$RsServerEvent_PrepareUploadAbortedCopyWithImpl;
@useResult
$Res call({
String sessionId
});
}
/// @nodoc
class _$RsServerEvent_PrepareUploadAbortedCopyWithImpl<$Res>
implements $RsServerEvent_PrepareUploadAbortedCopyWith<$Res> {
_$RsServerEvent_PrepareUploadAbortedCopyWithImpl(this._self, this._then);
final RsServerEvent_PrepareUploadAborted _self;
final $Res Function(RsServerEvent_PrepareUploadAborted) _then;
/// Create a copy of RsServerEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? sessionId = null,}) {
return _then(RsServerEvent_PrepareUploadAborted(
sessionId: null == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class RsServerEvent_CancelReceived extends RsServerEvent {
const RsServerEvent_CancelReceived({required this.ip, required this.sessionId}): super._();
final String ip;
final String sessionId;
/// Create a copy of RsServerEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$RsServerEvent_CancelReceivedCopyWith<RsServerEvent_CancelReceived> get copyWith => _$RsServerEvent_CancelReceivedCopyWithImpl<RsServerEvent_CancelReceived>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RsServerEvent_CancelReceived&&(identical(other.ip, ip) || other.ip == ip)&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId));
}
@override
int get hashCode => Object.hash(runtimeType,ip,sessionId);
@override
String toString() {
return 'RsServerEvent.cancelReceived(ip: $ip, sessionId: $sessionId)';
}
}
/// @nodoc
abstract mixin class $RsServerEvent_CancelReceivedCopyWith<$Res> implements $RsServerEventCopyWith<$Res> {
factory $RsServerEvent_CancelReceivedCopyWith(RsServerEvent_CancelReceived value, $Res Function(RsServerEvent_CancelReceived) _then) = _$RsServerEvent_CancelReceivedCopyWithImpl;
@useResult
$Res call({
String ip, String sessionId
});
}
/// @nodoc
class _$RsServerEvent_CancelReceivedCopyWithImpl<$Res>
implements $RsServerEvent_CancelReceivedCopyWith<$Res> {
_$RsServerEvent_CancelReceivedCopyWithImpl(this._self, this._then);
final RsServerEvent_CancelReceived _self;
final $Res Function(RsServerEvent_CancelReceived) _then;
/// Create a copy of RsServerEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? ip = null,Object? sessionId = null,}) {
return _then(RsServerEvent_CancelReceived(
ip: null == ip ? _self.ip : ip // ignore: cast_nullable_to_non_nullable
as String,sessionId: null == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class RsServerEvent_WebPrepareDownload extends RsServerEvent {
const RsServerEvent_WebPrepareDownload({required this.ip, required this.sessionId, this.userAgent}): super._();
@@ -72,7 +72,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
String get codegenVersion => '2.12.0';
@override
int get rustContentHash => -1029282510;
int get rustContentHash => 1916764705;
static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig(
stem: 'rust_lib_localsend_app',
@@ -152,8 +152,14 @@ abstract class RustLibApi extends BaseApi {
required RsCancellationToken cancelToken,
});
Future<void> crateApiServerRsHttpServerCancelSession({required RsHttpServer that, required String sessionId});
Stream<RsServerEvent> crateApiServerRsHttpServerListen({required RsHttpServer that});
Future<void> crateApiServerRsHttpServerRejectFileDownload({required RsHttpServer that, required String sessionId, required String fileId});
Future<void> crateApiServerRsHttpServerRejectFileUpload({required RsHttpServer that, required String sessionId, required String fileId});
Future<void> crateApiServerRsHttpServerRespondFileDownload({
required RsHttpServer that,
required String sessionId,
@@ -162,12 +168,13 @@ abstract class RustLibApi extends BaseApi {
int? fileDescriptor,
});
Future<void> crateApiServerRsHttpServerRespondFileUpload({
Stream<double> crateApiServerRsHttpServerRespondFileUpload({
required RsHttpServer that,
required String sessionId,
required String fileId,
String? path,
int? fileDescriptor,
required BigInt fileSize,
});
Future<void> crateApiServerRsHttpServerRespondPrepareDownload({required RsHttpServer that, required String sessionId, required bool accept});
@@ -670,6 +677,32 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
],
);
@override
Future<void> crateApiServerRsHttpServerCancelSession({required RsHttpServer that, required String sessionId}) {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_String(sessionId, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: null,
),
constMeta: kCrateApiServerRsHttpServerCancelSessionConstMeta,
argValues: [that, sessionId],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiServerRsHttpServerCancelSessionConstMeta => const TaskConstMeta(
debugName: 'RsHttpServer_cancel_session',
argNames: ['that', 'sessionId'],
);
@override
Stream<RsServerEvent> crateApiServerRsHttpServerListen({required RsHttpServer that}) {
final sink = RustStreamSink<RsServerEvent>();
@@ -680,7 +713,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_StreamSink_rs_server_event_Sse(sink, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -700,6 +733,60 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ['that', 'sink'],
);
@override
Future<void> crateApiServerRsHttpServerRejectFileDownload({required RsHttpServer that, required String sessionId, required String fileId}) {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_String(fileId, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: null,
),
constMeta: kCrateApiServerRsHttpServerRejectFileDownloadConstMeta,
argValues: [that, sessionId, fileId],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiServerRsHttpServerRejectFileDownloadConstMeta => const TaskConstMeta(
debugName: 'RsHttpServer_reject_file_download',
argNames: ['that', 'sessionId', 'fileId'],
);
@override
Future<void> crateApiServerRsHttpServerRejectFileUpload({required RsHttpServer that, required String sessionId, required String fileId}) {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_String(fileId, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: null,
),
constMeta: kCrateApiServerRsHttpServerRejectFileUploadConstMeta,
argValues: [that, sessionId, fileId],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiServerRsHttpServerRejectFileUploadConstMeta => const TaskConstMeta(
debugName: 'RsHttpServer_reject_file_upload',
argNames: ['that', 'sessionId', 'fileId'],
);
@override
Future<void> crateApiServerRsHttpServerRespondFileDownload({
required RsHttpServer that,
@@ -717,7 +804,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_String(fileId, serializer);
sse_encode_opt_String(path, serializer);
sse_encode_opt_box_autoadd_i_32(fileDescriptor, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -736,38 +823,45 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
);
@override
Future<void> crateApiServerRsHttpServerRespondFileUpload({
Stream<double> crateApiServerRsHttpServerRespondFileUpload({
required RsHttpServer that,
required String sessionId,
required String fileId,
String? path,
int? fileDescriptor,
required BigInt fileSize,
}) {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_String(fileId, serializer);
sse_encode_opt_String(path, serializer);
sse_encode_opt_box_autoadd_i_32(fileDescriptor, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: sse_decode_AnyhowException,
final sink = RustStreamSink<double>();
unawaited(
handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_StreamSink_f_64_Sse(sink, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_String(fileId, serializer);
sse_encode_opt_String(path, serializer);
sse_encode_opt_box_autoadd_i_32(fileDescriptor, serializer);
sse_encode_u_64(fileSize, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: sse_decode_AnyhowException,
),
constMeta: kCrateApiServerRsHttpServerRespondFileUploadConstMeta,
argValues: [that, sink, sessionId, fileId, path, fileDescriptor, fileSize],
apiImpl: this,
),
constMeta: kCrateApiServerRsHttpServerRespondFileUploadConstMeta,
argValues: [that, sessionId, fileId, path, fileDescriptor],
apiImpl: this,
),
);
return sink.stream;
}
TaskConstMeta get kCrateApiServerRsHttpServerRespondFileUploadConstMeta => const TaskConstMeta(
debugName: 'RsHttpServer_respond_file_upload',
argNames: ['that', 'sessionId', 'fileId', 'path', 'fileDescriptor'],
argNames: ['that', 'sink', 'sessionId', 'fileId', 'path', 'fileDescriptor', 'fileSize'],
);
@override
@@ -779,7 +873,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_bool(accept, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -805,7 +899,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
sse_encode_opt_list_String(acceptedFileIds, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -830,7 +924,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer(that, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -855,7 +949,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCFileReceiver(that, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_String,
@@ -883,7 +977,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCFileReceiver(that, serializer);
sse_encode_StreamSink_list_prim_u_8_strict_Sse(sink, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -911,7 +1005,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCFileSender(that, serializer);
sse_encode_list_prim_u_8_loose(data, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -936,7 +1030,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -964,7 +1058,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
sse_encode_StreamSink_rtc_file_error_Sse(sink, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -991,7 +1085,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_list_file_dto,
@@ -1019,7 +1113,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
sse_encode_StreamSink_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCFileReceiver_Sse(sink, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1049,7 +1143,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
sse_encode_StreamSink_rtc_status_Sse(sink, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1077,7 +1171,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
sse_encode_box_autoadd_rtc_send_file_response(status, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1103,7 +1197,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
sse_encode_String(pin, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1129,7 +1223,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCReceiveController(that, serializer);
sse_encode_Set_String_None(selection, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1157,7 +1251,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCSendController(that, serializer);
sse_encode_StreamSink_rtc_file_error_Sse(sink, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1184,7 +1278,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCSendController(that, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_Set_String_None,
@@ -1212,7 +1306,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCSendController(that, serializer);
sse_encode_StreamSink_rtc_status_Sse(sink, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1240,7 +1334,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCSendController(that, serializer);
sse_encode_String(fileId, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCFileSender,
@@ -1266,7 +1360,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRTCSendController(that, serializer);
sse_encode_String(pin, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1305,7 +1399,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
onConnection,
serializer,
);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1331,7 +1425,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
SyncTask(
callFfi: () {
final serializer = SseSerializer(generalizedFrbRustBinding);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!;
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!;
},
codec: SseCodec(
decodeSuccessData: sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsCancellationToken,
@@ -1359,7 +1453,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_String(cert, serializer);
sse_encode_ls_http_client_version(version, serializer);
sse_encode_opt_box_autoadd_u_32(timeoutMs, serializer);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!;
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!;
},
codec: SseCodec(
decodeSuccessData: sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpClient,
@@ -1383,7 +1477,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39, port: port_);
},
codec: SseCodec(
decodeSuccessData:
@@ -1408,7 +1502,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 40, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -1432,7 +1526,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 41, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_key_pair,
@@ -1477,7 +1571,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_opt_String(pin, serializer);
sse_encode_opt_box_autoadd_web_send_params(webSend, serializer);
sse_encode_opt_String(showToken, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 42, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer,
@@ -1503,7 +1597,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_String(cert, serializer);
sse_encode_String(publicKey, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 40, port: port_);
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 43, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -2445,9 +2539,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
);
case 1:
return RsServerEvent_PrepareUpload(
ip: dco_decode_String(raw[1]),
info: dco_decode_box_autoadd_register_dto_v_2(raw[2]),
files: dco_decode_Map_String_file_dto_None(raw[3]),
sessionId: dco_decode_String(raw[1]),
ip: dco_decode_String(raw[2]),
info: dco_decode_box_autoadd_register_dto_v_2(raw[3]),
files: dco_decode_Map_String_file_dto_None(raw[4]),
);
case 2:
return RsServerEvent_FileUpload(
@@ -2461,18 +2556,27 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
reason: dco_decode_session_end_reason_v_2(raw[2]),
);
case 4:
return RsServerEvent_PrepareUploadAborted(
sessionId: dco_decode_String(raw[1]),
);
case 5:
return RsServerEvent_CancelReceived(
ip: dco_decode_String(raw[1]),
sessionId: dco_decode_String(raw[2]),
);
case 6:
return RsServerEvent_WebPrepareDownload(
ip: dco_decode_String(raw[1]),
sessionId: dco_decode_String(raw[2]),
userAgent: dco_decode_opt_String(raw[3]),
);
case 5:
case 7:
return RsServerEvent_WebFileDownload(
sessionId: dco_decode_String(raw[1]),
fileId: dco_decode_String(raw[2]),
file: dco_decode_box_autoadd_file_dto(raw[3]),
);
case 6:
case 8:
return RsServerEvent_Show(
args: dco_decode_list_String(raw[1]),
);
@@ -3587,10 +3691,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
var var_info = sse_decode_box_autoadd_register_dto_v_2(deserializer);
return RsServerEvent_Register(ip: var_ip, info: var_info);
case 1:
var var_sessionId = sse_decode_String(deserializer);
var var_ip = sse_decode_String(deserializer);
var var_info = sse_decode_box_autoadd_register_dto_v_2(deserializer);
var var_files = sse_decode_Map_String_file_dto_None(deserializer);
return RsServerEvent_PrepareUpload(ip: var_ip, info: var_info, files: var_files);
return RsServerEvent_PrepareUpload(sessionId: var_sessionId, ip: var_ip, info: var_info, files: var_files);
case 2:
var var_sessionId = sse_decode_String(deserializer);
var var_fileId = sse_decode_String(deserializer);
@@ -3601,16 +3706,23 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
var var_reason = sse_decode_session_end_reason_v_2(deserializer);
return RsServerEvent_SessionEnd(sessionId: var_sessionId, reason: var_reason);
case 4:
var var_sessionId = sse_decode_String(deserializer);
return RsServerEvent_PrepareUploadAborted(sessionId: var_sessionId);
case 5:
var var_ip = sse_decode_String(deserializer);
var var_sessionId = sse_decode_String(deserializer);
return RsServerEvent_CancelReceived(ip: var_ip, sessionId: var_sessionId);
case 6:
var var_ip = sse_decode_String(deserializer);
var var_sessionId = sse_decode_String(deserializer);
var var_userAgent = sse_decode_opt_String(deserializer);
return RsServerEvent_WebPrepareDownload(ip: var_ip, sessionId: var_sessionId, userAgent: var_userAgent);
case 5:
case 7:
var var_sessionId = sse_decode_String(deserializer);
var var_fileId = sse_decode_String(deserializer);
var var_file = sse_decode_box_autoadd_file_dto(deserializer);
return RsServerEvent_WebFileDownload(sessionId: var_sessionId, fileId: var_fileId, file: var_file);
case 6:
case 8:
var var_args = sse_decode_list_String(deserializer);
return RsServerEvent_Show(args: var_args);
default:
@@ -4726,8 +4838,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_i_32(0, serializer);
sse_encode_String(ip, serializer);
sse_encode_box_autoadd_register_dto_v_2(info, serializer);
case RsServerEvent_PrepareUpload(ip: final ip, info: final info, files: final files):
case RsServerEvent_PrepareUpload(sessionId: final sessionId, ip: final ip, info: final info, files: final files):
sse_encode_i_32(1, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_String(ip, serializer);
sse_encode_box_autoadd_register_dto_v_2(info, serializer);
sse_encode_Map_String_file_dto_None(files, serializer);
@@ -4740,18 +4853,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_i_32(3, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_session_end_reason_v_2(reason, serializer);
case RsServerEvent_WebPrepareDownload(ip: final ip, sessionId: final sessionId, userAgent: final userAgent):
case RsServerEvent_PrepareUploadAborted(sessionId: final sessionId):
sse_encode_i_32(4, serializer);
sse_encode_String(sessionId, serializer);
case RsServerEvent_CancelReceived(ip: final ip, sessionId: final sessionId):
sse_encode_i_32(5, serializer);
sse_encode_String(ip, serializer);
sse_encode_String(sessionId, serializer);
case RsServerEvent_WebPrepareDownload(ip: final ip, sessionId: final sessionId, userAgent: final userAgent):
sse_encode_i_32(6, serializer);
sse_encode_String(ip, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_opt_String(userAgent, serializer);
case RsServerEvent_WebFileDownload(sessionId: final sessionId, fileId: final fileId, file: final file):
sse_encode_i_32(5, serializer);
sse_encode_i_32(7, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_String(fileId, serializer);
sse_encode_box_autoadd_file_dto(file, serializer);
case RsServerEvent_Show(args: final args):
sse_encode_i_32(6, serializer);
sse_encode_i_32(8, serializer);
sse_encode_list_String(args, serializer);
}
}
@@ -5097,6 +5217,16 @@ class RsHttpServerImpl extends RustOpaque implements RsHttpServer {
rustArcDecrementStrongCountPtr: RustLib.instance.api.rust_arc_decrement_strong_count_RsHttpServerPtr,
);
/// Cancels the active upload session, e.g. because the user aborted the
/// transfer on the receiving side.
///
/// Uploads that are already in progress still run to completion, but new
/// upload requests are rejected and a new session can be created.
/// No [RsServerEvent::SessionEnd] is emitted: the application initiated
/// the cancellation itself.
Future<void> cancelSession({required String sessionId}) =>
RustLib.instance.api.crateApiServerRsHttpServerCancelSession(that: this, sessionId: sessionId);
/// Emits server events until the server is stopped.
/// Can only be listened to once.
///
@@ -5106,6 +5236,22 @@ class RsHttpServerImpl extends RustOpaque implements RsHttpServer {
that: this,
);
/// Rejects the pending [RsServerEvent::WebFileDownload] event, e.g. because
/// the application failed to resolve a source for the file content.
///
/// The download request fails with an error response.
/// Does nothing if the download was already answered.
Future<void> rejectFileDownload({required String sessionId, required String fileId}) =>
RustLib.instance.api.crateApiServerRsHttpServerRejectFileDownload(that: this, sessionId: sessionId, fileId: fileId);
/// Rejects the pending [RsServerEvent::FileUpload] event, e.g. because
/// the application failed to prepare a save target for the file.
///
/// The upload request fails with an error response and the file is marked
/// as failed. Does nothing if the upload was already answered.
Future<void> rejectFileUpload({required String sessionId, required String fileId}) =>
RustLib.instance.api.crateApiServerRsHttpServerRejectFileUpload(that: this, sessionId: sessionId, fileId: fileId);
/// Answers the pending [RsServerEvent::WebFileDownload] event with the source
/// the file content should be read from (either a path or a file descriptor).
///
@@ -5116,8 +5262,23 @@ class RsHttpServerImpl extends RustOpaque implements RsHttpServer {
/// Answers the pending [RsServerEvent::FileUpload] event with the target
/// the file should be saved to (either a path or a file descriptor)
/// and waits until the file has been received completely.
Future<void> respondFileUpload({required String sessionId, required String fileId, String? path, int? fileDescriptor}) => RustLib.instance.api
.crateApiServerRsHttpServerRespondFileUpload(that: this, sessionId: sessionId, fileId: fileId, path: path, fileDescriptor: fileDescriptor);
///
/// The progress (fraction of [file_size]) is emitted on [sink]
/// while the file is being received.
Stream<double> respondFileUpload({
required String sessionId,
required String fileId,
String? path,
int? fileDescriptor,
required BigInt fileSize,
}) => RustLib.instance.api.crateApiServerRsHttpServerRespondFileUpload(
that: this,
sessionId: sessionId,
fileId: fileId,
path: path,
fileDescriptor: fileDescriptor,
fileSize: fileSize,
);
/// Answers the pending [RsServerEvent::WebPrepareDownload] event.
///
@@ -5133,6 +5294,7 @@ class RsHttpServerImpl extends RustOpaque implements RsHttpServer {
RustLib.instance.api.crateApiServerRsHttpServerRespondPrepareUpload(that: this, acceptedFileIds: acceptedFileIds);
/// Stops the server.
/// Returns after the listeners are closed, so the port can be bound again.
Future<void> stop() => RustLib.instance.api.crateApiServerRsHttpServerStop(
that: this,
);
@@ -36,6 +36,7 @@ class HttpServerStartTask implements BaseHttpServerTask {
}
/// Stops the HTTP server.
/// The stream of this task completes once the server has released the port.
class HttpServerStopTask implements BaseHttpServerTask {}
/// Answers a pending [HttpServerPrepareUploadEvent].
@@ -53,19 +54,52 @@ class HttpServerPrepareUploadDecisionTask implements BaseHttpServerTask {
/// should be saved to: either a file [path] or a writable [fileDescriptor] (Android).
///
/// The file is written by the Rust server itself.
/// A [HttpServerFileUploadResultEvent] is emitted on the stream of this task
/// once the file has been received completely (or failed).
/// [HttpServerFileUploadProgressEvent]s are emitted on the stream of this task
/// while the file is being received, followed by a final
/// [HttpServerFileUploadResultEvent] once the file has been received
/// completely (or failed).
class HttpServerFileUploadTargetTask implements BaseHttpServerTask {
final String sessionId;
final String fileId;
final String? path;
final int? fileDescriptor;
/// The expected file size in bytes, used to compute the progress.
final int fileSize;
HttpServerFileUploadTargetTask({
required this.sessionId,
required this.fileId,
required this.path,
required this.fileDescriptor,
required this.fileSize,
});
}
/// Rejects a pending [HttpServerFileUploadEvent], e.g. because preparing the
/// save target for the file failed. The upload request fails with an error
/// response and the file is marked as failed; the session itself continues.
/// Does nothing if the upload was already answered with a
/// [HttpServerFileUploadTargetTask].
class HttpServerRejectFileUploadTask implements BaseHttpServerTask {
final String sessionId;
final String fileId;
HttpServerRejectFileUploadTask({
required this.sessionId,
required this.fileId,
});
}
/// Cancels the active upload session, e.g. because the user aborted the
/// transfer on the receiving side. Uploads that are already in progress still
/// run to completion, but new upload requests are rejected and a new session
/// can be created. No [HttpServerSessionEndEvent] is emitted.
class HttpServerCancelSessionTask implements BaseHttpServerTask {
final String sessionId;
HttpServerCancelSessionTask({
required this.sessionId,
});
}
@@ -100,9 +134,27 @@ class HttpServerFileDownloadTargetTask implements BaseHttpServerTask {
});
}
/// Rejects a pending [HttpServerWebFileDownloadEvent], e.g. because no source
/// for the file content could be resolved. The download request fails with an
/// error response. Does nothing if the download was already answered with a
/// [HttpServerFileDownloadTargetTask].
class HttpServerRejectFileDownloadTask implements BaseHttpServerTask {
final String sessionId;
final String fileId;
HttpServerRejectFileDownloadTask({
required this.sessionId,
required this.fileId,
});
}
/// A message sent from the server isolate to the main isolate.
sealed class HttpServerEvent {}
/// The server has been started and is listening.
/// Always the first event emitted by a [HttpServerStartTask].
class HttpServerStartedEvent extends HttpServerEvent {}
/// A device registered itself on this server.
class HttpServerRegisterEvent extends HttpServerEvent {
final String ip;
@@ -117,11 +169,14 @@ class HttpServerRegisterEvent extends HttpServerEvent {
/// A sender requests to upload files.
/// Must be answered with a [HttpServerPrepareUploadDecisionTask].
class HttpServerPrepareUploadEvent extends HttpServerEvent {
/// The session ID the upload session will have when the request is accepted.
final String sessionId;
final String ip;
final RegisterDtoV2 info;
final Map<String, FileDto> files;
HttpServerPrepareUploadEvent({
required this.sessionId,
required this.ip,
required this.info,
required this.files,
@@ -142,6 +197,19 @@ class HttpServerFileUploadEvent extends HttpServerEvent {
});
}
/// The progress of a [HttpServerFileUploadTargetTask] as a fraction (0.0 to 1.0).
class HttpServerFileUploadProgressEvent extends HttpServerEvent {
final String sessionId;
final String fileId;
final double progress;
HttpServerFileUploadProgressEvent({
required this.sessionId,
required this.fileId,
required this.progress,
});
}
/// The result of a [HttpServerFileUploadTargetTask].
class HttpServerFileUploadResultEvent extends HttpServerEvent {
final String sessionId;
@@ -168,6 +236,30 @@ class HttpServerSessionEndEvent extends HttpServerEvent {
});
}
/// A prepare-upload request was aborted before a session was created,
/// e.g. the sender disconnected while the application was still deciding.
/// The [HttpServerPrepareUploadEvent] with the same [sessionId]
/// no longer needs to be answered.
class HttpServerPrepareUploadAbortedEvent extends HttpServerEvent {
final String sessionId;
HttpServerPrepareUploadAbortedEvent({required this.sessionId});
}
/// The remote device cancels a transfer this application is currently
/// *sending* to it. [sessionId] is the session ID issued by the remote device
/// during prepare-upload. The application must verify that [ip] matches the
/// target of the send session before cancelling it.
class HttpServerCancelReceivedEvent extends HttpServerEvent {
final String ip;
final String sessionId;
HttpServerCancelReceivedEvent({
required this.ip,
required this.sessionId,
});
}
/// A web client requests to download the shared files.
/// Must be answered with a [HttpServerPrepareDownloadDecisionTask].
class HttpServerWebPrepareDownloadEvent extends HttpServerEvent {
@@ -220,25 +312,45 @@ Future<void> setupHttpServerIsolate(
switch (task.data) {
case HttpServerStartTask startTask:
final syncState = ref.read(syncProvider);
final events = await ref
.read(httpServerProvider)
.start(
port: syncState.port,
tls: syncState.protocol == ProtocolType.https
? TlsConfig(
cert: syncState.securityContext.certificate,
privateKey: syncState.securityContext.privateKey,
)
: null,
alias: syncState.alias,
version: protocolVersion,
deviceModel: syncState.deviceInfo.deviceModel,
deviceType: syncState.deviceInfo.deviceType.toRust(),
fingerprint: syncState.securityContext.certificateHash,
pin: startTask.pin,
webSend: startTask.webSend,
showToken: startTask.showToken,
);
final Stream<RsServerEvent> events;
try {
events = await ref
.read(httpServerProvider)
.start(
port: syncState.port,
tls: syncState.protocol == ProtocolType.https
? TlsConfig(
cert: syncState.securityContext.certificate,
privateKey: syncState.securityContext.privateKey,
)
: null,
alias: syncState.alias,
version: protocolVersion,
deviceModel: syncState.deviceInfo.deviceModel,
deviceType: syncState.deviceInfo.deviceType.toRust(),
fingerprint: syncState.securityContext.certificateHash,
pin: startTask.pin,
webSend: startTask.webSend,
showToken: startTask.showToken,
);
} catch (e) {
// Starting failed (e.g. the port is already in use).
// The error must be sendable across the isolate boundary.
sendToMain(
IsolateTaskStreamResult.error(
id: task.id,
error: e.humanErrorMessage,
),
);
return;
}
sendToMain(
IsolateTaskStreamResult.event(
id: task.id,
data: HttpServerStartedEvent(),
),
);
try {
await for (final event in events) {
@@ -247,7 +359,8 @@ Future<void> setupHttpServerIsolate(
id: task.id,
data: switch (event) {
RsServerEvent_Register(:final ip, :final info) => HttpServerRegisterEvent(ip: ip, info: info),
RsServerEvent_PrepareUpload(:final ip, :final info, :final files) => HttpServerPrepareUploadEvent(
RsServerEvent_PrepareUpload(:final sessionId, :final ip, :final info, :final files) => HttpServerPrepareUploadEvent(
sessionId: sessionId,
ip: ip,
info: info,
files: files,
@@ -261,6 +374,13 @@ Future<void> setupHttpServerIsolate(
sessionId: sessionId,
reason: reason,
),
RsServerEvent_PrepareUploadAborted(:final sessionId) => HttpServerPrepareUploadAbortedEvent(
sessionId: sessionId,
),
RsServerEvent_CancelReceived(:final ip, :final sessionId) => HttpServerCancelReceivedEvent(
ip: ip,
sessionId: sessionId,
),
RsServerEvent_WebPrepareDownload(:final ip, :final sessionId, :final userAgent) => HttpServerWebPrepareDownloadEvent(
ip: ip,
sessionId: sessionId,
@@ -286,6 +406,11 @@ Future<void> setupHttpServerIsolate(
return;
case HttpServerStopTask _:
await ref.read(httpServerProvider).stop();
sendToMain(
IsolateTaskStreamResult.done(
id: task.id,
),
);
return;
case HttpServerPrepareUploadDecisionTask decisionTask:
await ref.read(httpServerProvider).respondPrepareUpload(acceptedFileIds: decisionTask.acceptedFileIds);
@@ -293,14 +418,27 @@ Future<void> setupHttpServerIsolate(
case HttpServerFileUploadTargetTask targetTask:
String? error;
try {
await ref
final progressStream = ref
.read(httpServerProvider)
.respondFileUpload(
sessionId: targetTask.sessionId,
fileId: targetTask.fileId,
path: targetTask.path,
fileDescriptor: targetTask.fileDescriptor,
fileSize: targetTask.fileSize,
);
await for (final progress in progressStream) {
sendToMain(
IsolateTaskStreamResult.event(
id: task.id,
data: HttpServerFileUploadProgressEvent(
sessionId: targetTask.sessionId,
fileId: targetTask.fileId,
progress: progress,
),
),
);
}
} catch (e) {
error = e.humanErrorMessage;
}
@@ -321,6 +459,17 @@ Future<void> setupHttpServerIsolate(
),
);
return;
case HttpServerRejectFileUploadTask rejectTask:
await ref
.read(httpServerProvider)
.rejectFileUpload(
sessionId: rejectTask.sessionId,
fileId: rejectTask.fileId,
);
return;
case HttpServerCancelSessionTask cancelTask:
await ref.read(httpServerProvider).cancelSession(sessionId: cancelTask.sessionId);
return;
case HttpServerPrepareDownloadDecisionTask decisionTask:
await ref
.read(httpServerProvider)
@@ -339,6 +488,14 @@ Future<void> setupHttpServerIsolate(
fileDescriptor: targetTask.fileDescriptor,
);
return;
case HttpServerRejectFileDownloadTask rejectTask:
await ref
.read(httpServerProvider)
.rejectFileDownload(
sessionId: rejectTask.sessionId,
fileId: rejectTask.fileId,
);
return;
}
},
);
@@ -228,22 +228,21 @@ class IsolateHttpServerStartAction extends ReduxActionWithResult<IsolateControll
}
}
class IsolateHttpServerStopAction extends ReduxAction<IsolateController, ParentIsolateState> {
/// Stops the HTTP server.
/// Completes once the server has released the port, so the port can be bound again.
class IsolateHttpServerStopAction extends AsyncReduxAction<IsolateController, ParentIsolateState> {
@override
ParentIsolateState reduce() {
Future<ParentIsolateState> reduce() async {
final connection = state.httpServer;
if (connection == null) {
throw StateError('httpServer is not initialized');
}
connection.sendToIsolate(
SendToIsolateData(
syncState: null,
data: IsolateTask(
data: HttpServerStopTask(),
),
),
);
await connection
.sendWrappedTaskAndListenStream(
task: HttpServerStopTask(),
)
.drain<void>();
return state;
}
@@ -283,6 +282,7 @@ class IsolateHttpServerPrepareUploadDecisionAction extends ReduxAction<IsolateCo
/// Answers a pending [HttpServerFileUploadEvent] with the target the file
/// should be saved to (either a [path] or a writable [fileDescriptor]).
/// [onProgress] is called with the progress (0.0 to 1.0) while the file is being received.
/// The returned future completes when the file has been received completely
/// and throws if saving the file failed.
class IsolateHttpServerFileUploadTargetAction extends ReduxActionWithResult<IsolateController, ParentIsolateState, Future<void>> {
@@ -290,12 +290,16 @@ class IsolateHttpServerFileUploadTargetAction extends ReduxActionWithResult<Isol
final String fileId;
final String? path;
final int? fileDescriptor;
final int fileSize;
final void Function(double progress)? onProgress;
IsolateHttpServerFileUploadTargetAction({
required this.sessionId,
required this.fileId,
required this.path,
required this.fileDescriptor,
required this.fileSize,
required this.onProgress,
});
@override
@@ -311,6 +315,7 @@ class IsolateHttpServerFileUploadTargetAction extends ReduxActionWithResult<Isol
fileId: fileId,
path: path,
fileDescriptor: fileDescriptor,
fileSize: fileSize,
),
);
@@ -319,11 +324,16 @@ class IsolateHttpServerFileUploadTargetAction extends ReduxActionWithResult<Isol
Future<void> _awaitResult(Stream<HttpServerEvent> events) async {
await for (final event in events) {
if (event case HttpServerFileUploadResultEvent(:final error)) {
if (error != null) {
throw HttpServerFileUploadException(error);
}
return;
switch (event) {
case HttpServerFileUploadProgressEvent(:final progress):
onProgress?.call(progress);
case HttpServerFileUploadResultEvent(:final error):
if (error != null) {
throw HttpServerFileUploadException(error);
}
return;
default:
break;
}
}
@@ -331,6 +341,75 @@ class IsolateHttpServerFileUploadTargetAction extends ReduxActionWithResult<Isol
}
}
/// Rejects a pending [HttpServerFileUploadEvent], e.g. because preparing the
/// save target for the file failed. The sender receives an error response for
/// this file and the session itself continues.
/// Does nothing if the upload was already answered with a
/// [IsolateHttpServerFileUploadTargetAction].
class IsolateHttpServerRejectFileUploadAction extends ReduxAction<IsolateController, ParentIsolateState> {
final String sessionId;
final String fileId;
IsolateHttpServerRejectFileUploadAction({
required this.sessionId,
required this.fileId,
});
@override
ParentIsolateState reduce() {
final connection = state.httpServer;
if (connection == null) {
throw StateError('httpServer is not initialized');
}
connection.sendToIsolate(
SendToIsolateData(
syncState: null,
data: IsolateTask(
data: HttpServerRejectFileUploadTask(
sessionId: sessionId,
fileId: fileId,
),
),
),
);
return state;
}
}
/// Cancels the active upload session of the HTTP server, e.g. because the
/// user aborted the transfer on the receiving side.
/// No [HttpServerSessionEndEvent] is emitted.
class IsolateHttpServerCancelSessionAction extends ReduxAction<IsolateController, ParentIsolateState> {
final String sessionId;
IsolateHttpServerCancelSessionAction({
required this.sessionId,
});
@override
ParentIsolateState reduce() {
final connection = state.httpServer;
if (connection == null) {
throw StateError('httpServer is not initialized');
}
connection.sendToIsolate(
SendToIsolateData(
syncState: null,
data: IsolateTask(
data: HttpServerCancelSessionTask(
sessionId: sessionId,
),
),
),
);
return state;
}
}
/// Answers a pending [HttpServerWebPrepareDownloadEvent].
class IsolateHttpServerPrepareDownloadDecisionAction extends ReduxAction<IsolateController, ParentIsolateState> {
final String sessionId;
@@ -406,6 +485,43 @@ class IsolateHttpServerFileDownloadTargetAction extends ReduxAction<IsolateContr
}
}
/// Rejects a pending [HttpServerWebFileDownloadEvent], e.g. because no source
/// for the file content could be resolved. The web client receives an error
/// response for this file.
/// Does nothing if the download was already answered with a
/// [IsolateHttpServerFileDownloadTargetAction].
class IsolateHttpServerRejectFileDownloadAction extends ReduxAction<IsolateController, ParentIsolateState> {
final String sessionId;
final String fileId;
IsolateHttpServerRejectFileDownloadAction({
required this.sessionId,
required this.fileId,
});
@override
ParentIsolateState reduce() {
final connection = state.httpServer;
if (connection == null) {
throw StateError('httpServer is not initialized');
}
connection.sendToIsolate(
SendToIsolateData(
syncState: null,
data: IsolateTask(
data: HttpServerRejectFileDownloadTask(
sessionId: sessionId,
fileId: fileId,
),
),
),
);
return state;
}
}
/// Saving a file received by the HTTP server failed.
class HttpServerFileUploadException implements Exception {
final String message;
@@ -131,6 +131,18 @@ class _PublishSyncStateAction extends ReduxAction<IsolateController, ParentIsola
data: null,
),
);
state.httpUpload?.sendToIsolate(
SendToIsolateData(
syncState: syncState,
data: null,
),
);
state.httpServer?.sendToIsolate(
SendToIsolateData(
syncState: syncState,
data: null,
),
);
return state.copyWith(
syncState: syncState,
@@ -52,21 +52,42 @@ class HttpServerService {
}
/// Answers a pending file upload with the target the file should be saved to
/// (either a [path] or a [fileDescriptor]) and waits until the file has been received completely.
Future<void> respondFileUpload({
/// (either a [path] or a [fileDescriptor]).
///
/// The returned stream emits the progress (fraction of [fileSize]) while the
/// file is being received and closes once the file has been received
/// completely (or errors when saving failed).
Stream<double> respondFileUpload({
required String sessionId,
required String fileId,
required String? path,
required int? fileDescriptor,
}) async {
await _requireServer().respondFileUpload(
required int fileSize,
}) {
return _requireServer().respondFileUpload(
sessionId: sessionId,
fileId: fileId,
path: path,
fileDescriptor: fileDescriptor,
fileSize: BigInt.from(fileSize),
);
}
/// Rejects a pending file upload, e.g. because no save target could be
/// prepared. The upload request fails with an error response and the file is
/// marked as failed; the session itself continues.
/// Does nothing if the upload was already answered via [respondFileUpload].
Future<void> rejectFileUpload({required String sessionId, required String fileId}) async {
await _requireServer().rejectFileUpload(sessionId: sessionId, fileId: fileId);
}
/// Cancels the active upload session. Uploads that are already in progress
/// still run to completion, but new upload requests are rejected and a new
/// session can be created. No session-end event is emitted.
Future<void> cancelSession({required String sessionId}) async {
await _requireServer().cancelSession(sessionId: sessionId);
}
/// Answers a pending web prepare-download request.
/// [accept] grants the download; `false` declines it.
Future<void> respondPrepareDownload({required String sessionId, required bool accept}) async {
@@ -89,7 +110,15 @@ class HttpServerService {
);
}
/// Rejects a pending web file download, e.g. because no content source could
/// be resolved. The download request fails with an error response.
/// Does nothing if the download was already answered via [respondFileDownload].
Future<void> rejectFileDownload({required String sessionId, required String fileId}) async {
await _requireServer().rejectFileDownload(sessionId: sessionId, fileId: fileId);
}
/// Stops the server. The event stream returned by [start] will end.
/// Completes once the port is released and can be bound again.
Future<void> stop() async {
final server = _server;
_server = null;
@@ -4,6 +4,7 @@ import 'package:localsend_isolates/model/dto/file_dto.dart';
import 'package:localsend_isolates/model/dto/multicast_dto.dart';
import 'package:localsend_isolates/rust/api/http.dart' as rust_http;
import 'package:localsend_isolates/rust/api/model.dart' as rust_model;
import 'package:localsend_isolates/rust/api/server.dart' as rust_server;
import 'package:localsend_isolates/src/isolate/child/sync_provider.dart';
import 'package:mime/mime.dart';
@@ -108,6 +109,43 @@ extension HumanErrorMessageExt on Object {
}
}
extension RustFileDtoExt on rust_model.FileDto {
FileDto toDart() {
return FileDto(
id: id,
fileName: fileName,
size: size.toInt(),
fileType: decodeFromMime(fileType),
hash: sha256,
preview: preview,
metadata: metadata != null
? FileMetadata(
lastModified: metadata!.modified != null ? DateTime.tryParse(metadata!.modified!) : null,
lastAccessed: metadata!.accessed != null ? DateTime.tryParse(metadata!.accessed!) : null,
)
: null,
);
}
}
extension RegisterDtoV2Ext on rust_server.RegisterDtoV2 {
Device toDevice(String ip, DiscoveryMethod? method) {
return Device(
signalingId: null,
ip: ip,
version: version,
port: port,
https: protocol == rust_server.ProtocolTypeV2.https,
fingerprint: fingerprint,
alias: alias,
deviceModel: deviceModel,
deviceType: deviceType?.toDart() ?? DeviceType.desktop,
download: download,
discoveryMethods: method == null ? const {} : {method},
);
}
}
extension RegisterResponseDtoExt on rust_model.RegisterResponseDto {
Device toDevice(String ip, int port, bool https, DiscoveryMethod method) {
return Device(
+1
View File
@@ -2773,6 +2773,7 @@ dependencies = [
"bytes",
"futures-core",
"futures-sink",
"futures-util",
"pin-project-lite",
"tokio",
]
@@ -25,6 +25,8 @@ pub enum RsServerEvent {
/// A sender requests to upload files via `POST /api/localsend/v2/prepare-upload`.
PrepareUpload {
/// The session ID the upload session will have when the request is accepted.
session_id: String,
ip: String,
info: RegisterDtoV2,
files: HashMap<String, FileDto>,
@@ -43,6 +45,18 @@ pub enum RsServerEvent {
reason: SessionEndReasonV2,
},
/// A prepare-upload request was aborted before a session was created,
/// e.g. the sender disconnected while the application was still deciding.
/// The [RsServerEvent::PrepareUpload] with the same session ID
/// no longer needs to be answered.
PrepareUploadAborted { session_id: String },
/// `POST /api/localsend/v2/cancel` was received for a session this server
/// does not manage: the remote device cancels a transfer this application
/// is currently *sending* to it. The application must verify that [ip]
/// matches the target of the send session before cancelling it.
CancelReceived { ip: String, session_id: String },
/// A web client requests to download the shared files via `POST /api/localsend/v2/prepare-download`.
///
/// Must be answered with [RsHttpServer::respond_prepare_download].
@@ -70,9 +84,10 @@ pub enum RsServerEvent {
}
pub struct RsHttpServer {
handle: localsend::http::server::ServerHandle,
event_rx: Mutex<Option<mpsc::Receiver<ServerEventV2>>>,
stop_tx: Mutex<Option<oneshot::Sender<()>>>,
pending_decision: Mutex<Option<oneshot::Sender<PrepareUploadDecisionV2>>>,
pending_decision: Mutex<Option<(String, oneshot::Sender<PrepareUploadDecisionV2>)>>,
pending_uploads: Mutex<HashMap<(String, String), oneshot::Sender<FileUploadTarget>>>,
web_event_rx: Mutex<Option<mpsc::Receiver<WebSendEvent>>>,
pending_download_decisions: Mutex<HashMap<String, oneshot::Sender<bool>>>,
@@ -148,7 +163,7 @@ pub async fn start_server(
None => (None, None),
};
localsend::http::server::start_with_port(
let handle = localsend::http::server::start_with_port(
port,
tls,
ClientInfo {
@@ -166,6 +181,7 @@ pub async fn start_server(
.await?;
Ok(RsHttpServer {
handle,
event_rx: Mutex::new(Some(event_rx)),
stop_tx: Mutex::new(Some(stop_tx)),
pending_decision: Mutex::new(None),
@@ -231,13 +247,15 @@ impl RsHttpServer {
});
}
ServerEventV2::PrepareUpload {
session_id,
ip,
info,
files,
decision_tx,
} => {
*self.pending_decision.lock().await = Some(decision_tx);
*self.pending_decision.lock().await = Some((session_id.clone(), decision_tx));
let _ = sink.add(RsServerEvent::PrepareUpload {
session_id,
ip: ip.to_string(),
info,
files,
@@ -267,6 +285,24 @@ impl RsHttpServer {
.retain(|(sid, _), _| sid != &session_id);
let _ = sink.add(RsServerEvent::SessionEnd { session_id, reason });
}
ServerEventV2::PrepareUploadAborted { session_id } => {
// Drop the stale decision responder (the request already ended).
// A newer prepare-upload request may already hold the slot;
// only clear it if it still belongs to the aborted request.
{
let mut pending = self.pending_decision.lock().await;
if pending.as_ref().is_some_and(|(sid, _)| sid == &session_id) {
*pending = None;
}
}
let _ = sink.add(RsServerEvent::PrepareUploadAborted { session_id });
}
ServerEventV2::CancelReceived { ip, session_id } => {
let _ = sink.add(RsServerEvent::CancelReceived {
ip: ip.to_string(),
session_id,
});
}
}
}
@@ -315,7 +351,7 @@ impl RsHttpServer {
&self,
accepted_file_ids: Option<Vec<String>>,
) -> anyhow::Result<()> {
let Some(decision_tx) = self.pending_decision.lock().await.take() else {
let Some((_, decision_tx)) = self.pending_decision.lock().await.take() else {
return Err(anyhow::anyhow!("No pending prepare-upload request"));
};
@@ -334,12 +370,17 @@ impl RsHttpServer {
/// Answers the pending [RsServerEvent::FileUpload] event with the target
/// the file should be saved to (either a path or a file descriptor)
/// and waits until the file has been received completely.
///
/// The progress (fraction of [file_size]) is emitted on [sink]
/// while the file is being received.
pub async fn respond_file_upload(
&self,
sink: StreamSink<f64>,
session_id: String,
file_id: String,
path: Option<String>,
file_descriptor: Option<i32>,
file_size: u64,
) -> anyhow::Result<()> {
let Some(target_tx) = self
.pending_uploads
@@ -350,8 +391,31 @@ impl RsHttpServer {
return Err(anyhow::anyhow!("No pending file upload for this file"));
};
let (progress_tx, mut progress_rx) = mpsc::channel::<u64>(16);
tokio::spawn(async move {
let mut last_emit = None::<std::time::Instant>;
while let Some(written) = progress_rx.recv().await {
let now = std::time::Instant::now();
let is_final = written >= file_size;
if !is_final {
if let Some(last) = last_emit {
if now.duration_since(last) < std::time::Duration::from_millis(20) {
continue;
}
}
}
last_emit = Some(now);
let progress = if file_size == 0 {
1.0
} else {
(written as f64 / file_size as f64).min(1.0)
};
let _ = sink.add(progress);
}
});
let (result_tx, result_rx) = oneshot::channel::<Result<(), String>>();
let target = resolve_upload_target(path, file_descriptor, result_tx)?;
let target = resolve_upload_target(path, file_descriptor, result_tx, progress_tx)?;
target_tx
.send(target)
@@ -364,6 +428,19 @@ impl RsHttpServer {
}
}
/// Rejects the pending [RsServerEvent::FileUpload] event, e.g. because
/// the application failed to prepare a save target for the file.
///
/// The upload request fails with an error response and the file is marked
/// as failed. Does nothing if the upload was already answered.
pub async fn reject_file_upload(&self, session_id: String, file_id: String) {
// Dropping the responder fails the request waiting for the target.
self.pending_uploads
.lock()
.await
.remove(&(session_id, file_id));
}
/// Answers the pending [RsServerEvent::WebPrepareDownload] event.
///
/// Passing `true` accepts the download request, `false` declines it.
@@ -417,10 +494,43 @@ impl RsHttpServer {
Ok(())
}
/// Rejects the pending [RsServerEvent::WebFileDownload] event, e.g. because
/// the application failed to resolve a source for the file content.
///
/// The download request fails with an error response.
/// Does nothing if the download was already answered.
pub async fn reject_file_download(&self, session_id: String, file_id: String) {
// Dropping the responder fails the request waiting for the content.
self.pending_downloads
.lock()
.await
.remove(&(session_id, file_id));
}
/// Cancels the active upload session, e.g. because the user aborted the
/// transfer on the receiving side.
///
/// Uploads that are already in progress still run to completion, but new
/// upload requests are rejected and a new session can be created.
/// No [RsServerEvent::SessionEnd] is emitted: the application initiated
/// the cancellation itself.
pub async fn cancel_session(&self, session_id: String) {
self.handle.cancel_v2_session(&session_id).await;
// Drop unanswered upload responders of this session so their requests
// fail instead of waiting for a target forever.
self.pending_uploads
.lock()
.await
.retain(|(sid, _), _| sid != &session_id);
}
/// Stops the server.
/// Returns after the listeners are closed, so the port can be bound again.
pub async fn stop(&self) {
if let Some(stop_tx) = self.stop_tx.lock().await.take() {
let _ = stop_tx.send(());
self.handle.wait_stopped().await;
}
}
}
@@ -438,11 +548,13 @@ fn resolve_upload_target(
path: Option<String>,
file_descriptor: Option<i32>,
result_tx: oneshot::Sender<Result<(), String>>,
progress_tx: mpsc::Sender<u64>,
) -> anyhow::Result<FileUploadTarget> {
match (path, file_descriptor) {
(Some(path), None) => Ok(FileUploadTarget::Path {
path: path.into(),
result_tx,
progress_tx: Some(progress_tx),
}),
(None, Some(file_descriptor)) => {
#[cfg(target_os = "android")]
@@ -450,11 +562,12 @@ fn resolve_upload_target(
Ok(FileUploadTarget::Fd {
fd: file_descriptor,
result_tx,
progress_tx: Some(progress_tx),
})
}
#[cfg(not(target_os = "android"))]
{
let _ = (file_descriptor, result_tx);
let _ = (file_descriptor, result_tx, progress_tx);
Err(anyhow::anyhow!(
"File descriptors are only supported on Android"
))
@@ -42,7 +42,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueMoi,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1029282510;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1916764705;
// Section: executor
@@ -711,6 +711,68 @@ fn wire__crate__api__http__RsHttpClient_upload_impl(
},
)
}
fn wire__crate__api__server__RsHttpServer_cancel_session_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "RsHttpServer_cancel_session",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let message = unsafe {
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
ptr_,
rust_vec_len_,
data_len_,
)
};
let mut deserializer =
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
let api_that = <RustOpaqueMoi<
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsHttpServer>,
>>::sse_decode(&mut deserializer);
let api_session_id = <String>::sse_decode(&mut deserializer);
deserializer.end();
move |context| async move {
transform_result_sse::<_, ()>(
(move || async move {
let mut api_that_guard = None;
let decode_indices_ =
flutter_rust_bridge::for_generated::lockable_compute_decode_order(
vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
&api_that, 0, false,
)],
);
for i in decode_indices_ {
match i {
0 => {
api_that_guard =
Some(api_that.lockable_decode_async_ref().await)
}
_ => unreachable!(),
}
}
let api_that_guard = api_that_guard.unwrap();
let output_ok = Result::<_, ()>::Ok({
crate::api::server::RsHttpServer::cancel_session(
&*api_that_guard,
api_session_id,
)
.await;
})?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__server__RsHttpServer_listen_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
@@ -773,6 +835,134 @@ fn wire__crate__api__server__RsHttpServer_listen_impl(
},
)
}
fn wire__crate__api__server__RsHttpServer_reject_file_download_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "RsHttpServer_reject_file_download",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let message = unsafe {
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
ptr_,
rust_vec_len_,
data_len_,
)
};
let mut deserializer =
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
let api_that = <RustOpaqueMoi<
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsHttpServer>,
>>::sse_decode(&mut deserializer);
let api_session_id = <String>::sse_decode(&mut deserializer);
let api_file_id = <String>::sse_decode(&mut deserializer);
deserializer.end();
move |context| async move {
transform_result_sse::<_, ()>(
(move || async move {
let mut api_that_guard = None;
let decode_indices_ =
flutter_rust_bridge::for_generated::lockable_compute_decode_order(
vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
&api_that, 0, false,
)],
);
for i in decode_indices_ {
match i {
0 => {
api_that_guard =
Some(api_that.lockable_decode_async_ref().await)
}
_ => unreachable!(),
}
}
let api_that_guard = api_that_guard.unwrap();
let output_ok = Result::<_, ()>::Ok({
crate::api::server::RsHttpServer::reject_file_download(
&*api_that_guard,
api_session_id,
api_file_id,
)
.await;
})?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__server__RsHttpServer_reject_file_upload_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "RsHttpServer_reject_file_upload",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let message = unsafe {
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
ptr_,
rust_vec_len_,
data_len_,
)
};
let mut deserializer =
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
let api_that = <RustOpaqueMoi<
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsHttpServer>,
>>::sse_decode(&mut deserializer);
let api_session_id = <String>::sse_decode(&mut deserializer);
let api_file_id = <String>::sse_decode(&mut deserializer);
deserializer.end();
move |context| async move {
transform_result_sse::<_, ()>(
(move || async move {
let mut api_that_guard = None;
let decode_indices_ =
flutter_rust_bridge::for_generated::lockable_compute_decode_order(
vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
&api_that, 0, false,
)],
);
for i in decode_indices_ {
match i {
0 => {
api_that_guard =
Some(api_that.lockable_decode_async_ref().await)
}
_ => unreachable!(),
}
}
let api_that_guard = api_that_guard.unwrap();
let output_ok = Result::<_, ()>::Ok({
crate::api::server::RsHttpServer::reject_file_upload(
&*api_that_guard,
api_session_id,
api_file_id,
)
.await;
})?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__server__RsHttpServer_respond_file_download_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
@@ -864,10 +1054,15 @@ fn wire__crate__api__server__RsHttpServer_respond_file_upload_impl(
let api_that = <RustOpaqueMoi<
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsHttpServer>,
>>::sse_decode(&mut deserializer);
let api_sink =
<StreamSink<f64, flutter_rust_bridge::for_generated::SseCodec>>::sse_decode(
&mut deserializer,
);
let api_session_id = <String>::sse_decode(&mut deserializer);
let api_file_id = <String>::sse_decode(&mut deserializer);
let api_path = <Option<String>>::sse_decode(&mut deserializer);
let api_file_descriptor = <Option<i32>>::sse_decode(&mut deserializer);
let api_file_size = <u64>::sse_decode(&mut deserializer);
deserializer.end();
move |context| async move {
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
@@ -891,10 +1086,12 @@ fn wire__crate__api__server__RsHttpServer_respond_file_upload_impl(
let api_that_guard = api_that_guard.unwrap();
let output_ok = crate::api::server::RsHttpServer::respond_file_upload(
&*api_that_guard,
api_sink,
api_session_id,
api_file_id,
api_path,
api_file_descriptor,
api_file_size,
)
.await?;
Ok(output_ok)
@@ -3504,6 +3701,7 @@ impl SseDecode for crate::api::server::RsServerEvent {
};
}
1 => {
let mut var_sessionId = <String>::sse_decode(deserializer);
let mut var_ip = <String>::sse_decode(deserializer);
let mut var_info = <crate::api::server::RegisterDtoV2>::sse_decode(deserializer);
let mut var_files =
@@ -3511,6 +3709,7 @@ impl SseDecode for crate::api::server::RsServerEvent {
deserializer,
);
return crate::api::server::RsServerEvent::PrepareUpload {
session_id: var_sessionId,
ip: var_ip,
info: var_info,
files: var_files,
@@ -3536,6 +3735,20 @@ impl SseDecode for crate::api::server::RsServerEvent {
};
}
4 => {
let mut var_sessionId = <String>::sse_decode(deserializer);
return crate::api::server::RsServerEvent::PrepareUploadAborted {
session_id: var_sessionId,
};
}
5 => {
let mut var_ip = <String>::sse_decode(deserializer);
let mut var_sessionId = <String>::sse_decode(deserializer);
return crate::api::server::RsServerEvent::CancelReceived {
ip: var_ip,
session_id: var_sessionId,
};
}
6 => {
let mut var_ip = <String>::sse_decode(deserializer);
let mut var_sessionId = <String>::sse_decode(deserializer);
let mut var_userAgent = <Option<String>>::sse_decode(deserializer);
@@ -3545,7 +3758,7 @@ impl SseDecode for crate::api::server::RsServerEvent {
user_agent: var_userAgent,
};
}
5 => {
7 => {
let mut var_sessionId = <String>::sse_decode(deserializer);
let mut var_fileId = <String>::sse_decode(deserializer);
let mut var_file = <crate::api::model::FileDto>::sse_decode(deserializer);
@@ -3555,7 +3768,7 @@ impl SseDecode for crate::api::server::RsServerEvent {
file: var_file,
};
}
6 => {
8 => {
let mut var_args = <Vec<String>>::sse_decode(deserializer);
return crate::api::server::RsServerEvent::Show { args: var_args };
}
@@ -3838,131 +4051,149 @@ fn pde_ffi_dispatcher_primary_impl(
),
9 => wire__crate__api__http__RsHttpClient_register_impl(port, ptr, rust_vec_len, data_len),
10 => wire__crate__api__http__RsHttpClient_upload_impl(port, ptr, rust_vec_len, data_len),
11 => wire__crate__api__server__RsHttpServer_listen_impl(port, ptr, rust_vec_len, data_len),
12 => wire__crate__api__server__RsHttpServer_respond_file_download_impl(
11 => wire__crate__api__server__RsHttpServer_cancel_session_impl(
port,
ptr,
rust_vec_len,
data_len,
),
13 => wire__crate__api__server__RsHttpServer_respond_file_upload_impl(
12 => wire__crate__api__server__RsHttpServer_listen_impl(port, ptr, rust_vec_len, data_len),
13 => wire__crate__api__server__RsHttpServer_reject_file_download_impl(
port,
ptr,
rust_vec_len,
data_len,
),
14 => wire__crate__api__server__RsHttpServer_respond_prepare_download_impl(
14 => wire__crate__api__server__RsHttpServer_reject_file_upload_impl(
port,
ptr,
rust_vec_len,
data_len,
),
15 => wire__crate__api__server__RsHttpServer_respond_prepare_upload_impl(
15 => wire__crate__api__server__RsHttpServer_respond_file_download_impl(
port,
ptr,
rust_vec_len,
data_len,
),
16 => wire__crate__api__server__RsHttpServer_stop_impl(port, ptr, rust_vec_len, data_len),
17 => wire__crate__api__webrtc__RtcFileReceiver_get_file_id_impl(
16 => wire__crate__api__server__RsHttpServer_respond_file_upload_impl(
port,
ptr,
rust_vec_len,
data_len,
),
18 => wire__crate__api__webrtc__RtcFileReceiver_receive_impl(
17 => wire__crate__api__server__RsHttpServer_respond_prepare_download_impl(
port,
ptr,
rust_vec_len,
data_len,
),
19 => wire__crate__api__webrtc__RtcFileSender_send_impl(port, ptr, rust_vec_len, data_len),
20 => wire__crate__api__webrtc__RtcReceiveController_decline_impl(
18 => wire__crate__api__server__RsHttpServer_respond_prepare_upload_impl(
port,
ptr,
rust_vec_len,
data_len,
),
21 => wire__crate__api__webrtc__RtcReceiveController_listen_error_impl(
19 => wire__crate__api__server__RsHttpServer_stop_impl(port, ptr, rust_vec_len, data_len),
20 => wire__crate__api__webrtc__RtcFileReceiver_get_file_id_impl(
port,
ptr,
rust_vec_len,
data_len,
),
22 => wire__crate__api__webrtc__RtcReceiveController_listen_files_impl(
21 => wire__crate__api__webrtc__RtcFileReceiver_receive_impl(
port,
ptr,
rust_vec_len,
data_len,
),
23 => wire__crate__api__webrtc__RtcReceiveController_listen_receiving_impl(
22 => wire__crate__api__webrtc__RtcFileSender_send_impl(port, ptr, rust_vec_len, data_len),
23 => wire__crate__api__webrtc__RtcReceiveController_decline_impl(
port,
ptr,
rust_vec_len,
data_len,
),
24 => wire__crate__api__webrtc__RtcReceiveController_listen_status_impl(
24 => wire__crate__api__webrtc__RtcReceiveController_listen_error_impl(
port,
ptr,
rust_vec_len,
data_len,
),
25 => wire__crate__api__webrtc__RtcReceiveController_send_file_status_impl(
25 => wire__crate__api__webrtc__RtcReceiveController_listen_files_impl(
port,
ptr,
rust_vec_len,
data_len,
),
26 => wire__crate__api__webrtc__RtcReceiveController_send_pin_impl(
26 => wire__crate__api__webrtc__RtcReceiveController_listen_receiving_impl(
port,
ptr,
rust_vec_len,
data_len,
),
27 => wire__crate__api__webrtc__RtcReceiveController_send_selection_impl(
27 => wire__crate__api__webrtc__RtcReceiveController_listen_status_impl(
port,
ptr,
rust_vec_len,
data_len,
),
28 => wire__crate__api__webrtc__RtcSendController_listen_error_impl(
28 => wire__crate__api__webrtc__RtcReceiveController_send_file_status_impl(
port,
ptr,
rust_vec_len,
data_len,
),
29 => wire__crate__api__webrtc__RtcSendController_listen_selected_files_impl(
29 => wire__crate__api__webrtc__RtcReceiveController_send_pin_impl(
port,
ptr,
rust_vec_len,
data_len,
),
30 => wire__crate__api__webrtc__RtcSendController_listen_status_impl(
30 => wire__crate__api__webrtc__RtcReceiveController_send_selection_impl(
port,
ptr,
rust_vec_len,
data_len,
),
31 => wire__crate__api__webrtc__RtcSendController_send_file_impl(
31 => wire__crate__api__webrtc__RtcSendController_listen_error_impl(
port,
ptr,
rust_vec_len,
data_len,
),
32 => wire__crate__api__webrtc__RtcSendController_send_pin_impl(
32 => wire__crate__api__webrtc__RtcSendController_listen_selected_files_impl(
port,
ptr,
rust_vec_len,
data_len,
),
33 => wire__crate__api__webrtc__connect_impl(port, ptr, rust_vec_len, data_len),
36 => wire__crate__api__stream__create_stream_impl(port, ptr, rust_vec_len, data_len),
37 => {
33 => wire__crate__api__webrtc__RtcSendController_listen_status_impl(
port,
ptr,
rust_vec_len,
data_len,
),
34 => wire__crate__api__webrtc__RtcSendController_send_file_impl(
port,
ptr,
rust_vec_len,
data_len,
),
35 => wire__crate__api__webrtc__RtcSendController_send_pin_impl(
port,
ptr,
rust_vec_len,
data_len,
),
36 => wire__crate__api__webrtc__connect_impl(port, ptr, rust_vec_len, data_len),
39 => wire__crate__api__stream__create_stream_impl(port, ptr, rust_vec_len, data_len),
40 => {
wire__crate__api__logging__enable_debug_logging_impl(port, ptr, rust_vec_len, data_len)
}
38 => wire__crate__api__crypto__generate_key_pair_impl(port, ptr, rust_vec_len, data_len),
39 => wire__crate__api__server__start_server_impl(port, ptr, rust_vec_len, data_len),
40 => wire__crate__api__crypto__verify_cert_impl(port, ptr, rust_vec_len, data_len),
41 => wire__crate__api__crypto__generate_key_pair_impl(port, ptr, rust_vec_len, data_len),
42 => wire__crate__api__server__start_server_impl(port, ptr, rust_vec_len, data_len),
43 => wire__crate__api__crypto__verify_cert_impl(port, ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
@@ -3977,8 +4208,8 @@ fn pde_ffi_dispatcher_sync_impl(
match func_id {
2 => wire__crate__api__stream__Dart2RustStreamSink_close_impl(ptr, rust_vec_len, data_len),
6 => wire__crate__api__http__RsCancellationToken_cancel_impl(ptr, rust_vec_len, data_len),
34 => wire__crate__api__http__create_cancellation_token_impl(ptr, rust_vec_len, data_len),
35 => wire__crate__api__http__create_client_impl(ptr, rust_vec_len, data_len),
37 => wire__crate__api__http__create_cancellation_token_impl(ptr, rust_vec_len, data_len),
38 => wire__crate__api__http__create_client_impl(ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
@@ -4632,8 +4863,14 @@ impl flutter_rust_bridge::IntoDart for crate::api::server::RsServerEvent {
info.into_into_dart().into_dart(),
]
.into_dart(),
crate::api::server::RsServerEvent::PrepareUpload { ip, info, files } => [
crate::api::server::RsServerEvent::PrepareUpload {
session_id,
ip,
info,
files,
} => [
1.into_dart(),
session_id.into_into_dart().into_dart(),
ip.into_into_dart().into_dart(),
info.into_into_dart().into_dart(),
files.into_into_dart().into_dart(),
@@ -4656,12 +4893,21 @@ impl flutter_rust_bridge::IntoDart for crate::api::server::RsServerEvent {
reason.into_into_dart().into_dart(),
]
.into_dart(),
crate::api::server::RsServerEvent::PrepareUploadAborted { session_id } => {
[4.into_dart(), session_id.into_into_dart().into_dart()].into_dart()
}
crate::api::server::RsServerEvent::CancelReceived { ip, session_id } => [
5.into_dart(),
ip.into_into_dart().into_dart(),
session_id.into_into_dart().into_dart(),
]
.into_dart(),
crate::api::server::RsServerEvent::WebPrepareDownload {
ip,
session_id,
user_agent,
} => [
4.into_dart(),
6.into_dart(),
ip.into_into_dart().into_dart(),
session_id.into_into_dart().into_dart(),
user_agent.into_into_dart().into_dart(),
@@ -4672,14 +4918,14 @@ impl flutter_rust_bridge::IntoDart for crate::api::server::RsServerEvent {
file_id,
file,
} => [
5.into_dart(),
7.into_dart(),
session_id.into_into_dart().into_dart(),
file_id.into_into_dart().into_dart(),
file.into_into_dart().into_dart(),
]
.into_dart(),
crate::api::server::RsServerEvent::Show { args } => {
[6.into_dart(), args.into_into_dart().into_dart()].into_dart()
[8.into_dart(), args.into_into_dart().into_dart()].into_dart()
}
_ => {
unimplemented!("");
@@ -5740,8 +5986,14 @@ impl SseEncode for crate::api::server::RsServerEvent {
<String>::sse_encode(ip, serializer);
<crate::api::server::RegisterDtoV2>::sse_encode(info, serializer);
}
crate::api::server::RsServerEvent::PrepareUpload { ip, info, files } => {
crate::api::server::RsServerEvent::PrepareUpload {
session_id,
ip,
info,
files,
} => {
<i32>::sse_encode(1, serializer);
<String>::sse_encode(session_id, serializer);
<String>::sse_encode(ip, serializer);
<crate::api::server::RegisterDtoV2>::sse_encode(info, serializer);
<std::collections::HashMap<String, crate::api::model::FileDto>>::sse_encode(
@@ -5763,12 +6015,21 @@ impl SseEncode for crate::api::server::RsServerEvent {
<String>::sse_encode(session_id, serializer);
<crate::api::server::SessionEndReasonV2>::sse_encode(reason, serializer);
}
crate::api::server::RsServerEvent::PrepareUploadAborted { session_id } => {
<i32>::sse_encode(4, serializer);
<String>::sse_encode(session_id, serializer);
}
crate::api::server::RsServerEvent::CancelReceived { ip, session_id } => {
<i32>::sse_encode(5, serializer);
<String>::sse_encode(ip, serializer);
<String>::sse_encode(session_id, serializer);
}
crate::api::server::RsServerEvent::WebPrepareDownload {
ip,
session_id,
user_agent,
} => {
<i32>::sse_encode(4, serializer);
<i32>::sse_encode(6, serializer);
<String>::sse_encode(ip, serializer);
<String>::sse_encode(session_id, serializer);
<Option<String>>::sse_encode(user_agent, serializer);
@@ -5778,13 +6039,13 @@ impl SseEncode for crate::api::server::RsServerEvent {
file_id,
file,
} => {
<i32>::sse_encode(5, serializer);
<i32>::sse_encode(7, serializer);
<String>::sse_encode(session_id, serializer);
<String>::sse_encode(file_id, serializer);
<crate::api::model::FileDto>::sse_encode(file, serializer);
}
crate::api::server::RsServerEvent::Show { args } => {
<i32>::sse_encode(6, serializer);
<i32>::sse_encode(8, serializer);
<Vec<String>>::sse_encode(args, serializer);
}
_ => {