@@ -2,16 +2,31 @@ use crate::rc::{Ref, RefCountable};
22use crate :: string:: { BnString , IntoCStr } ;
33use binaryninjacore_sys:: * ;
44use std:: ffi:: { c_char, c_void, CStr } ;
5+ use std:: marker:: PhantomData ;
6+ use std:: ops:: Deref ;
57use std:: ptr:: NonNull ;
68
79pub trait WebsocketClientCallback : Sync + Send {
8- fn connected ( & mut self ) -> bool ;
10+ /// Receive a notification that the websocket connection has been connected successfully.
11+ ///
12+ /// Return `false` if you would like to terminate the connection early.
13+ fn connected ( & self ) -> bool ;
914
10- fn disconnected ( & mut self ) ;
15+ /// Receive a notification that the websocket connection has been terminated.
16+ ///
17+ /// For implementations, you must call this at the end of the websocket connection lifecycle
18+ /// even if you notify the client of an error.
19+ fn disconnected ( & self ) ;
1120
12- fn error ( & mut self , msg : & str ) ;
21+ /// Receive an error from the websocket connection.
22+ ///
23+ /// For implementations, you typically write the data to an interior mutable buffer on the instance.
24+ fn error ( & self , msg : & str ) ;
1325
14- fn read ( & mut self , data : & [ u8 ] ) -> bool ;
26+ /// Receive data from the websocket connection.
27+ ///
28+ /// For implementations, you typically write the data to an interior mutable buffer on the instance.
29+ fn read ( & self , data : & [ u8 ] ) -> bool ;
1530}
1631
1732pub trait WebsocketClient : Sync + Send {
@@ -27,7 +42,32 @@ pub trait WebsocketClient: Sync + Send {
2742 fn disconnect ( & self ) -> bool ;
2843}
2944
45+ /// Represents a live websocket connection.
46+ ///
47+ /// This manages the lifetime of the callback, ensuring it outlives the connection.
48+ pub struct ActiveConnection < ' a , C : WebsocketClientCallback > {
49+ pub client : Ref < CoreWebsocketClient > ,
50+ _callback : PhantomData < & ' a mut C > ,
51+ }
52+
53+ impl < ' a , C : WebsocketClientCallback > Deref for ActiveConnection < ' a , C > {
54+ type Target = CoreWebsocketClient ;
55+
56+ fn deref ( & self ) -> & Self :: Target {
57+ & self . client
58+ }
59+ }
60+
61+ impl < ' a , C : WebsocketClientCallback > Drop for ActiveConnection < ' a , C > {
62+ fn drop ( & mut self ) {
63+ self . client . disconnect ( ) ;
64+ }
65+ }
66+
3067/// Implements a websocket client.
68+ ///
69+ /// To connect, use [`Ref<CoreWebsocketClient>::connect`] which will return an [`ActiveConnection`]
70+ /// which manages the lifecycle of the websocket connection.
3171#[ repr( transparent) ]
3272pub struct CoreWebsocketClient {
3373 pub ( crate ) handle : NonNull < BNWebsocketClient > ,
@@ -43,7 +83,50 @@ impl CoreWebsocketClient {
4383 & mut * self . handle . as_ptr ( )
4484 }
4585
46- /// Initializes the web socket connection.
86+ /// Call the connect callback function, forward the callback returned value
87+ pub fn notify_connected ( & self ) -> bool {
88+ unsafe { BNNotifyWebsocketClientConnect ( self . handle . as_ptr ( ) ) }
89+ }
90+
91+ /// Notify the callback function of a disconnect. This must be called at the end of an active
92+ /// websocket connection lifecycle, to free resources.
93+ ///
94+ /// NOTE: This does not actually disconnect, use the [Self::disconnect] function for that.
95+ pub fn notify_disconnected ( & self ) {
96+ unsafe { BNNotifyWebsocketClientDisconnect ( self . handle . as_ptr ( ) ) }
97+ }
98+
99+ /// Call the error callback function, this is not a terminating request you must use
100+ /// [`CoreWebsocketClient::notify_disconnected`] to terminate the connection.
101+ pub fn notify_error ( & self , msg : & str ) {
102+ let error = msg. to_cstr ( ) ;
103+ unsafe { BNNotifyWebsocketClientError ( self . handle . as_ptr ( ) , error. as_ptr ( ) ) }
104+ }
105+
106+ /// Call the read callback function, forward the callback returned value.
107+ pub fn notify_read ( & self , data : & [ u8 ] ) -> bool {
108+ unsafe {
109+ BNNotifyWebsocketClientReadData (
110+ self . handle . as_ptr ( ) ,
111+ data. as_ptr ( ) as * mut _ ,
112+ data. len ( ) . try_into ( ) . unwrap ( ) ,
113+ )
114+ }
115+ }
116+
117+ pub fn write ( & self , data : & [ u8 ] ) -> bool {
118+ let len = u64:: try_from ( data. len ( ) ) . unwrap ( ) ;
119+ unsafe { BNWriteWebsocketClientData ( self . as_raw ( ) , data. as_ptr ( ) , len) != 0 }
120+ }
121+
122+ pub fn disconnect ( & self ) -> bool {
123+ unsafe { BNDisconnectWebsocketClient ( self . as_raw ( ) ) }
124+ }
125+ }
126+
127+ impl Ref < CoreWebsocketClient > {
128+ /// Initializes the web socket connection, returning the [`ActiveConnection`], once dropped the
129+ /// connection will be disconnected.
47130 ///
48131 /// Connect to a given url, asynchronously. The connection will be run in a
49132 /// separate thread managed by the websocket provider.
@@ -54,20 +137,22 @@ impl CoreWebsocketClient {
54137 /// If the connection succeeds, [WebsocketClientCallback::connected] will be called. On normal
55138 /// termination, [WebsocketClientCallback::disconnected] will be called.
56139 ///
57- /// If the connection succeeds, but later fails, [WebsocketClientCallback::disconnected ] will not
58- /// be called, and [ WebsocketClientCallback::error ] will be called instead .
140+ /// If the connection succeeds but later fails, [` WebsocketClientCallback::error` ] will be called
141+ /// and shortly thereafter [` WebsocketClientCallback::disconnected` ] will be called.
59142 ///
60- /// If the connection fails, neither [WebsocketClientCallback::connected] nor
61- /// [WebsocketClientCallback::disconnected] will be called, and [WebsocketClientCallback::error]
62- /// will be called instead.
63- ///
64- /// If [WebsocketClientCallback::connected] or [WebsocketClientCallback::read] return false, the
143+ /// If [`WebsocketClientCallback::connected`] or [`WebsocketClientCallback::read`] return false, the
65144 /// connection will be aborted.
66145 ///
67146 /// * `host` - Full url with scheme, domain, optionally port, and path
68147 /// * `headers` - HTTP header keys and values
69148 /// * `callback` - Callbacks for various websocket events
70- pub fn initialize_connection < I , C > ( & self , host : & str , headers : I , callbacks : & mut C ) -> bool
149+ #[ must_use]
150+ pub fn connect < ' a , I , C > (
151+ self ,
152+ host : & str ,
153+ headers : I ,
154+ callbacks : & ' a C ,
155+ ) -> Option < ActiveConnection < ' a , C > >
71156 where
72157 I : IntoIterator < Item = ( String , String ) > ,
73158 C : WebsocketClientCallback ,
@@ -79,16 +164,16 @@ impl CoreWebsocketClient {
79164 . unzip ( ) ;
80165 let header_keys: Vec < * const c_char > = header_keys. iter ( ) . map ( |k| k. as_ptr ( ) ) . collect ( ) ;
81166 let header_values: Vec < * const c_char > = header_values. iter ( ) . map ( |v| v. as_ptr ( ) ) . collect ( ) ;
82- // SAFETY: This context will only be live for the duration of BNConnectWebsocketClient
167+ // SAFETY: This context will live for as long as the `ActiveConnection` is alive.
83168 // SAFETY: Any subsequent call to BNConnectWebsocketClient will write over the context.
84169 let mut output_callbacks = BNWebsocketClientOutputCallbacks {
85- context : callbacks as * mut C as * mut c_void ,
170+ context : callbacks as * const C as * mut C as * mut c_void ,
86171 connectedCallback : Some ( cb_connected :: < C > ) ,
87172 disconnectedCallback : Some ( cb_disconnected :: < C > ) ,
88173 errorCallback : Some ( cb_error :: < C > ) ,
89174 readCallback : Some ( cb_read :: < C > ) ,
90175 } ;
91- unsafe {
176+ let success = unsafe {
92177 BNConnectWebsocketClient (
93178 self . handle . as_ptr ( ) ,
94179 url. as_ptr ( ) ,
@@ -97,46 +182,17 @@ impl CoreWebsocketClient {
97182 header_values. as_ptr ( ) ,
98183 & mut output_callbacks,
99184 )
100- }
101- }
102-
103- /// Call the connect callback function, forward the callback returned value
104- pub fn notify_connected ( & self ) -> bool {
105- unsafe { BNNotifyWebsocketClientConnect ( self . handle . as_ptr ( ) ) }
106- }
107-
108- /// Notify the callback function of a disconnect,
109- ///
110- /// NOTE: This does not actually disconnect, use the [Self::disconnect] function for that.
111- pub fn notify_disconnected ( & self ) {
112- unsafe { BNNotifyWebsocketClientDisconnect ( self . handle . as_ptr ( ) ) }
113- }
114-
115- /// Call the error callback function
116- pub fn notify_error ( & self , msg : & str ) {
117- let error = msg. to_cstr ( ) ;
118- unsafe { BNNotifyWebsocketClientError ( self . handle . as_ptr ( ) , error. as_ptr ( ) ) }
119- }
185+ } ;
120186
121- /// Call the read callback function, forward the callback returned value
122- pub fn notify_read ( & self , data : & [ u8 ] ) -> bool {
123- unsafe {
124- BNNotifyWebsocketClientReadData (
125- self . handle . as_ptr ( ) ,
126- data. as_ptr ( ) as * mut _ ,
127- data. len ( ) . try_into ( ) . unwrap ( ) ,
128- )
187+ if success {
188+ Some ( ActiveConnection {
189+ client : self ,
190+ _callback : PhantomData ,
191+ } )
192+ } else {
193+ None
129194 }
130195 }
131-
132- pub fn write ( & self , data : & [ u8 ] ) -> bool {
133- let len = u64:: try_from ( data. len ( ) ) . unwrap ( ) ;
134- unsafe { BNWriteWebsocketClientData ( self . as_raw ( ) , data. as_ptr ( ) , len) != 0 }
135- }
136-
137- pub fn disconnect ( & self ) -> bool {
138- unsafe { BNDisconnectWebsocketClient ( self . as_raw ( ) ) }
139- }
140196}
141197
142198unsafe impl Sync for CoreWebsocketClient { }
0 commit comments