1717// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
1919const std = @import ("std" );
20+ const builtin = @import ("builtin" );
2021
2122const jsruntime = @import ("jsruntime" );
2223const Completion = jsruntime .IO .Completion ;
@@ -37,6 +38,7 @@ const Error = IOError || std.fmt.ParseIntError || cdp.Error || NoError;
3738const TimeoutCheck = std .time .ns_per_ms * 100 ;
3839
3940const log = std .log .scoped (.server );
41+ const IsLinux = builtin .target .os .tag == .linux ;
4042
4143// I/O Main
4244// --------
@@ -55,6 +57,7 @@ pub const Ctx = struct {
5557 err : ? Error = null ,
5658
5759 // I/O fields
60+ accept_completion : * Completion ,
5861 conn_completion : * Completion ,
5962 timeout_completion : * Completion ,
6063 timeout : u64 ,
@@ -76,13 +79,14 @@ pub const Ctx = struct {
7679 completion : * Completion ,
7780 result : AcceptError ! std.posix.socket_t ,
7881 ) void {
79- std .debug .assert (completion == self .conn_completion );
82+ std .debug .assert (completion == self .acceptCompletion () );
8083
8184 self .conn_socket = result catch | err | {
8285 log .err ("accept error: {any}" , .{err });
8386 self .err = err ;
8487 return ;
8588 };
89+ log .info ("client connected" , .{});
8690
8791 // set connection timestamp and timeout
8892 self .last_active = std .time .Instant .now () catch | err | {
@@ -112,6 +116,11 @@ pub const Ctx = struct {
112116 std .debug .assert (completion == self .conn_completion );
113117
114118 const size = result catch | err | {
119+ if (err == error .FileDescriptorInvalid and self .isClosed ()) {
120+ // connection has been closed, do nothing
121+ log .debug ("recv on closed conn" , .{});
122+ return ;
123+ }
115124 log .err ("read error: {any}" , .{err });
116125 self .err = err ;
117126 return ;
@@ -188,21 +197,9 @@ pub const Ctx = struct {
188197 };
189198
190199 if (now .since (self .last_active .? ) > self .timeout ) {
191- // closing
192- log .debug ("conn timeout, closing..." , .{});
193-
194- // NOTE: we should cancel the current read
195- // but it seems that's just closing the connection is enough
196- // (and cancel does not work on MacOS)
197-
198200 // close current connection
199- self .loop .io .close (
200- * Ctx ,
201- self ,
202- Ctx .closeCbk ,
203- self .timeout_completion ,
204- self .conn_socket ,
205- );
201+ log .debug ("conn timeout, closing..." , .{});
202+ self .close ();
206203 return ;
207204 }
208205
@@ -216,39 +213,6 @@ pub const Ctx = struct {
216213 );
217214 }
218215
219- fn closeCbk (self : * Ctx , completion : * Completion , result : CloseError ! void ) void {
220- _ = completion ;
221- // NOTE: completion can be either self.conn_completion or self.timeout_completion
222-
223- _ = result catch | err | {
224- log .err ("close error: {any}" , .{err });
225- self .err = err ;
226- return ;
227- };
228-
229- // conn is closed
230- self .last_active = null ;
231-
232- // restart a new browser session in case of re-connect
233- if (! self .sessionNew ) {
234- self .newSession () catch | err | {
235- log .err ("new session error: {any}" , .{err });
236- return ;
237- };
238- }
239-
240- log .info ("accepting new conn..." , .{});
241-
242- // continue accepting incoming requests
243- self .loop .io .accept (
244- * Ctx ,
245- self ,
246- Ctx .acceptCbk ,
247- self .conn_completion ,
248- self .accept_socket ,
249- );
250- }
251-
252216 // shortcuts
253217 // ---------
254218
@@ -267,6 +231,15 @@ pub const Ctx = struct {
267231 return self .browser .session .env ;
268232 }
269233
234+ inline fn acceptCompletion (self : * Ctx ) * Completion {
235+ // NOTE: the logical completion to use here is the accept_completion
236+ // as the pipe_connection can be used simulteanously by a recv I/O operation.
237+ // But on MacOS (kqueue) the recv I/O operation on a closed socket leads to a panic
238+ // so we use the pipe_connection to avoid this problem
239+ if (IsLinux ) return self .accept_completion ;
240+ return self .conn_completion ;
241+ }
242+
270243 // actions
271244 // -------
272245
@@ -276,13 +249,7 @@ pub const Ctx = struct {
276249 if (std .mem .eql (u8 , cmd , "close" )) {
277250 // close connection
278251 log .info ("close cmd, closing conn..." , .{});
279- self .loop .io .close (
280- * Ctx ,
281- self ,
282- Ctx .closeCbk ,
283- self .conn_completion ,
284- self .conn_socket ,
285- );
252+ self .close ();
286253 return error .Closed ;
287254 }
288255
@@ -307,6 +274,33 @@ pub const Ctx = struct {
307274 }
308275 }
309276
277+ fn close (self : * Ctx ) void {
278+ std .posix .close (self .conn_socket );
279+
280+ // conn is closed
281+ log .info ("connection closed" , .{});
282+ self .last_active = null ;
283+
284+ // restart a new browser session in case of re-connect
285+ if (! self .sessionNew ) {
286+ self .newSession () catch | err | {
287+ log .err ("new session error: {any}" , .{err });
288+ return ;
289+ };
290+ }
291+
292+ log .info ("accepting new conn..." , .{});
293+
294+ // continue accepting incoming requests
295+ self .loop .io .accept (
296+ * Ctx ,
297+ self ,
298+ Ctx .acceptCbk ,
299+ self .acceptCompletion (),
300+ self .accept_socket ,
301+ );
302+ }
303+
310304 fn newSession (self : * Ctx ) ! void {
311305 try self .browser .newSession (self .alloc (), self .loop );
312306 try self .browser .session .initInspector (
@@ -430,6 +424,7 @@ pub fn listen(
430424 defer msg_buf .deinit (loop .alloc );
431425
432426 // create I/O completions
427+ var accept_completion : Completion = undefined ;
433428 var conn_completion : Completion = undefined ;
434429 var timeout_completion : Completion = undefined ;
435430
@@ -443,6 +438,7 @@ pub fn listen(
443438 .msg_buf = & msg_buf ,
444439 .accept_socket = server_socket ,
445440 .timeout = timeout ,
441+ .accept_completion = & accept_completion ,
446442 .conn_completion = & conn_completion ,
447443 .timeout_completion = & timeout_completion ,
448444 };
@@ -454,7 +450,7 @@ pub fn listen(
454450
455451 // accepting connection asynchronously on internal server
456452 log .info ("accepting new conn..." , .{});
457- loop .io .accept (* Ctx , & ctx , Ctx .acceptCbk , ctx .conn_completion , ctx .accept_socket );
453+ loop .io .accept (* Ctx , & ctx , Ctx .acceptCbk , ctx .acceptCompletion () , ctx .accept_socket );
458454
459455 // infinite loop on I/O events, either:
460456 // - cmd from incoming connection on server socket
0 commit comments