@@ -5,6 +5,8 @@ module fasthttp
55
66import runtime
77import net
8+ import os.notify
9+ import time
810
911#include <fcntl.h>
1012#include <errno.h>
7072 handler fn (HttpRequest) ! []u8 @[required]
7173 user_data voidptr
7274}
75+
76+ struct Server {
77+ pub :
78+ port int = 3000
79+ max_request_buffer_size int = 8192
80+ user_data voidptr
81+ mut :
82+ listen_fds []int = []int {len: max_thread_pool_size, cap: max_thread_pool_size}
83+ threads []thread = []thread{len: max_thread_pool_size, cap: max_thread_pool_size}
84+ request_handler fn (HttpRequest) ! []u8 @[required]
85+ }
86+
87+ // new_server creates and initializes a new Server instance.
88+ pub fn new_server (config ServerConfig) ! & Server {
89+ if config.max_request_buffer_size < = 0 {
90+ return error ('max_request_buffer_size must be greater than 0' )
91+ }
92+ mut server := & Server{
93+ port: config.port
94+ max_request_buffer_size: config.max_request_buffer_size
95+ user_data: config.user_data
96+ request_handler: config.handler
97+ }
98+ unsafe {
99+ server.listen_fds.flags.set (.noslices | .noshrink | .nogrow)
100+ server.threads.flags.set (.noslices | .noshrink | .nogrow)
101+ }
102+ return server
103+ }
104+
105+ fn set_blocking (fd int , blocking bool ) {
106+ flags := C.fcntl (fd, C.F_GETFL, 0 )
107+ if flags == - 1 {
108+ // TODO: better error handling
109+ eprintln (@LOCATION)
110+ return
111+ }
112+ if blocking {
113+ // This removes the O_NONBLOCK flag from flags and set it.
114+ C.fcntl (fd, C.F_SETFL, flags & ~ C.O_NONBLOCK)
115+ } else {
116+ // This adds the O_NONBLOCK flag from flags and set it.
117+ C.fcntl (fd, C.F_SETFL, flags | C.O_NONBLOCK)
118+ }
119+ }
120+
121+ fn close_socket (fd int ) bool {
122+ ret := C.close (fd)
123+ if ret == - 1 {
124+ if C.errno == C.EINTR {
125+ // Interrupted by signal, retry is safe
126+ return close_socket (fd)
127+ }
128+ eprintln ('ERROR: close(fd=${fd }) failed with errno=${C .errno }' )
129+ return false
130+ }
131+ return true
132+ }
133+
134+ fn create_server_socket (port int ) int {
135+ // Create a socket with non-blocking mode
136+ server_fd := C.socket (net.AddrFamily.ip, net.SocketType.tcp, 0 )
137+ if server_fd < 0 {
138+ eprintln (@LOCATION)
139+ C.perror (c 'Socket creation failed' )
140+ return - 1
141+ }
142+
143+ set_blocking (server_fd, false )
144+
145+ // Enable SO_REUSEADDR and SO_REUSEPORT
146+ opt := 1
147+ if C.setsockopt (server_fd, C.SOL_SOCKET, C.SO_REUSEADDR, & opt, sizeof (opt)) < 0 {
148+ eprintln (@LOCATION)
149+ C.perror (c 'setsockopt SO_REUSEADDR failed' )
150+ close_socket (server_fd)
151+ return - 1
152+ }
153+ if C.setsockopt (server_fd, C.SOL_SOCKET, C.SO_REUSEPORT, & opt, sizeof (opt)) < 0 {
154+ eprintln (@LOCATION)
155+ C.perror (c 'setsockopt SO_REUSEPORT failed' )
156+ close_socket (server_fd)
157+ return - 1
158+ }
159+
160+ addr := net.new_ip (u16 (port), [u8 (0 ), 0 , 0 , 0 ]! )
161+ alen := addr.len ()
162+ if C.bind (server_fd, voidptr (& addr), alen) < 0 {
163+ eprintln (@LOCATION)
164+ C.perror (c 'Bind failed' )
165+ close_socket (server_fd)
166+ return - 1
167+ }
168+ if C.listen (server_fd, max_connection_size) < 0 {
169+ eprintln (@LOCATION)
170+ C.perror (c 'Listen failed' )
171+ close_socket (server_fd)
172+ return - 1
173+ }
174+ return server_fd
175+ }
176+
177+ fn handle_accept_loop (mut notifier notify.FdNotifier, listen_fd int ) {
178+ for {
179+ client_fd := C.accept4 (listen_fd, C.NULL, C.NULL, C.SOCK_NONBLOCK)
180+ if client_fd < 0 {
181+ if C.errno == C.EAGAIN || C.errno == C.EWOULDBLOCK {
182+ break // No more incoming connections; exit loop.
183+ }
184+ eprintln (@LOCATION)
185+ C.perror (c 'Accept failed' )
186+ break
187+ }
188+ // Enable TCP_NODELAY for lower latency
189+ opt := 1
190+ C.setsockopt (client_fd, C.IPPROTO_TCP, C.TCP_NODELAY, & opt, sizeof (opt))
191+ // Register client socket with notifier
192+ notifier.add (client_fd, .read, .edge_trigger) or {
193+ eprintln ('notifier.add failed: ${err }' )
194+ close_socket (client_fd)
195+ }
196+ }
197+ }
198+
199+ fn handle_client_closure (mut notifier notify.FdNotifier, client_fd int ) {
200+ // Never close the listening socket here
201+ if client_fd == 0 {
202+ return
203+ }
204+ if client_fd < = 0 {
205+ eprintln ('ERROR: Invalid FD=${client_fd } for closure' )
206+ return
207+ }
208+ notifier.remove (client_fd) or {}
209+ close_socket (client_fd)
210+ }
211+
212+ fn process_events (mut server Server, listen_fd int ) {
213+ mut notifier := notify.new () or {
214+ eprintln ('Failed to create notifier: ${err }' )
215+ return
216+ }
217+ defer {
218+ notifier.close () or {}
219+ }
220+ notifier.add (listen_fd, .read, .edge_trigger) or {
221+ eprintln ('Failed to add listen fd to notifier: ${err }' )
222+ return
223+ }
224+ mut request_buffer := []u8 {len: server.max_request_buffer_size, cap: server.max_request_buffer_size}
225+ unsafe {
226+ request_buffer.flags.set (.noslices | .nogrow | .noshrink)
227+ }
228+ for {
229+ for event in notifier.wait (time.infinite) {
230+ if event.fd == listen_fd {
231+ handle_accept_loop (mut notifier, listen_fd)
232+ continue
233+ }
234+ if event.kind.has (.error) || event.kind.has (.hangup) {
235+ client_fd := event.fd
236+ if client_fd == listen_fd {
237+ eprintln ('ERROR: listen fd had HUP/ERR' )
238+ continue
239+ }
240+ if client_fd > 0 {
241+ C.send (client_fd, status_444_ response.data, status_444_ response.len,
242+ C.MSG_NOSIGNAL)
243+ handle_client_closure (mut notifier, client_fd)
244+ } else {
245+ eprintln ('ERROR: Invalid FD from notifier: ${client_fd }' )
246+ }
247+ continue
248+ }
249+ if event.kind.has (.read) {
250+ client_fd := event.fd
251+ bytes_read := C.recv (client_fd, unsafe { & request_buffer[0 ] }, server.max_request_buffer_size - 1 ,
252+ 0 )
253+ if bytes_read > 0 {
254+ // Check if request exceeds buffer size
255+ if bytes_read > = server.max_request_buffer_size - 1 {
256+ C.send (client_fd, status_413_ response.data, status_413_ response.len,
257+ C.MSG_NOSIGNAL)
258+ handle_client_closure (mut notifier, client_fd)
259+ continue
260+ }
261+ mut readed_request_buffer := []u8 {cap: bytes_read}
262+ unsafe {
263+ readed_request_buffer.push_many (& request_buffer[0 ], bytes_read)
264+ }
265+ mut decoded_http_request := decode_http_request (readed_request_buffer) or {
266+ eprintln ('Error decoding request ${err }' )
267+ C.send (client_fd, tiny_bad_request_response.data, tiny_bad_request_response.len,
268+ C.MSG_NOSIGNAL)
269+ handle_client_closure (mut notifier, client_fd)
270+ continue
271+ }
272+ decoded_http_request.client_conn_fd = client_fd
273+ decoded_http_request.user_data = server.user_data
274+ response_buffer := server.request_handler (decoded_http_request) or {
275+ eprintln ('Error handling request ${err }' )
276+ C.send (client_fd, tiny_bad_request_response.data, tiny_bad_request_response.len,
277+ C.MSG_NOSIGNAL)
278+ handle_client_closure (mut notifier, client_fd)
279+ continue
280+ }
281+ // Send response
282+ sent := C.send (client_fd, response_buffer.data, response_buffer.len,
283+ C.MSG_NOSIGNAL | C.MSG_DONTWAIT)
284+ if sent < 0 && C.errno != C.EAGAIN && C.errno != C.EWOULDBLOCK {
285+ eprintln ('ERROR: send() failed with errno=${C .errno }' )
286+ handle_client_closure (mut notifier, client_fd)
287+ continue
288+ }
289+ // Leave the connection open; closure is driven by client FIN or errors
290+ } else if bytes_read == 0 {
291+ // Normal client closure (FIN received)
292+ handle_client_closure (mut notifier, client_fd)
293+ } else if bytes_read < 0 && C.errno != C.EAGAIN && C.errno != C.EWOULDBLOCK {
294+ // Unexpected recv error - send 444 No Response
295+ C.send (client_fd, status_444_ response.data, status_444_ response.len,
296+ C.MSG_NOSIGNAL)
297+ handle_client_closure (mut notifier, client_fd)
298+ }
299+ }
300+ }
301+ }
302+ }
303+
304+ // run starts the server and begins listening for incoming connections.
305+ pub fn (mut server Server) run () ! {
306+ $if windows {
307+ eprintln ('Windows is not supported yet' )
308+ return
309+ }
310+ for i := 0 ; i < max_thread_pool_size; i++ {
311+ server.listen_fds[i] = create_server_socket (server.port)
312+ if server.listen_fds[i] < 0 {
313+ return
314+ }
315+ server.threads[i] = spawn process_events (mut server, server.listen_fds[i])
316+ }
317+
318+ println ('listening on http://localhost:${server .port }/' )
319+ // Main thread waits for workers; accepts are handled in worker epoll loops
320+ for i in 0 .. max_thread_pool_size {
321+ server.threads[i].wait ()
322+ }
323+ }
0 commit comments