13
13
14
14
use std:: collections:: HashMap ;
15
15
use std:: net:: { IpAddr , Ipv4Addr , SocketAddr , SocketAddrV4 } ;
16
+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
16
17
use std:: sync:: Arc ;
17
18
use std:: time:: Duration ;
18
19
20
+ use atomic_destructor:: { AtomicDestroyer , AtomicDestructor } ;
19
21
use bytes:: Bytes ;
20
22
use http_body_util:: combinators:: BoxBody ;
21
23
use http_body_util:: { BodyExt , Full } ;
@@ -165,11 +167,8 @@ pub struct BrowserSignerProxyOptions {
165
167
pub addr : SocketAddr ,
166
168
}
167
169
168
- /// Nostr Browser Signer Proxy
169
- ///
170
- /// Proxy to use Nostr Browser signer (NIP-07) in native applications.
171
170
#[ derive( Debug , Clone ) ]
172
- pub struct BrowserSignerProxy {
171
+ struct InnerBrowserSignerProxy {
173
172
/// Configuration options for the proxy
174
173
options : BrowserSignerProxyOptions ,
175
174
/// Internal state of the proxy including request queues
@@ -178,6 +177,38 @@ pub struct BrowserSignerProxy {
178
177
handle : OnceCell < Arc < JoinHandle < ( ) > > > ,
179
178
/// Notification trigger for graceful shutdown
180
179
shutdown : Arc < Notify > ,
180
+ /// Flag to indicate if the server is shutdown
181
+ is_shutdown : Arc < AtomicBool > ,
182
+ }
183
+
184
+ impl AtomicDestroyer for InnerBrowserSignerProxy {
185
+ fn on_destroy ( & self ) {
186
+ self . shutdown ( ) ;
187
+ }
188
+ }
189
+
190
+ impl InnerBrowserSignerProxy {
191
+ #[ inline]
192
+ fn is_shutdown ( & self ) -> bool {
193
+ self . is_shutdown . load ( Ordering :: SeqCst )
194
+ }
195
+
196
+ fn shutdown ( & self ) {
197
+ // Mark the server as shutdown
198
+ self . is_shutdown . store ( true , Ordering :: SeqCst ) ;
199
+
200
+ // Notify all waiters that the proxy is shutting down
201
+ self . shutdown . notify_one ( ) ;
202
+ self . shutdown . notify_waiters ( ) ;
203
+ }
204
+ }
205
+
206
+ /// Nostr Browser Signer Proxy
207
+ ///
208
+ /// Proxy to use Nostr Browser signer (NIP-07) in native applications.
209
+ #[ derive( Debug , Clone ) ]
210
+ pub struct BrowserSignerProxy {
211
+ inner : AtomicDestructor < InnerBrowserSignerProxy > ,
181
212
}
182
213
183
214
impl Default for BrowserSignerProxyOptions {
@@ -210,8 +241,6 @@ impl BrowserSignerProxyOptions {
210
241
}
211
242
}
212
243
213
- // TODO: use atomic-destructor to automatically shutdown this when all instances are dropped
214
-
215
244
impl BrowserSignerProxy {
216
245
/// Construct a new browser signer proxy
217
246
pub fn new ( options : BrowserSignerProxyOptions ) -> Self {
@@ -221,32 +250,36 @@ impl BrowserSignerProxy {
221
250
} ;
222
251
223
252
Self {
224
- options,
225
- state : Arc :: new ( state) ,
226
- handle : OnceCell :: new ( ) ,
227
- shutdown : Arc :: new ( Notify :: new ( ) ) ,
253
+ inner : AtomicDestructor :: new ( InnerBrowserSignerProxy {
254
+ options,
255
+ state : Arc :: new ( state) ,
256
+ handle : OnceCell :: new ( ) ,
257
+ shutdown : Arc :: new ( Notify :: new ( ) ) ,
258
+ is_shutdown : Arc :: new ( AtomicBool :: new ( false ) ) ,
259
+ } ) ,
228
260
}
229
261
}
230
262
231
263
/// Get the signer proxy webpage URL
232
264
#[ inline]
233
265
pub fn url ( & self ) -> String {
234
- format ! ( "http://{}" , self . options. addr)
266
+ format ! ( "http://{}" , self . inner . options. addr)
235
267
}
236
268
237
269
/// Start the proxy
238
270
///
239
271
/// If this is not called, will be automatically started on the first interaction with the signer.
240
272
pub async fn start ( & self ) -> Result < ( ) , Error > {
241
273
let _handle: & Arc < JoinHandle < ( ) > > = self
274
+ . inner
242
275
. handle
243
276
. get_or_try_init ( || async {
244
- let listener = TcpListener :: bind ( self . options . addr ) . await ?;
277
+ let listener = TcpListener :: bind ( self . inner . options . addr ) . await ?;
245
278
246
- tracing:: info!( "Starting proxy server on {}" , self . options. addr) ;
279
+ tracing:: info!( "Starting proxy server on {}" , self . inner . options. addr) ;
247
280
248
- let state = self . state . clone ( ) ;
249
- let shutdown = self . shutdown . clone ( ) ;
281
+ let state = self . inner . state . clone ( ) ;
282
+ let shutdown = self . inner . shutdown . clone ( ) ;
250
283
251
284
let handle: JoinHandle < ( ) > = tokio:: spawn ( async move {
252
285
loop {
@@ -293,20 +326,26 @@ impl BrowserSignerProxy {
293
326
294
327
#[ inline]
295
328
async fn store_pending_response ( & self , id : Uuid , tx : Sender < Result < Value , String > > ) {
296
- let mut pending_responses = self . state . pending_responses . lock ( ) . await ;
329
+ let mut pending_responses = self . inner . state . pending_responses . lock ( ) . await ;
297
330
pending_responses. insert ( id, tx) ;
298
331
}
299
332
300
333
#[ inline]
301
334
async fn store_outgoing_request ( & self , request : RequestData ) {
302
- let mut outgoing_requests = self . state . outgoing_requests . lock ( ) . await ;
335
+ let mut outgoing_requests = self . inner . state . outgoing_requests . lock ( ) . await ;
303
336
outgoing_requests. push ( request) ;
304
337
}
305
338
306
339
async fn request < T > ( & self , method : RequestMethod , params : Value ) -> Result < T , Error >
307
340
where
308
341
T : DeserializeOwned ,
309
342
{
343
+ // Ensure is not shutdown
344
+ if self . inner . is_shutdown ( ) {
345
+ return Err ( Error :: Shutdown ) ;
346
+ }
347
+
348
+ // Start the proxy if not already started
310
349
self . start ( ) . await ?;
311
350
312
351
// Construct the request
@@ -322,7 +361,7 @@ impl BrowserSignerProxy {
322
361
self . store_outgoing_request ( request) . await ;
323
362
324
363
// Wait for response
325
- match time:: timeout ( self . options . timeout , rx)
364
+ match time:: timeout ( self . inner . options . timeout , rx)
326
365
. await
327
366
. map_err ( |_| Error :: Timeout ) ??
328
367
{
0 commit comments