1717// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
1919const std = @import ("std" );
20+ const posix = std .posix ;
2021
2122const jsruntime = @import ("jsruntime" );
2223
@@ -31,6 +32,102 @@ pub const UserContext = apiweb.UserContext;
3132
3233const socket_path = "/tmp/browsercore-server.sock" ;
3334
35+ // Inspired by std.net.StreamServer in Zig < 0.12
36+ pub const StreamServer = struct {
37+ /// Copied from `Options` on `init`.
38+ kernel_backlog : u31 ,
39+ reuse_address : bool ,
40+ reuse_port : bool ,
41+ nonblocking : bool ,
42+
43+ /// `undefined` until `listen` returns successfully.
44+ listen_address : std.net.Address ,
45+
46+ sockfd : ? posix.socket_t ,
47+
48+ pub const Options = struct {
49+ /// How many connections the kernel will accept on the application's behalf.
50+ /// If more than this many connections pool in the kernel, clients will start
51+ /// seeing "Connection refused".
52+ kernel_backlog : u31 = 128 ,
53+
54+ /// Enable SO.REUSEADDR on the socket.
55+ reuse_address : bool = false ,
56+
57+ /// Enable SO.REUSEPORT on the socket.
58+ reuse_port : bool = false ,
59+
60+ /// Non-blocking mode.
61+ nonblocking : bool = false ,
62+ };
63+
64+ /// After this call succeeds, resources have been acquired and must
65+ /// be released with `deinit`.
66+ pub fn init (options : Options ) StreamServer {
67+ return StreamServer {
68+ .sockfd = null ,
69+ .kernel_backlog = options .kernel_backlog ,
70+ .reuse_address = options .reuse_address ,
71+ .reuse_port = options .reuse_port ,
72+ .nonblocking = options .nonblocking ,
73+ .listen_address = undefined ,
74+ };
75+ }
76+
77+ /// Release all resources. The `StreamServer` memory becomes `undefined`.
78+ pub fn deinit (self : * StreamServer ) void {
79+ self .close ();
80+ self .* = undefined ;
81+ }
82+
83+ pub fn listen (self : * StreamServer , address : std.net.Address ) ! void {
84+ const sock_flags = posix .SOCK .STREAM | posix .SOCK .CLOEXEC ;
85+ var use_sock_flags : u32 = sock_flags ;
86+ if (self .nonblocking ) use_sock_flags |= posix .SOCK .NONBLOCK ;
87+ const proto = if (address .any .family == posix .AF .UNIX ) @as (u32 , 0 ) else posix .IPPROTO .TCP ;
88+
89+ const sockfd = try posix .socket (address .any .family , use_sock_flags , proto );
90+ self .sockfd = sockfd ;
91+ errdefer {
92+ posix .close (sockfd );
93+ self .sockfd = null ;
94+ }
95+
96+ if (self .reuse_address ) {
97+ try posix .setsockopt (
98+ sockfd ,
99+ posix .SOL .SOCKET ,
100+ posix .SO .REUSEADDR ,
101+ & std .mem .toBytes (@as (c_int , 1 )),
102+ );
103+ }
104+ if (@hasDecl (posix .SO , "REUSEPORT" ) and self .reuse_port ) {
105+ try posix .setsockopt (
106+ sockfd ,
107+ posix .SOL .SOCKET ,
108+ posix .SO .REUSEPORT ,
109+ & std .mem .toBytes (@as (c_int , 1 )),
110+ );
111+ }
112+
113+ var socklen = address .getOsSockLen ();
114+ try posix .bind (sockfd , & address .any , socklen );
115+ try posix .listen (sockfd , self .kernel_backlog );
116+ try posix .getsockname (sockfd , & self .listen_address .any , & socklen );
117+ }
118+
119+ /// Stop listening. It is still necessary to call `deinit` after stopping listening.
120+ /// Calling `deinit` will automatically call `close`. It is safe to call `close` when
121+ /// not listening.
122+ pub fn close (self : * StreamServer ) void {
123+ if (self .sockfd ) | fd | {
124+ posix .close (fd );
125+ self .sockfd = null ;
126+ self .listen_address = undefined ;
127+ }
128+ }
129+ };
130+
34131pub fn main () ! void {
35132
36133 // create v8 vm
@@ -53,13 +150,15 @@ pub fn main() !void {
53150
54151 // server
55152 const addr = try std .net .Address .initUnix (socket_path );
56- var srv = std . net . StreamServer .init (.{
153+ var srv = StreamServer .init (.{
57154 .reuse_address = true ,
58155 .reuse_port = true ,
59- .force_nonblocking = true ,
156+ .nonblocking = true ,
60157 });
61158 defer srv .deinit ();
159+
62160 try srv .listen (addr );
161+ defer srv .close ();
63162 std .debug .print ("Listening on: {s}...\n " , .{socket_path });
64163
65164 var browser = try Browser .init (arena .allocator (), vm );
0 commit comments