How CasaOS Multiplexes API Versions Using HandlerMultiplexer

CasaOS routes requests for multiple API versions (v1, v2, v3) and documentation through a single HTTP server by using the util_http.HandlerMultiplexer type from the CasaOS-Common library, which inspects the first URL path segment and dispatches to version-specific Echo routers.

CasaOS serves its REST API across three major versions from one HTTP server without URL conflicts. The system achieves this through a centralized multiplexing pattern implemented in the shared CasaOS-Common repository. By using the HandlerMultiplexer type, CasaOS cleanly separates version-specific routing logic while maintaining a concise server configuration in main.go.

HandlerMultiplexer Architecture

The multiplexing strategy centers on a map that associates URL prefixes with concrete http.Handler implementations. In main.go, the server initializes separate Echo routers for each API version and registers them under distinct keys.

Building the Handler Map

The server constructs the multiplexer by mapping path prefixes to their respective routers:

// main.go – HTTP server setup
v1Router := route.InitV1Router()
v2Router := route.InitV2Router()
v2DocRouter := route.InitV2DocRouter(_docHTML, _docYAML)
v3File := route.InitFile()

mux := &util_http.HandlerMultiplexer{
    HandlerMap: map[string]http.Handler{
        "v1":  v1Router,    // Handles /v1/... routes
        "v2":  v2Router,    // Handles /v2/... routes
        "v3":  v3File,      // Handles /v3/... routes (file API)
        "doc": v2DocRouter, // Handles /doc/... routes (OpenAPI UI)
    },
}

s := &http.Server{
    Handler:           mux,
    ReadHeaderTimeout: 5 * time.Second,
}

This configuration makes the HandlerMultiplexer the sole entry point for all incoming HTTP requests.

Request Dispatch and Path Rewriting

The HandlerMultiplexer processes each request by extracting the first path segment to determine the target version. According to the CasaOS-Common source code, the implementation resides in util_http/handler_multiplexer.go.

Core ServeHTTP Logic

When a request arrives, the multiplexer performs three operations:

  • Extracts the prefix by splitting the URL path
  • Looks up the handler in the configured map
  • Rewrites the path to remove the version prefix before forwarding
func (h *HandlerMultiplexer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // Split "/v1/..." → ["v1", "…"]
    parts := strings.SplitN(strings.TrimPrefix(r.URL.Path, "/"), "/", 2)
    if len(parts) == 0 {
        http.NotFound(w, r)
        return
    }
    prefix := parts[0]

    // Find the appropriate handler
    handler, ok := h.HandlerMap[prefix]
    if !ok {
        http.NotFound(w, r)
        return
    }

    // Rewrite path for downstream router
    if len(parts) > 1 {
        r.URL.Path = "/" + parts[1]
    } else {
        r.URL.Path = "/"
    }
    handler.ServeHTTP(w, r)
}

This path stripping ensures that version-specific Echo routers receive URLs without the version prefix, allowing routes to be defined cleanly (e.g., /sys/info rather than /v1/sys/info).

Version-Specific Route Configuration

Each API version maintains its own routing logic in dedicated packages. The v1 routes live in route/v1/*, while v2 routes reside in route/v2/*. The documentation endpoint uses route/v2/doc.go.

Because the HandlerMultiplexer removes the version prefix before dispatch, the Echo routers define their routes relative to the root:

  • GET /v1/sys/info → v1 router handles /sys/info
  • GET /v2/port/list → v2 router handles /port/list
  • GET /doc/openapi.yaml → doc router handles /openapi.yaml

Practical API Request Examples

The following curl commands demonstrate how the multiplexer routes requests to different handlers.

Accessing the v1 system API:

curl http://localhost:80/v1/sys/info

The multiplexer extracts "v1", strips the prefix, and forwards the request to v1Router with the path /sys/info.

Accessing the v2 port management API:

curl http://localhost:80/v2/port/list

This routes to v2Router, which processes the request as /port/list.

Accessing the OpenAPI documentation:

curl http://localhost:80/doc/openapi.yaml

The request dispatches to v2DocRouter, serving the OpenAPI specification.

Summary

  • The util_http.HandlerMultiplexer from CasaOS-Common enables CasaOS to serve multiple API versions from a single HTTP server.
  • Configuration occurs in main.go, where version-specific Echo routers register under keys like "v1", "v2", and "doc".
  • The multiplexer extracts the first URL path segment, looks up the corresponding handler, and strips the prefix before forwarding.
  • Each version maintains isolated route definitions in route/v1/* and route/v2/*, receiving paths without version prefixes.
  • Unmatched prefixes return 404 Not Found immediately.

Frequently Asked Questions

What is the HandlerMultiplexer in CasaOS?

The HandlerMultiplexer is a Go type defined in the CasaOS-Common library at util_http/handler_multiplexer.go. It implements the http.Handler interface to route incoming requests to different sub-handlers based on the first path segment, enabling a single server to host multiple API versions.

How does CasaOS handle API versioning?

CasaOS handles API versioning through URL path prefixes (e.g., /v1/, /v2/) processed by the HandlerMultiplexer. Each version has its own Echo router initialized in main.go, and the multiplexer dispatches requests to the appropriate router while stripping the version prefix so that routes can be defined independently of the version string.

Where is the HandlerMultiplexer defined?

The HandlerMultiplexer struct and its ServeHTTP method are defined in the CasaOS-Common repository within the file util_http/handler_multiplexer.go. CasaOS imports this shared library to handle request multiplexing in its main application.

Why does CasaOS strip the version prefix from the URL?

CasaOS strips the version prefix to keep route definitions clean and maintainable. By removing /v1 or /v2 before dispatch, the Echo routers can define routes like /sys/info instead of /v1/sys/info, preventing tight coupling between the version identifier and the business logic of each API endpoint.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →