@@ -69,6 +69,7 @@ struct SessionBuilderInner {
69
69
70
70
use_display_control : bool ,
71
71
enable_credssp : bool ,
72
+ outbound_message_size_limit : Option < u32 > ,
72
73
}
73
74
74
75
impl Default for SessionBuilderInner {
@@ -97,6 +98,7 @@ impl Default for SessionBuilderInner {
97
98
98
99
use_display_control : false ,
99
100
enable_credssp : true ,
101
+ outbound_message_size_limit : None ,
100
102
}
101
103
}
102
104
}
@@ -219,6 +221,16 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder {
219
221
|kdc_proxy_url: String | { self . 0 . borrow_mut( ) . kdc_proxy_url = Some ( kdc_proxy_url) } ;
220
222
|display_control: bool | { self . 0 . borrow_mut( ) . use_display_control = display_control } ;
221
223
|enable_credssp: bool | { self . 0 . borrow_mut( ) . enable_credssp = enable_credssp } ;
224
+ |outbound_message_size_limit: f64 | {
225
+ let limit = if outbound_message_size_limit >= 0.0 && outbound_message_size_limit <= f64 :: from( u32 :: MAX ) {
226
+ #[ expect( clippy:: cast_possible_truncation, clippy:: cast_sign_loss) ]
227
+ { outbound_message_size_limit as u32 }
228
+ } else {
229
+ warn!( outbound_message_size_limit, "Invalid outbound message size limit; fallback to unlimited" ) ;
230
+ 0 // Fallback to no limit for invalid values.
231
+ } ;
232
+ self . 0 . borrow_mut( ) . outbound_message_size_limit = if limit > 0 { Some ( limit) } else { None } ;
233
+ } ;
222
234
}
223
235
224
236
self . clone ( )
@@ -242,6 +254,7 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder {
242
254
remote_clipboard_changed_callback,
243
255
remote_received_format_list_callback,
244
256
force_clipboard_update_callback,
257
+ outbound_message_size_limit,
245
258
) ;
246
259
247
260
{
@@ -271,6 +284,7 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder {
271
284
remote_clipboard_changed_callback = inner. remote_clipboard_changed_callback . clone ( ) ;
272
285
remote_received_format_list_callback = inner. remote_received_format_list_callback . clone ( ) ;
273
286
force_clipboard_update_callback = inner. force_clipboard_update_callback . clone ( ) ;
287
+ outbound_message_size_limit = inner. outbound_message_size_limit ;
274
288
}
275
289
276
290
info ! ( "Connect to RDP host" ) ;
@@ -293,9 +307,9 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder {
293
307
)
294
308
} ) ;
295
309
296
- let ws = WebSocket :: open ( & proxy_address) . context ( "Couldn’ t open WebSocket" ) ?;
310
+ let ws = WebSocket :: open ( & proxy_address) . context ( "couldn' t open WebSocket" ) ?;
297
311
298
- // NOTE: ideally, when the WebSocket can’ t be opened, the above call should fail with details on why is that
312
+ // NOTE: ideally, when the WebSocket can' t be opened, the above call should fail with details on why is that
299
313
// (e.g., the proxy hostname could not be resolved, proxy service is not running), but errors are neved
300
314
// bubbled up in practice, so instead we poll the WebSocket state until we know its connected (i.e., the
301
315
// WebSocket handshake is a success and user data can be exchanged).
@@ -339,7 +353,7 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder {
339
353
340
354
let ( writer_tx, writer_rx) = mpsc:: unbounded ( ) ;
341
355
342
- spawn_local ( writer_task ( writer_rx, rdp_writer) ) ;
356
+ spawn_local ( writer_task ( writer_rx, rdp_writer, outbound_message_size_limit ) ) ;
343
357
344
358
Ok ( Session {
345
359
desktop_size : connection_result. desktop_size ,
@@ -885,22 +899,39 @@ fn build_config(
885
899
}
886
900
}
887
901
888
- async fn writer_task ( rx : mpsc:: UnboundedReceiver < Vec < u8 > > , rdp_writer : WriteHalf < WebSocket > ) {
902
+ async fn writer_task (
903
+ rx : mpsc:: UnboundedReceiver < Vec < u8 > > ,
904
+ rdp_writer : WriteHalf < WebSocket > ,
905
+ outbound_limit : Option < u32 > ,
906
+ ) {
889
907
debug ! ( "writer task started" ) ;
890
908
891
909
async fn inner (
892
910
mut rx : mpsc:: UnboundedReceiver < Vec < u8 > > ,
893
911
mut rdp_writer : WriteHalf < WebSocket > ,
912
+ outbound_limit : Option < u32 > ,
894
913
) -> anyhow:: Result < ( ) > {
895
914
while let Some ( frame) = rx. next ( ) . await {
896
- rdp_writer. write_all ( & frame) . await . context ( "Couldn’t write frame" ) ?;
897
- rdp_writer. flush ( ) . await . context ( "Couldn’t flush" ) ?;
915
+ match outbound_limit {
916
+ Some ( max_size) if frame. len ( ) > max_size as usize => {
917
+ // Send in chunks.
918
+ for chunk in frame. chunks ( max_size as usize ) {
919
+ rdp_writer. write_all ( chunk) . await . context ( "couldn't write chunk" ) ?;
920
+ rdp_writer. flush ( ) . await . context ( "couldn't flush chunk" ) ?;
921
+ }
922
+ }
923
+ _ => {
924
+ // Send complete frame (default case).
925
+ rdp_writer. write_all ( & frame) . await . context ( "couldn't write frame" ) ?;
926
+ rdp_writer. flush ( ) . await . context ( "couldn't flush frame" ) ?;
927
+ }
928
+ }
898
929
}
899
930
900
931
Ok ( ( ) )
901
932
}
902
933
903
- match inner ( rx, rdp_writer) . await {
934
+ match inner ( rx, rdp_writer, outbound_limit ) . await {
904
935
Ok ( ( ) ) => debug ! ( "writer task ended gracefully" ) ,
905
936
Err ( e) => error ! ( "writer task ended unexpectedly: {e:#}" ) ,
906
937
}
@@ -960,7 +991,7 @@ async fn connect(
960
991
. ok ( )
961
992
. map ( |url| KerberosConfig {
962
993
kdc_proxy_url : Some ( url) ,
963
- // HACK: It’ s supposed to be the computer name of the client, but since it’ s not easy to retrieve this information in the browser,
994
+ // HACK: It' s supposed to be the computer name of the client, but since it' s not easy to retrieve this information in the browser,
964
995
// we set the destination hostname instead because it happens to work.
965
996
hostname : Some ( destination) ,
966
997
} ) ,
@@ -1030,7 +1061,7 @@ where
1030
1061
framed
1031
1062
. write_all ( & rdcleanpath_req)
1032
1063
. await
1033
- . context ( "couldn’ t write RDCleanPath request" ) ?;
1064
+ . context ( "couldn' t write RDCleanPath request" ) ?;
1034
1065
}
1035
1066
1036
1067
{
0 commit comments