Skip to content

Entrypoint

The entrypoint package provides the main HTTP entry point for GoDoxy, handling domain-based routing, middleware application, short link matching, access logging, and HTTP server lifecycle management.

Overview

The entrypoint package implements the primary HTTP handler that receives all incoming requests, manages the lifecycle of HTTP servers, determines the target route based on hostname, applies middleware, and forwards requests to the appropriate route handler.

Key Features

  • Domain-based route lookup with subdomain support
  • Short link (go/<alias> domain) handling
  • Middleware chain application
  • Access logging for all requests
  • Configurable not-found handling
  • Per-domain route resolution
  • HTTP server management (HTTP/HTTPS)
  • Route pool abstractions via PoolLike and RWPoolLike interfaces

Primary Consumers

  • HTTP servers: Per-listen-addr servers dispatch requests to routes
  • Route providers: Register routes via StartAddRoute
  • Configuration layer: Validates and applies middleware/access-logging config

Non-goals

  • Does not implement route discovery (delegates to providers)
  • Does not handle TLS certificate management (delegates to autocert)
  • Does not implement health checks (delegates to internal/health/monitor)
  • Does not manage TCP/UDP listeners directly (only HTTP/HTTPS via goutils/server)

Stability

Internal package with stable core interfaces. The Entrypoint interface is the public contract.

Public API

Entrypoint Interface

go
type Entrypoint interface {
    // Server capabilities
    SupportProxyProtocol() bool
    DisablePoolsLog(v bool)

    // Route registry access
    GetRoute(alias string) (types.Route, bool)
    StartAddRoute(r types.Route) error
    IterRoutes(yield func(r types.Route) bool)
    NumRoutes() int
    RoutesByProvider() map[string][]types.Route

    // Route pool accessors
    HTTPRoutes() PoolLike[types.HTTPRoute]
    StreamRoutes() PoolLike[types.StreamRoute]
    ExcludedRoutes() RWPoolLike[types.Route]

    // Health info queries
    GetHealthInfo() map[string]types.HealthInfo
    GetHealthInfoWithoutDetail() map[string]types.HealthInfoWithoutDetail
    GetHealthInfoSimple() map[string]types.HealthStatus

    // Configuration
    SetFindRouteDomains(domains []string)
    SetMiddlewares(mws []map[string]any) error
    SetNotFoundRules(rules rules.Rules)
    SetAccessLogger(parent task.Parent, cfg *accesslog.RequestLoggerConfig) error

    // Context integration
    ShortLinkMatcher() *ShortLinkMatcher
}

Pool Interfaces

go
type PoolLike[Route types.Route] interface {
    Get(alias string) (Route, bool)
    Iter(yield func(alias string, r Route) bool)
    Size() int
}

type RWPoolLike[Route types.Route] interface {
    PoolLike[Route]
    Add(r Route)
    Del(r Route)
}

Configuration

go
type Config struct {
    SupportProxyProtocol bool                     `json:"support_proxy_protocol"`
    Rules                struct {
        NotFound rules.Rules                     `json:"not_found"`
    }                                               `json:"rules"`
    Middlewares          []map[string]any          `json:"middlewares"`
    AccessLog            *accesslog.RequestLoggerConfig `json:"access_log" validate:"omitempty"`
}

Context Functions

go
func SetCtx(ctx interface{ SetValue(any, any) }, ep Entrypoint)
func FromCtx(ctx context.Context) Entrypoint

Architecture

Core Components

Request Processing Pipeline

Server Lifecycle

Data Flow

Route Registry

Routes are managed per-entrypoint:

go
// Adding a route (main entry point for providers)
if err := ep.StartAddRoute(route); err != nil {
    return err
}

// Iterating all routes including excluded
ep.IterRoutes(func(r types.Route) bool {
    log.Info().Str("alias", r.Name()).Msg("route")
    return true // continue iteration
})

// Querying by alias
route, ok := ep.GetRoute("myapp")

// Grouping by provider
byProvider := ep.RoutesByProvider()

Configuration Surface

Config Source

Environment variables and YAML config file:

yaml
entrypoint:
  support_proxy_protocol: true
  middlewares:
    - rate_limit:
        requests_per_second: 100
  rules:
    not_found:
      # not-found rules configuration
  access_log:
    path: /var/log/godoxy/access.log

Environment Variables

VariableDescription
PROXY_SUPPORT_PROXY_PROTOCOLEnable PROXY protocol support

Dependency and Integration Map

DependencyPurpose
internal/routeRoute types and handlers
internal/route/rulesNot-found rules processing
internal/logging/accesslogRequest logging
internal/net/gphttp/middlewareMiddleware chain
internal/typesRoute and health types
github.com/puzpuzpuz/xsync/v4Concurrent server map
github.com/yusing/goutils/poolRoute pool implementations
github.com/yusing/goutils/taskLifecycle management
github.com/yusing/goutils/serverHTTP/HTTPS server lifecycle

Observability

Logs

LevelContextDescription
DEBUGroute, listen_urlRoute addition/removal
DEBUGaddr, protoServer lifecycle
ERRORroute, listen_urlServer startup failures

Metrics

Route metrics exposed via GetHealthInfo methods:

go
// Health info for all routes
healthMap := ep.GetHealthInfo()
// {
//   "myapp": {Status: "healthy", Uptime: 3600, Latency: 5ms},
//   "excluded-route": {Status: "unknown", Detail: "n/a"},
// }

Security Considerations

  • Route lookup is read-only from route pools
  • Middleware chain is applied per-request
  • Proxy protocol support must be explicitly enabled
  • Access logger captures request metadata before processing
  • Short link matching is limited to configured domains

Failure Modes and Recovery

FailureBehaviorRecovery
Server bind failsError returned, route not addedFix port/address conflict
Route start failsRoute excluded, error loggedFix route configuration
Middleware load failsSetMiddlewares returns errorFix middleware configuration
Context cancelledAll servers stopped gracefullyRestart entrypoint

Usage Examples

Basic Setup

go
ep := entrypoint.NewEntrypoint(parent, &entrypoint.Config{
    SupportProxyProtocol: false,
})

// Configure domain matching
ep.SetFindRouteDomains([]string{".example.com", "example.com"})

// Configure middleware
err := ep.SetMiddlewares([]map[string]any{
    {"rate_limit": map[string]any{"requests_per_second": 100}},
})
if err != nil {
    return err
}

// Configure access logging
err = ep.SetAccessLogger(parent, &accesslog.RequestLoggerConfig{
    Path: "/var/log/godoxy/access.log",
})
if err != nil {
    return err
}

Route Querying

go
// Iterate all routes including excluded
ep.IterRoutes(func(r types.Route) bool {
    log.Info().
        Str("alias", r.Name()).
        Str("provider", r.ProviderName()).
        Bool("excluded", r.ShouldExclude()).
        Msg("route")
    return true // continue iteration
})

// Get health info for all routes
healthMap := ep.GetHealthInfoSimple()
for alias, status := range healthMap {
    log.Info().Str("alias", alias).Str("status", string(status)).Msg("health")
}

Route Addition

Routes are typically added by providers via StartAddRoute:

go
// StartAddRoute handles route registration and server creation
if err := ep.StartAddRoute(route); err != nil {
    return err
}

Context Integration

Routes can access the entrypoint from request context:

go
// Set entrypoint in context (typically during initialization)
entrypoint.SetCtx(task, ep)

// Get entrypoint from context
if ep := entrypoint.FromCtx(r.Context()); ep != nil {
    route, ok := ep.GetRoute("alias")
}

Testing Notes

Released under the MIT License.