Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit 5a70b41

Browse files
authored
Merge pull request #284 from Lightbug-HQ/feature/server-executor
Add SyncExecutor to server
2 parents 3aae65f + 97ed9da commit 5a70b41

2 files changed

Lines changed: 80 additions & 34 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ __pycache__
1919

2020
# misc
2121
.vscode
22+
.mcp.json
23+
.claude
24+
CLAUDE.md
25+
.kli/**

lightbug_http/server.mojo

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,60 @@ fn handle_connection[
472472
break
473473

474474

475+
struct SyncExecutor[T: HTTPService](Movable):
476+
"""Single-threaded executor: handles each connection to completion before accepting the next.
477+
478+
This is the default executor used by `Server.serve`. It processes connections
479+
sequentially — receive, parse, dispatch to handler, respond — then loops back
480+
to accept the next connection.
481+
482+
Parameters:
483+
T: The HTTP service type that handles incoming requests.
484+
"""
485+
486+
var provision_pool: ProvisionPool
487+
var config: ServerConfig
488+
var server_address: String
489+
var tcp_keep_alive: Bool
490+
491+
fn __init__(out self, config: ServerConfig, server_address: String, tcp_keep_alive: Bool):
492+
self.provision_pool = ProvisionPool(config.max_connections, config)
493+
self.config = config.copy()
494+
self.server_address = server_address
495+
self.tcp_keep_alive = tcp_keep_alive
496+
497+
fn execute(mut self, var conn: TCPConnection[NetworkType.tcp4], mut handler: Self.T):
498+
var index: Int
499+
try:
500+
index = self.provision_pool.borrow()
501+
except:
502+
try:
503+
conn^.teardown()
504+
except:
505+
pass
506+
return
507+
508+
try:
509+
handle_connection(
510+
conn,
511+
self.provision_pool.provisions[index],
512+
handler,
513+
self.config,
514+
self.server_address,
515+
self.tcp_keep_alive,
516+
)
517+
except:
518+
pass
519+
finally:
520+
try:
521+
conn^.teardown()
522+
except:
523+
pass
524+
self.provision_pool.provisions[index].prepare_for_new_request()
525+
self.provision_pool.provisions[index].keepalive_count = 0
526+
self.provision_pool.release(index)
527+
528+
475529
struct Server(Movable):
476530
"""HTTP/1.1 Server implementation."""
477531

@@ -543,7 +597,7 @@ struct Server(Movable):
543597
raise server_err^
544598

545599
fn serve[T: HTTPService](self, ln: NoTLSListener[NetworkType.tcp4], mut handler: T) raises ServerError:
546-
"""Serve HTTP requests from an existing listener.
600+
"""Serve HTTP requests from an existing listener using the default single-threaded executor.
547601
548602
Parameters:
549603
T: The type of HTTPService that handles incoming requests.
@@ -555,46 +609,34 @@ struct Server(Movable):
555609
Raises:
556610
ServerError: If accept fails or critical connection handling errors occur.
557611
"""
558-
var provision_pool = ProvisionPool(self.config.max_connections, self.config)
612+
var executor = SyncExecutor[T](self.config, self._address, self.tcp_keep_alive)
613+
self.serve_with_executor(ln, handler, executor)
614+
615+
fn serve_with_executor[
616+
T: HTTPService,
617+
](self, ln: NoTLSListener[NetworkType.tcp4], mut handler: T, mut executor: SyncExecutor[T]) raises ServerError:
618+
"""Serve HTTP requests using a custom executor for connection dispatch.
559619
620+
Use this method to provide a custom execution model such as a thread pool
621+
or async runtime for handling connections concurrently.
622+
623+
Parameters:
624+
T: The type of HTTPService that handles incoming requests.
625+
Args:
626+
ln: TCP server that listens for incoming connections.
627+
handler: An object that handles incoming HTTP requests.
628+
executor: The executor that dispatches each accepted connection.
629+
630+
Raises:
631+
ServerError: If accept fails or critical connection handling errors occur.
632+
"""
560633
while True:
561634
var conn: TCPConnection[NetworkType.tcp4]
562635
try:
563636
conn = ln.accept()
564637
except listener_err:
565638
raise listener_err^
566-
567-
var index: Int
568-
try:
569-
index = provision_pool.borrow()
570-
except provision_err:
571-
# Pool exhausted - close the connection and continue
572-
try:
573-
conn^.teardown()
574-
except:
575-
pass
576-
continue
577-
578-
try:
579-
handle_connection(
580-
conn,
581-
provision_pool.provisions[index],
582-
handler,
583-
self.config,
584-
self.address(),
585-
self.tcp_keep_alive,
586-
)
587-
except socket_err:
588-
# Connection handling failed - just close the connection
589-
pass
590-
finally:
591-
try:
592-
conn^.teardown()
593-
except:
594-
pass
595-
provision_pool.provisions[index].prepare_for_new_request()
596-
provision_pool.provisions[index].keepalive_count = 0
597-
provision_pool.release(index)
639+
executor.execute(conn^, handler)
598640

599641

600642
fn _send_error_response(mut conn: TCPConnection[NetworkType.tcp4], var response: HTTPResponse):

0 commit comments

Comments
 (0)