77 local_socket:: { traits, Listener , ListenerNonblockingMode , Name } ,
88 Sealed , TryClone ,
99 } ,
10- std:: io ,
10+ std:: { fmt :: Debug , io } ,
1111} ;
1212
1313/// A builder for [local socket listeners](traits::Listener), including [`Listener`].
14- #[ derive( Debug ) ]
1514pub struct ListenerOptions < ' n > {
1615 pub ( crate ) name : Name < ' n > ,
17- pub ( crate ) nonblocking : ListenerNonblockingMode ,
18- pub ( crate ) reclaim_name : bool ,
16+ flags : u8 ,
1917 #[ cfg( unix) ]
20- pub ( crate ) mode : Option < libc:: mode_t > ,
18+ mode : libc:: mode_t ,
2119 #[ cfg( windows) ]
2220 pub ( crate ) security_descriptor : Option < SecurityDescriptor > ,
2321}
2422impl Sealed for ListenerOptions < ' _ > { }
2523
24+ const SHFT_NONBLOCKING_ACCEPT : u8 = 0 ;
25+ const SHFT_NONBLOCKING_STREAM : u8 = 1 ;
26+ const SHFT_RECLAIM_NAME : u8 = 2 ;
27+ const SHFT_TRY_OVERWRITE : u8 = 3 ; // TODO
28+ const SHFT_HAS_MODE : u8 = 4 ;
29+
30+ const ALL_BITS : u8 = ( 1 << 5 ) - 1 ;
31+ const NONBLOCKING_BITS : u8 = ( 1 << SHFT_NONBLOCKING_ACCEPT ) | ( 1 << SHFT_NONBLOCKING_STREAM ) ;
32+
2633impl TryClone for ListenerOptions < ' _ > {
2734 fn try_clone ( & self ) -> io:: Result < Self > {
2835 Ok ( Self {
2936 name : self . name . clone ( ) ,
30- nonblocking : self . nonblocking ,
31- reclaim_name : self . reclaim_name ,
37+ flags : self . flags ,
3238 #[ cfg( unix) ]
3339 mode : self . mode ,
3440 #[ cfg( windows) ]
@@ -48,10 +54,9 @@ impl ListenerOptions<'_> {
4854 pub fn new ( ) -> Self {
4955 Self {
5056 name : Name :: invalid ( ) ,
51- nonblocking : ListenerNonblockingMode :: Neither ,
52- reclaim_name : true ,
57+ flags : 1 << SHFT_RECLAIM_NAME ,
5358 #[ cfg( unix) ]
54- mode : None ,
59+ mode : 0 ,
5560 #[ cfg( windows) ]
5661 security_descriptor : None ,
5762 }
@@ -63,14 +68,82 @@ impl<'n> ListenerOptions<'n> {
6368 builder_setters ! {
6469 /// Sets the name the server will listen on.
6570 name: Name <' n>,
66- /// Selects the nonblocking mode to be used by the listener.
67- ///
68- /// The default value is `Neither`.
69- nonblocking: ListenerNonblockingMode ,
70- /// Sets whether [name reclamation](Listener#name-reclamation) is to happen or not.
71- ///
72- /// This is enabled by default.
73- reclaim_name: bool ,
71+ }
72+ /// Selects the nonblocking mode to be used by the listener.
73+ ///
74+ /// The default value is `Neither`.
75+ #[ must_use = builder_must_use ! ( ) ]
76+ #[ inline( always) ]
77+ #[ allow( clippy:: as_conversions) ]
78+ pub fn nonblocking ( mut self , nonblocking : ListenerNonblockingMode ) -> Self {
79+ self . flags = ( self . flags & ( ALL_BITS ^ NONBLOCKING_BITS ) ) | nonblocking as u8 ;
80+ self
81+ }
82+ /// Sets whether [name reclamation](Listener#name-reclamation) is to happen or not.
83+ ///
84+ /// This is enabled by default.
85+ #[ must_use = builder_must_use ! ( ) ]
86+ #[ inline( always) ]
87+ #[ allow( clippy:: as_conversions) ]
88+ pub fn reclaim_name ( mut self , reclaim_name : bool ) -> Self {
89+ self . flags = ( self . flags & ( ALL_BITS ^ ( 1 << SHFT_RECLAIM_NAME ) ) )
90+ | ( ( reclaim_name as u8 ) << SHFT_RECLAIM_NAME ) ;
91+ self
92+ }
93+ /// Sets whether an attempt to handle [`AddrInUse`](std::io::ErrorKind::AddrInUse) errors by
94+ /// overwriting an existing listener (in the same manner as in
95+ /// [name reclamation](Listener#name-reclamation)) is to be made or not.
96+ ///
97+ /// If this is enabled, name reclamation will be performed on behalf of a previous listener,
98+ /// even if it is still running and accepting connections, thereby displacing it from the
99+ /// socket name so that the newly created listener could take its place.
100+ ///
101+ /// This is disabled by default.
102+ ///
103+ /// ## Platform-specific behavior
104+ /// ### Unix
105+ /// On Unix, this deletes the socket file if an `AddrInUse` error is encountered. The previous
106+ /// listener, if it is still listening on its socket, is not (and in fact cannot be) notified
107+ /// of this in any way.
108+ ///
109+ /// The deletion suffers from an unavoidable TOCTOU race between the `AddrInUse` error being
110+ /// observed and the socket file being deleted, since another process may replace the socket
111+ /// file with a different file, causing Interprocess to delete that file instead. Note that
112+ /// this generally has no inadvertent privilege escalation implications, as the privileges
113+ /// required for renaming a file are the same as the ones required for deleting it, but the
114+ /// behavior may still be surprising in this (admittedly rather artificial) edge case.
115+ ///
116+ /// ### Windows
117+ /// Does nothing (meaning the error goes unhandled), as named pipes cannot be overwritten.
118+ #[ must_use = builder_must_use ! ( ) ]
119+ #[ inline( always) ]
120+ #[ allow( clippy:: as_conversions) ]
121+ pub fn try_overwrite ( mut self , try_overwrite : bool ) -> Self {
122+ self . flags = ( self . flags & ( ALL_BITS ^ ( 1 << SHFT_TRY_OVERWRITE ) ) )
123+ | ( ( try_overwrite as u8 ) << SHFT_TRY_OVERWRITE ) ;
124+ self
125+ }
126+ #[ cfg( unix) ]
127+ #[ inline( always) ]
128+ pub ( crate ) fn set_mode ( & mut self , mode : libc:: mode_t ) {
129+ self . flags |= 1 << SHFT_HAS_MODE ;
130+ self . mode = mode;
131+ }
132+ }
133+
134+ /// Option getters.
135+ impl ListenerOptions < ' _ > {
136+ pub ( crate ) fn get_nonblocking_accept ( & self ) -> bool {
137+ self . flags & ( 1 << SHFT_NONBLOCKING_ACCEPT ) != 0
138+ }
139+ pub ( crate ) fn get_nonblocking_stream ( & self ) -> bool {
140+ self . flags & ( 1 << SHFT_NONBLOCKING_STREAM ) != 0
141+ }
142+ pub ( crate ) fn get_reclaim_name ( & self ) -> bool { self . flags & ( 1 << SHFT_RECLAIM_NAME ) != 0 }
143+ pub ( crate ) fn get_try_overwrite ( & self ) -> bool { self . flags & ( 1 << SHFT_TRY_OVERWRITE ) != 0 }
144+ #[ cfg( unix) ]
145+ pub ( crate ) fn get_mode ( & self ) -> Option < libc:: mode_t > {
146+ ( self . flags & ( 1 << SHFT_HAS_MODE ) != 0 ) . then_some ( self . mode )
74147 }
75148}
76149
@@ -108,3 +181,27 @@ impl Default for ListenerOptions<'_> {
108181 #[ inline]
109182 fn default ( ) -> Self { Self :: new ( ) }
110183}
184+
185+ impl Debug for ListenerOptions < ' _ > {
186+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
187+ let mut dbs = f. debug_struct ( "ListenerOptions" ) ;
188+ let nonblocking = ListenerNonblockingMode :: from_bool (
189+ self . get_nonblocking_accept ( ) ,
190+ self . get_nonblocking_stream ( ) ,
191+ ) ;
192+ dbs. field ( "name" , & self . name )
193+ . field ( "nonblocking" , & nonblocking)
194+ . field ( "reclaim_name" , & self . get_reclaim_name ( ) )
195+ . field ( "try_overwrite" , & self . get_try_overwrite ( ) ) ;
196+ #[ cfg( unix) ]
197+ {
198+ // FIXME not octal
199+ dbs. field ( "mode" , & self . get_mode ( ) ) ;
200+ }
201+ #[ cfg( windows) ]
202+ {
203+ dbs. field ( "security_descriptor" , & self . security_descriptor ) ;
204+ }
205+ dbs. finish ( )
206+ }
207+ }
0 commit comments