Files
bux-lang/docs/superpowers/specs/2026-06-14-nexus-rewrite-design.md
dimgigov aaeb01e518 feat(nexus): production modular HTTP/1.1 server + compiler fixes
- Rewrite apps/nexus with modular architecture:
  Config, Http, Errors, Parser, Router, Handlers, Server, Main
- Use algebraic enums for ParseResult/FileResult/HttpError
- Thread-pool server via Channel<ConnectionTask> and spawn
- Fix C backend type ordering for generic struct instances
  (Array_T, Iter_T) and algebraic enum struct payloads
- Collect Slice_T types from struct fields and enum payloads
- Fix match lowering for simple enums (direct value compare)
- Resolve match expression return type from first arm
- Infer element type for for-in over Array<UserStruct>
- Preserve generic type args in field access resolution
- Add fflush to PrintLine/Print for immediate server logs
- Add modern_features golden regression test
- Regenerate golden expected.c files
2026-06-15 00:54:03 +03:00

11 KiB

Nexus HTTP Server Rewrite — Design Spec

Date: 2026-06-14
Goal: Rewrite apps/nexus as a production-ready, modular HTTP/1.1 server that exercises modern Bux language features.


1. Scope

Keep the same observable behavior as the current Nexus app, but restructure it for production quality:

  • HTTP/1.1 request parsing
  • Static file serving from ./public/
  • API endpoints: GET /api/health, GET /api/info
  • WebSocket upgrade detection on /ws
  • Multi-threaded request handling

What changes:

  • Monolithic Main.bux → modular source tree
  • Function-pointer dispatch → interface-based handlers and middleware
  • Ad-hoc error handling → algebraic enum Result/Option patterns
  • Raw pointer buffer parsing → @[Checked] borrow-checked parsing
  • Direct worker spawn → thread-pool backed by Channel<ConnectionTask>

2. Module Layout

apps/nexus/
├── bux.toml
├── README.md
├── public/
│   ├── index.html
│   └── 404.html
└── src/
    ├── Main.bux          # Entry point: config, socket setup, spawn workers, run acceptor
    ├── Config.bux        # ServerConfig struct + CTFE defaults
    ├── Errors.bux        # HttpError enum + Result/Option helpers
    ├── Http.bux          # HttpMethod enum, Request/Response structs, status text, MIME
    ├── Parser.bux        # @[Checked] HTTP/1.1 request parser
    ├── Router.bux        # Route, Router, HttpHandler interface
    ├── Middleware.bux    # Middleware interface + LoggingMiddleware
    ├── Handlers.bux      # Static files, API, WebSocket upgrade, 404
    └── Server.bux        # ConnectionTask, Worker, acceptor loop, channel plumbing

3. Language Features to Exercise

Feature Where
Modules & pub/private Every file
Algebraic enums HttpMethod, HttpError, ParseResult
Pattern matching (match) Error dispatch in Main, method parsing
Structs + extend methods HttpRequest, HttpResponse, Router, ServerConfig
interface + extend ... for HttpHandler, Middleware
Generics Channel<ConnectionTask>
for ... in loops Header iteration, route dispatch (over Array<HeaderEntry> and Array<Route>)
Array<T> generic container Header storage, route table
@[Checked] + & / &mut Parser.bux string buffer access
Result + ? operator Parser fallible operations
const func / CTFE Default config values
spawn + Channel<T> Thread-pool in Server.bux
Raw multi-line / interpolated strings Response templates, logs

4. Core Types

4.1 HTTP Types (Http.bux)

pub enum HttpMethod {
    GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, UNKNOWN,
}

pub enum HttpError {
    BadRequest,
    NotFound,
    MethodNotAllowed,
    InternalError(String),
}

pub struct HeaderEntry {
    key: String;
    value: String;
}

pub struct HttpRequest {
    method: HttpMethod;
    path: String;
    version: String;
    body: String;
    headers: Array<HeaderEntry>;
}

pub struct HttpResponse {
    statusCode: int;
    contentType: String;
    body: String;
    extraHeaders: String;
}

4.2 Result Types (Errors.bux)

Bux's Std::Result only carries int in Ok, so Nexus defines domain-specific enums:

pub enum ParseResult {
    Ok(HttpRequest),
    Err(HttpError),
}

pub enum FileResult {
    Ok(String),
    Err(HttpError),
}

Helpers:

  • ParseResult_IsOk, ParseResult_UnwrapOr
  • FileResult_IsOk, FileResult_UnwrapOr

If the bootstrap ? operator only recognizes Result/Option with exact variant names, parser code will fall back to explicit match.

4.3 Handler & Middleware Interfaces

// Router.bux
pub interface HttpHandler {
    func Handle(self: &Self, req: &HttpRequest) -> HttpResponse;
}

pub enum Handler {
    StaticFile,
    ApiHealth,
    ApiInfo,
    WsUpgrade,
    NotFound,
}

extend Handler for HttpHandler {
    func Handle(self: &Handler, req: &HttpRequest) -> HttpResponse {
        match self {
            Handler::StaticFile => return ServeStaticFile(req),
            Handler::ApiHealth  => return HandleApiHealth(),
            Handler::ApiInfo    => return HandleApiInfo(),
            Handler::WsUpgrade  => return HandleWebSocketUpgrade(req),
            Handler::NotFound   => return NotFoundResponse(),
        }
        return NotFoundResponse();
    }
}

pub struct Route {
    method: HttpMethod;
    path: String;
    handler: Handler;
}

pub struct Router {
    routes: Array<Route>;
    notFound: Handler;
}

// Middleware.bux
pub interface Middleware {
    func Process(self: &Self, req: &HttpRequest, next: &HttpHandler) -> HttpResponse;
}

Bux interfaces currently support static dispatch through generics, not dynamic *Handler pointers. The route table stores the concrete Handler enum and dispatches via match (showcasing algebraic enums + pattern matching). Middleware uses the interface with generic bounds (T: HttpHandler) for static composition.


5. Data Flow

  1. Main reads ServerConfig and creates a listening socket.
  2. Main spawns workerCount - 1 worker threads; the main thread becomes the last worker.
  3. Main spawns one dedicated acceptor thread that loops on Net_Accept and pushes ConnectionTask { fd } into Channel<ConnectionTask>.
  4. Each worker loops:
    task = Channel_Recv(&taskQueue)
    HandleConnection(task.fd, &appHandler)
    Net_Close(task.fd)
    
  5. HandleConnection:
    • Net_Recv raw request
    • ParseRequest(raw)ParseResult (matched explicitly; ? is used only for Result/int payloads)
    • On success, Router.Dispatch(&req) or middleware chain
    • Http_BuildResponse(resp) + Net_Send

6. Parser Design (Parser.bux)

Parser functions take the raw request by value (String is already a C-string reference) and build an HttpRequest:

pub func ParseRequest(raw: String) -> ParseResult {
    match FindToken(raw, " ") {
        ParseResult::Ok(end) => {
            let methodStr: String = Slice(raw, 0, end);
            let method: HttpMethod = ParseMethod(methodStr);
            // ... path, version, headers, body
            return ParseResult_NewOk(req);
        }
        ParseResult::Err(e) => return ParseResult_NewErr(e),
    }
}

@[Checked] is used where mutable borrows make sense (e.g. SetResponseCode(resp: &mut HttpResponse, code: int)), not on raw string parsing.

Headers are stored in a dynamically grown Array<HeaderEntry>.


7. Router & Middleware

7.1 Router Dispatch

extend Router {
    pub func Dispatch(self: *Router, req: &HttpRequest) -> HttpResponse {
        for route in self.routes {
            if route.method == req.method && String_Eq(route.path, req.path) {
                return route.handler.Handle(req);  // Handler enum implements HttpHandler
            }
        }
        return self.notFound.Handle(req);
    }
}

7.2 Middleware Chain

LoggingMiddleware logs method/path and delegates to next:

pub struct LoggingMiddleware {}

extend LoggingMiddleware for Middleware {
    func Process<T: HttpHandler>(self: *LoggingMiddleware, req: &HttpRequest, next: &T) -> HttpResponse {
        Print(Http_MethodName(req.method));
        Print(" ");
        PrintLine(req.path);
        return next.Handle(req);
    }
}

Chaining is achieved with a generic MiddlewareHandler<M: Middleware, T: HttpHandler> struct that implements HttpHandler and holds (middleware, next). This uses Bux's static-dispatch interfaces (no vtable pointers).


8. Handlers (Handlers.bux)

Handler Purpose
StaticFileHandler Serve files from public/ with MIME sniffing and path-traversal guard
HealthHandler GET /api/health → JSON status
InfoHandler GET /api/info → JSON server info
WsUpgradeHandler GET /ws or Upgrade: websocket → 101 response with placeholder accept key
NotFoundHandler 404 JSON/text fallback

All implement HttpHandler.


9. Server & Thread Pool (Server.bux)

pub struct ConnectionTask {
    fd: int;
}

pub struct Server {
    config: ServerConfig;
    taskQueue: Channel<ConnectionTask>;
    handler: *HttpHandler;
}

Flow:

  1. Server_Run(&server) creates socket, binds, listens.
  2. Spawns workers; each calls Worker_Run(&server).
  3. Acceptor_Run(&server) loops:
    let fd: int = Net_Accept(serverFd);
    if fd >= 0 {
        let task: ConnectionTask = ConnectionTask { fd: fd };
        Channel_Send<ConnectionTask>(&server.taskQueue, task);
    }
    
  4. Worker_Run pops tasks and calls HandleConnection(task.fd, server.handler).

Socket fd ownership is always with the worker that received the task; it is closed before the worker loops again.


10. Config (Config.bux)

pub struct ServerConfig {
    bindAddr: String;
    port: int;
    workerCount: int;
    publicDir: String;
    backlog: int;
}

const func DefaultConfig() -> ServerConfig {
    return ServerConfig {
        bindAddr: "0.0.0.0",
        port: 8080,
        workerCount: 4,
        publicDir: "public",
        backlog: 128,
    };
}

11. Testing & Verification

  • buxc check must pass on the whole package.
  • buxc run starts the server; verify with:
    • curl http://localhost:8080/
    • curl http://localhost:8080/api/health
    • curl http://localhost:8080/api/info
    • curl -i -N -H "Upgrade: websocket" -H "Connection: Upgrade" http://localhost:8080/ws
  • Existing integration test surface (buxc check on apps/nexus) remains the gate.

12. Out of Scope

  • Real HTTP/2 frame parsing or WebSocket frame parsing (keep detection/upgrade only, same as current).
  • TLS/HTTPS.
  • Full SHA-1/Base64 WebSocket accept key (keep placeholder).
  • Generic Result<T, E> (Bux Std::Result is Ok(int) only; use domain enums instead).

13. Risks & Mitigations

Risk Mitigation
interface vtables with generic structs Avoid generic interfaces; use concrete *HttpHandler pointers.
Channel<T> with non-trivial T ConnectionTask contains only int; safe to copy.
? operator on custom enums Fall back to explicit match if needed.
@[Checked] borrow checker on raw String Use &String for read-only parsing; avoid &mut unless mutation is required.

14. Success Criteria

  1. buxc check apps/nexus passes with zero errors.
  2. buxc run in apps/nexus serves static files and API endpoints correctly.
  3. The new code uses at minimum: modules, algebraic enums, pattern matching, interfaces, extend, generics, for ... in, Channel<T>, spawn, Result/Option, @[Checked].