server

package
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README

Glyph HTTP Server

A production-ready HTTP server implementation for the Glyph language that handles route registration, request/response processing, and middleware execution.

Features

  • Full HTTP server implementation using Go's net/http
  • Route registration with path parameters (:id, :userId, etc.)
  • Query parameter parsing
  • JSON request body parsing
  • JSON response serialization
  • Error handling with proper HTTP status codes
  • Middleware chain support
  • Support for all HTTP methods: GET, POST, PUT, DELETE, PATCH

Architecture

The server is composed of several components:

Core Types (types.go)
  • Route: Represents an Glyph route definition
  • Context: Request context with parsed parameters
  • RouteHandler: Function signature for route handlers
  • Middleware: Function signature for middleware
  • Interpreter: Interface for executing Glyph route logic
Router (router.go)
  • Route registration and storage
  • Path pattern parsing (static segments and parameters)
  • Route matching with parameter extraction
  • Efficient route lookup
Handler (handler.go)
  • HTTP request/response handling
  • Query parameter parsing
  • JSON body parsing
  • Response serialization
  • Error handling and logging
Middleware (middleware.go)
  • Built-in middleware:
    • LoggingMiddleware(): Request/response logging
    • RecoveryMiddleware(): Panic recovery
    • CORSMiddleware(): CORS header support
    • HeaderMiddleware(): Custom header injection
    • ChainMiddlewares(): Combine multiple middlewares
Server (server.go)
  • Main HTTP server implementation
  • Route registration API
  • Graceful shutdown support
  • Mock interpreter for testing

Usage

Basic Server
package main

import (
    "github.com/glyphlang/glyph/pkg/server"
)

func main() {
    // Create server with mock interpreter
    srv := server.NewServer(
        server.WithAddr(":8080"),
        server.WithInterpreter(&server.MockInterpreter{}),
    )

    // Register routes
    srv.RegisterRoute(&server.Route{
        Method: server.GET,
        Path:   "/hello",
    })

    // Start server
    srv.Start(":8080")
}
Route with Path Parameters
srv.RegisterRoute(&server.Route{
    Method: server.GET,
    Path:   "/api/users/:id",
})

// Request: GET /api/users/123
// PathParams: {"id": "123"}
Multiple Path Parameters
srv.RegisterRoute(&server.Route{
    Method: server.GET,
    Path:   "/api/users/:userId/posts/:postId",
})

// Request: GET /api/users/42/posts/99
// PathParams: {"userId": "42", "postId": "99"}
Custom Handler
srv.RegisterRoute(&server.Route{
    Method: server.POST,
    Path:   "/api/users",
    Handler: func(ctx *server.Context) error {
        // Access request body
        name := ctx.Body["name"].(string)
        email := ctx.Body["email"].(string)

        // Process...
        user := createUser(name, email)

        // Send response
        return server.SendJSON(ctx, 201, map[string]interface{}{
            "id":    user.ID,
            "name":  user.Name,
            "email": user.Email,
        })
    },
})
Middleware
// Add global middleware
srv := server.NewServer(
    server.WithMiddleware(server.LoggingMiddleware()),
    server.WithMiddleware(server.RecoveryMiddleware()),
)

// Add route-specific middleware
authMiddleware := func(next server.RouteHandler) server.RouteHandler {
    return func(ctx *server.Context) error {
        // Check authentication
        token := ctx.Request.Header.Get("Authorization")
        if token == "" {
            return server.SendError(ctx, 401, "unauthorized")
        }
        return next(ctx)
    }
}

srv.RegisterRoute(&server.Route{
    Method:      server.GET,
    Path:        "/api/protected",
    Middlewares: []server.Middleware{authMiddleware},
})
Query Parameters
// Request: GET /api/users?page=2&limit=10
// QueryParams: {"page": "2", "limit": "10"}

srv.RegisterRoute(&server.Route{
    Method: server.GET,
    Path:   "/api/users",
    Handler: func(ctx *server.Context) error {
        page := ctx.QueryParams["page"]
        limit := ctx.QueryParams["limit"]
        // Use page and limit...
        return server.SendJSON(ctx, 200, users)
    },
})
JSON Request Body
srv.RegisterRoute(&server.Route{
    Method: server.POST,
    Path:   "/api/users",
    Handler: func(ctx *server.Context) error {
        // ctx.Body contains parsed JSON
        name := ctx.Body["name"].(string)
        email := ctx.Body["email"].(string)

        // Process...
        return server.SendJSON(ctx, 201, result)
    },
})
Error Handling
srv.RegisterRoute(&server.Route{
    Method: server.GET,
    Path:   "/api/users/:id",
    Handler: func(ctx *server.Context) error {
        id := ctx.PathParams["id"]

        user, err := findUser(id)
        if err != nil {
            return server.SendError(ctx, 404, "user not found")
        }

        return server.SendJSON(ctx, 200, user)
    },
})
Graceful Shutdown
import (
    "context"
    "os"
    "os/signal"
    "time"
)

func main() {
    srv := server.NewServer()
    // Register routes...

    // Start server in goroutine
    go func() {
        if err := srv.Start(":8080"); err != nil {
            log.Fatal(err)
        }
    }()

    // Wait for interrupt signal
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)
    <-quit

    // Graceful shutdown with 30 second timeout
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := srv.Stop(ctx); err != nil {
        log.Fatal(err)
    }
}

Testing

Run the test suite:

go test ./pkg/server/... -v

Run benchmarks:

go test ./pkg/server/... -bench=.

Example curl Commands

Once the server is running, you can test it with curl:

# GET request
curl http://localhost:8080/hello

# GET with path parameters
curl http://localhost:8080/api/users/123

# GET with query parameters
curl http://localhost:8080/api/users?page=1&limit=10

# POST with JSON body
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"name": "John Doe", "email": "[email protected]"}'

# PUT request
curl -X PUT http://localhost:8080/api/users/123 \
  -H "Content-Type: application/json" \
  -d '{"name": "Jane Doe"}'

# DELETE request
curl -X DELETE http://localhost:8080/api/users/123

# PATCH request
curl -X PATCH http://localhost:8080/api/users/123 \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]"}'

Mock Interpreter

The MockInterpreter provides a simple implementation for testing:

interpreter := &server.MockInterpreter{
    Response: map[string]interface{}{
        "message": "Hello from mock",
        "data":    []int{1, 2, 3},
    },
}

srv := server.NewServer(server.WithInterpreter(interpreter))

The mock will echo back request details (path, method, params, body) if no custom response is set.

Integration with Glyph

When integrated with the Glyph compiler and interpreter, routes will be registered from parsed Glyph source files:

// Parse Glyph file
routes := parser.Parse("main.glyph")

// Register with server
srv.RegisterRoutes(routes)

// Start server
srv.Start(":8080")

The interpreter will execute the Glyph route logic when requests are received.

Performance

The router uses efficient pattern matching with O(n) complexity where n is the number of registered routes. Path parameter extraction is optimized with pre-parsed segments.

Benchmarks on a typical development machine:

  • Route matching: ~500 ns/op
  • Full request handling with JSON: ~5-10 μs/op

Error Responses

All errors are returned as JSON:

{
  "error": true,
  "message": "route not found",
  "code": 404,
  "details": "no route matches path /api/invalid"
}

Status Codes

The server uses appropriate HTTP status codes:

  • 200 OK: Successful request
  • 201 Created: Resource created
  • 204 No Content: Successful with no response body
  • 400 Bad Request: Invalid JSON or request format
  • 404 Not Found: Route not found
  • 500 Internal Server Error: Handler error or panic

Thread Safety

The server is thread-safe and can handle concurrent requests. Route registration should be done before starting the server.

Logging

The server includes built-in request/response logging via LoggingMiddleware():

[REQUEST] GET /api/users
[RESPONSE] GET /api/users - 200 (1.234ms)

Errors are logged with details:

[ERROR] POST /api/users: invalid JSON body - unexpected token

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConvertPatternToMuxFormat added in v0.3.5

func ConvertPatternToMuxFormat(pattern string) string

ConvertPatternToMuxFormat converts Glyph's :param syntax to Go's {param} syntax for http.ServeMux pattern matching. e.g., "/chat/:room" becomes "/chat/{room}"

func ExtractPathParamValues added in v0.3.5

func ExtractPathParamValues(pattern, actualPath string) map[string]string

ExtractPathParamValues extracts parameter values from an actual path given a pattern. pattern: "/chat/:room" actualPath: "/chat/general" -> {"room": "general"} Handles URL-encoded values by decoding them.

func ExtractRouteParamNames added in v0.3.5

func ExtractRouteParamNames(path string) []string

ExtractRouteParamNames extracts parameter names from a route pattern. e.g., "/users/:id/:action" returns ["id", "action"] e.g., "/chat/:room" returns ["room"]

func GetStatusCode

func GetStatusCode(err error) int

GetStatusCode extracts the status code from an error Returns 500 if not an HTTPError

func IsHTTPError

func IsHTTPError(err error) bool

IsHTTPError checks if an error implements HTTPError interface

func LivenessHTTPHandler

func LivenessHTTPHandler() http.HandlerFunc

LivenessHTTPHandler creates a standard http.Handler for liveness checks

func ReadinessHTTPHandler

func ReadinessHTTPHandler(hm *HealthManager) http.HandlerFunc

ReadinessHTTPHandler creates a standard http.Handler for readiness checks

func SendError

func SendError(ctx *Context, statusCode int, message string) error

SendError is a helper to send error responses

func SendHTTPError

func SendHTTPError(ctx *Context, err HTTPError) error

SendHTTPError is a helper to send HTTPError responses in handlers

func SendJSON

func SendJSON(ctx *Context, statusCode int, data interface{}) error

SendJSON is a helper to send JSON responses from handlers

func SetTrustedProxies added in v0.5.0

func SetTrustedProxies(proxies []string)

SetTrustedProxies configures the set of trusted proxy IP addresses. Only requests from these addresses will have their X-Forwarded-For and X-Real-IP headers honored for client IP extraction. This function is safe for concurrent use.

func WriteError

func WriteError(w http.ResponseWriter, err error)

WriteError writes a generic error as JSON response If err implements HTTPError, uses that. Otherwise creates InternalError.

func WriteErrorResponse

func WriteErrorResponse(w http.ResponseWriter, err HTTPError)

WriteErrorResponse writes an HTTPError as JSON response

Types

type AuthRateLimitConfig

type AuthRateLimitConfig struct {
	MaxFailures     int           // Maximum failures before lockout (default: 5)
	LockoutDuration time.Duration // Initial lockout duration (default: 1 minute)
	MaxLockout      time.Duration // Maximum lockout duration with exponential backoff (default: 15 minutes)
	ResetAfter      time.Duration // Reset failure count after this duration of no failures (default: 15 minutes)
	TrustProxy      bool          // When true, use X-Forwarded-For/X-Real-IP headers for client IP
}

AuthRateLimitConfig configures auth rate limiting behavior

func DefaultAuthRateLimitConfig

func DefaultAuthRateLimitConfig() AuthRateLimitConfig

DefaultAuthRateLimitConfig returns sensible defaults for auth rate limiting

type BadRequestError

type BadRequestError struct {
	*BaseError
}

BadRequestError represents a 400 Bad Request error (generic)

func NewBadRequestError

func NewBadRequestError(message string) *BadRequestError

NewBadRequestError creates a new bad request error

func NewBadRequestErrorWithCause

func NewBadRequestErrorWithCause(message string, cause error) *BadRequestError

NewBadRequestErrorWithCause creates a bad request error with cause

type BaseError

type BaseError struct {
	Code   int
	Type   string
	Msg    string
	Detail string
	Cause  error
}

BaseError provides common functionality for all error types

func (*BaseError) Error

func (e *BaseError) Error() string

func (*BaseError) ErrorType

func (e *BaseError) ErrorType() string

func (*BaseError) StatusCode

func (e *BaseError) StatusCode() int

func (*BaseError) ToResponse

func (e *BaseError) ToResponse() *ErrorResponse

type CheckResult

type CheckResult struct {
	Status    HealthStatus `json:"status"`
	LatencyMs int64        `json:"latency_ms,omitempty"`
	Message   string       `json:"message,omitempty"`
	Error     string       `json:"error,omitempty"`
}

CheckResult represents the result of a single health check

type ConflictError

type ConflictError struct {
	*BaseError
	Resource string
}

ConflictError represents a 409 Conflict error

func NewConflictError

func NewConflictError(resource, message string) *ConflictError

NewConflictError creates a new conflict error

type Context

type Context struct {
	Request        *http.Request
	ResponseWriter http.ResponseWriter
	PathParams     map[string]string
	QueryParams    map[string][]string // All values for each query param
	Body           map[string]interface{}
	StatusCode     int
}

Context represents the request context with parsed parameters

type DatabaseHealthChecker

type DatabaseHealthChecker struct {
	// contains filtered or unexported fields
}

DatabaseHealthChecker checks database connectivity

func NewDatabaseHealthChecker

func NewDatabaseHealthChecker(name string, pingFn func(ctx context.Context) error) *DatabaseHealthChecker

NewDatabaseHealthChecker creates a new database health checker

func (*DatabaseHealthChecker) Check

Check performs the database health check

func (*DatabaseHealthChecker) Name

func (d *DatabaseHealthChecker) Name() string

Name returns the checker name

type ErrorResponse

type ErrorResponse struct {
	Status  int    `json:"status"`
	Error   string `json:"error"`
	Message string `json:"message"`
	Details string `json:"details,omitempty"`
}

ErrorResponse represents a standardized JSON error response

type ForbiddenError

type ForbiddenError struct {
	*BaseError
	Action string
}

ForbiddenError represents a 403 Forbidden error

func NewForbiddenError

func NewForbiddenError(action string) *ForbiddenError

NewForbiddenError creates a new forbidden error

type HTTPError

type HTTPError interface {
	error
	StatusCode() int
	ErrorType() string
	ToResponse() *ErrorResponse
}

HTTPError is the base interface for all HTTP errors

func WrapError

func WrapError(err error, defaultMsg string) HTTPError

WrapError converts a standard error to an HTTPError based on context This is useful for converting errors from other packages

type HTTPHealthChecker

type HTTPHealthChecker struct {
	// contains filtered or unexported fields
}

HTTPHealthChecker checks external HTTP service availability

func NewHTTPHealthChecker

func NewHTTPHealthChecker(name, urlStr string) (*HTTPHealthChecker, error)

NewHTTPHealthChecker creates a new HTTP service health checker. The URL must use http or https scheme. Returns an error if the URL is invalid or points to a private/loopback network address.

func (*HTTPHealthChecker) Check

Check performs the HTTP health check

func (*HTTPHealthChecker) Name

func (h *HTTPHealthChecker) Name() string

Name returns the checker name

type HTTPMethod

type HTTPMethod string

HTTPMethod represents an HTTP method

const (
	GET    HTTPMethod = "GET"
	POST   HTTPMethod = "POST"
	PUT    HTTPMethod = "PUT"
	DELETE HTTPMethod = "DELETE"
	PATCH  HTTPMethod = "PATCH"
)

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler creates an HTTP handler function for routing

func NewHandler

func NewHandler(router *Router, interpreter Interpreter) *Handler

NewHandler creates a new handler with the given router and interpreter

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements the http.Handler interface

type HealthCheckFunc

type HealthCheckFunc struct {
	// contains filtered or unexported fields
}

HealthCheckFunc is a function type that implements HealthChecker

func NewHealthCheckFunc

func NewHealthCheckFunc(name string, fn func(ctx context.Context) *CheckResult) *HealthCheckFunc

NewHealthCheckFunc creates a new HealthCheckFunc

func (*HealthCheckFunc) Check

func (f *HealthCheckFunc) Check(ctx context.Context) *CheckResult

Check implements HealthChecker

func (*HealthCheckFunc) Name

func (f *HealthCheckFunc) Name() string

Name implements HealthChecker

type HealthChecker

type HealthChecker interface {
	// Check performs a health check and returns the result
	// The context may contain a timeout
	Check(ctx context.Context) *CheckResult
	// Name returns the name of this health checker
	Name() string
}

HealthChecker is an interface for components that can report their health

type HealthManager

type HealthManager struct {
	// contains filtered or unexported fields
}

HealthManager manages health checks and provides endpoints

Example

ExampleHealthManager demonstrates basic health check setup

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a health manager
	hm := server.NewHealthManager(
		server.WithHealthCheckTimeout(5 * time.Second),
	)

	// Register a custom health checker
	hm.RegisterChecker(server.NewHealthCheckFunc("my-service", func(ctx context.Context) *server.CheckResult {
		// Perform your health check logic here
		return &server.CheckResult{
			Status:    server.StatusHealthy,
			LatencyMs: 5,
		}
	}))

	// Create server and register health routes
	srv := server.NewServer()
	if err := srv.RegisterHealthRoutes(hm); err != nil {
		log.Fatal(err)
	}

	fmt.Println("Health routes registered: /health/live, /health/ready, /health")
}
Output:

Health routes registered: /health/live, /health/ready, /health
Example (CustomChecker)

ExampleHealthManager_customChecker demonstrates creating a custom health checker

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a health manager
	hm := server.NewHealthManager()

	// Create a custom health checker with specific logic
	customChecker := server.NewHealthCheckFunc("redis", func(ctx context.Context) *server.CheckResult {
		start := time.Now()

		// Perform your custom health check logic
		// For example, check Redis connection
		// err := redisClient.Ping(ctx).Err()

		latency := time.Since(start).Milliseconds()

		// Simulate a healthy check
		return &server.CheckResult{
			Status:    server.StatusHealthy,
			LatencyMs: latency,
			Message:   "Redis is operational",
		}
	})

	hm.RegisterChecker(customChecker)

	fmt.Println("Custom Redis health checker registered")
}
Output:

Custom Redis health checker registered
Example (DatabaseChecker)

ExampleHealthManager_databaseChecker demonstrates database health checking

package main

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a health manager
	hm := server.NewHealthManager()

	// Simulate a database connection (in real code, use your actual DB)
	var db *sql.DB // This would be your actual database connection

	// Register a database health checker
	dbChecker := server.NewDatabaseHealthChecker("database", func(ctx context.Context) error {
		// In real code, use db.PingContext(ctx)
		if db == nil {
			return nil // For this example
		}
		return db.PingContext(ctx)
	})

	hm.RegisterChecker(dbChecker)

	fmt.Println("Database health checker registered")
}
Output:

Database health checker registered
Example (DegradedStatus)

ExampleHealthManager_degradedStatus demonstrates handling degraded service status

package main

import (
	"context"
	"fmt"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a custom checker that returns degraded status
	checker := server.NewHealthCheckFunc("slow-service", func(ctx context.Context) *server.CheckResult {
		// Simulate a slow but functional service
		return &server.CheckResult{
			Status:    server.StatusDegraded,
			LatencyMs: 250,
			Message:   "Service is slow but operational",
		}
	})

	fmt.Println(checker.Name())
	result := checker.Check(context.Background())
	fmt.Println(result.Status)
}
Output:

slow-service
degraded
Example (HttpChecker)

ExampleHealthManager_httpChecker demonstrates external service health checking

package main

import (
	"fmt"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a health manager
	hm := server.NewHealthManager()

	// Register an HTTP service health checker
	apiChecker, err := server.NewHTTPHealthChecker("external-api", "https://api.example.com/health")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	hm.RegisterChecker(apiChecker)

	fmt.Println("External API health checker registered")
}
Output:

External API health checker registered
Example (MultipleCheckers)

ExampleHealthManager_multipleCheckers demonstrates using multiple health checkers

package main

import (
	"fmt"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a health manager
	hm := server.NewHealthManager()

	// Register multiple checkers
	hm.RegisterChecker(server.NewStaticHealthChecker("database", server.StatusHealthy))
	hm.RegisterChecker(server.NewStaticHealthChecker("redis", server.StatusHealthy))
	hm.RegisterChecker(server.NewStaticHealthChecker("elasticsearch", server.StatusHealthy))

	fmt.Println("3 health checkers registered")
}
Output:

3 health checkers registered
Example (StandaloneHTTP)

ExampleHealthManager_standaloneHTTP demonstrates using health handlers without the GLYPHLANG server

package main

import (
	"fmt"
	"net/http"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a health manager
	hm := server.NewHealthManager()

	// Register some checkers
	hm.RegisterChecker(server.NewStaticHealthChecker("db", server.StatusHealthy))

	// Use with standard http.ServeMux
	mux := http.NewServeMux()
	mux.HandleFunc("/health/live", server.LivenessHTTPHandler())
	mux.HandleFunc("/health/ready", server.ReadinessHTTPHandler(hm))

	fmt.Println("Health endpoints registered with http.ServeMux")
}
Output:

Health endpoints registered with http.ServeMux
Example (Timeout)

ExampleHealthManager_timeout demonstrates health check with timeout

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create a health manager with short timeout
	hm := server.NewHealthManager(
		server.WithHealthCheckTimeout(100 * time.Millisecond),
	)

	// Register a checker that respects context timeout
	hm.RegisterChecker(server.NewHealthCheckFunc("service", func(ctx context.Context) *server.CheckResult {
		select {
		case <-ctx.Done():
			return &server.CheckResult{
				Status: server.StatusUnhealthy,
				Error:  "timeout exceeded",
			}
		case <-time.After(50 * time.Millisecond):
			return &server.CheckResult{
				Status: server.StatusHealthy,
			}
		}
	}))

	fmt.Println("Health manager configured with 100ms timeout")
}
Output:

Health manager configured with 100ms timeout

func NewHealthManager

func NewHealthManager(options ...HealthManagerOption) *HealthManager

NewHealthManager creates a new health manager

func (*HealthManager) HealthHandler

func (hm *HealthManager) HealthHandler() RouteHandler

HealthHandler handles detailed health check requests Similar to readiness but always returns 200 with detailed status

func (*HealthManager) LivenessHandler

func (hm *HealthManager) LivenessHandler() RouteHandler

LivenessHandler handles liveness probe requests Liveness probes check if the application is running Returns 200 if the application is alive

func (*HealthManager) ReadinessHandler

func (hm *HealthManager) ReadinessHandler() RouteHandler

ReadinessHandler handles readiness probe requests Readiness probes check if the application is ready to serve traffic Returns 200 if ready, 503 if not ready

func (*HealthManager) RegisterChecker

func (hm *HealthManager) RegisterChecker(checker HealthChecker)

RegisterChecker registers a new health checker

func (*HealthManager) UnregisterChecker

func (hm *HealthManager) UnregisterChecker(name string)

UnregisterChecker removes a health checker

type HealthManagerOption

type HealthManagerOption func(*HealthManager)

HealthManagerOption is a functional option for configuring the health manager

func WithHealthCheckTimeout

func WithHealthCheckTimeout(timeout time.Duration) HealthManagerOption

WithHealthCheckTimeout sets the timeout for health checks

type HealthResponse

type HealthResponse struct {
	Status    HealthStatus            `json:"status"`
	Checks    map[string]*CheckResult `json:"checks,omitempty"`
	Timestamp time.Time               `json:"timestamp"`
}

HealthResponse represents the aggregated health check response

type HealthStatus

type HealthStatus string

HealthStatus represents the health status of a component

const (
	// StatusHealthy indicates the component is functioning properly
	StatusHealthy HealthStatus = "healthy"
	// StatusDegraded indicates the component is functioning but with issues
	StatusDegraded HealthStatus = "degraded"
	// StatusUnhealthy indicates the component is not functioning
	StatusUnhealthy HealthStatus = "unhealthy"
)

type InternalError

type InternalError struct {
	*BaseError
}

InternalError represents a 500 Internal Server Error

func NewInternalError

func NewInternalError(message string) *InternalError

NewInternalError creates a new internal server error

func NewInternalErrorWithCause

func NewInternalErrorWithCause(message string, cause error) *InternalError

NewInternalErrorWithCause creates an internal error with a cause

type Interpreter

type Interpreter interface {
	Execute(route *Route, ctx *Context) (interface{}, error)
}

Interpreter interface for executing GLYPH route logic This will be properly implemented later - for now we mock it

type Middleware

type Middleware func(next RouteHandler) RouteHandler

Middleware is a function that wraps a handler

func AuthMiddleware

func AuthMiddleware(validateFunc func(*Context) (bool, error)) Middleware

AuthMiddleware is a placeholder for authentication middleware In production, this would validate JWT tokens, API keys, or session tokens

func BasicAuthMiddleware

func BasicAuthMiddleware(validTokens map[string]bool) Middleware

BasicAuthMiddleware provides simple token-based authentication with rate limiting to prevent brute force attacks

func BasicAuthMiddlewareWithConfig

func BasicAuthMiddlewareWithConfig(validTokens map[string]bool, config AuthRateLimitConfig) Middleware

BasicAuthMiddlewareWithConfig provides token-based authentication with custom rate limit config

func CORSMiddleware

func CORSMiddleware(allowedOrigins []string) Middleware

CORSMiddleware adds CORS headers to responses Security: When allowedOrigins contains "*", we set the literal "*" header and explicitly disable credentials to prevent security vulnerabilities

func CSRFMiddleware added in v0.5.0

func CSRFMiddleware() Middleware

CSRFMiddleware provides Cross-Site Request Forgery protection. It generates a random token and sets it as a cookie. On state-changing requests (POST, PUT, PATCH, DELETE), it validates the token from either the X-CSRF-Token header or the csrf_token form field. This middleware is opt-in.

func ChainMiddlewares

func ChainMiddlewares(middlewares ...Middleware) Middleware

ChainMiddlewares combines multiple middlewares into one

func HeaderMiddleware

func HeaderMiddleware(headers map[string]string) Middleware

HeaderMiddleware adds custom headers to all responses

func LoggingMiddleware

func LoggingMiddleware() Middleware

LoggingMiddleware logs request details

func RateLimitMiddleware

func RateLimitMiddleware(config RateLimiterConfig) Middleware

RateLimitMiddleware implements simple in-memory rate limiting

func RecoveryMiddleware

func RecoveryMiddleware() Middleware

RecoveryMiddleware recovers from panics and returns 500 error It logs full panic details to server logs but returns a generic error to clients to prevent information disclosure

func SecurityHeadersMiddleware

func SecurityHeadersMiddleware() Middleware

SecurityHeadersMiddleware adds security headers to all responses These headers help protect against common web vulnerabilities

func TimeoutMiddleware

func TimeoutMiddleware(timeout time.Duration) Middleware

TimeoutMiddleware adds a timeout to request processing

func TracingMiddleware

func TracingMiddleware(config interface{}) Middleware

TracingMiddleware creates a middleware that adds OpenTelemetry distributed tracing This middleware integrates with the pkg/tracing package It should be added early in the middleware chain to trace the entire request lifecycle

Usage:

import "github.com/glyphlang/glyph/pkg/tracing"

config := tracing.DefaultMiddlewareConfig()
server := NewServer(
    WithMiddleware(TracingMiddleware(config)),
)

Note: This requires the tracing package to be initialized first:

tp, err := tracing.InitTracing(tracing.DefaultConfig())
defer tp.Shutdown(context.Background())

type NotFoundError

type NotFoundError struct {
	*BaseError
	Resource string
}

NotFoundError represents a 404 Not Found error

func NewNotFoundError

func NewNotFoundError(resource string) *NotFoundError

NewNotFoundError creates a new not found error

func NewNotFoundErrorWithDetails

func NewNotFoundErrorWithDetails(resource, message string) *NotFoundError

NewNotFoundErrorWithDetails creates a not found error with custom message

type RateLimiterConfig

type RateLimiterConfig struct {
	RequestsPerMinute int
	BurstSize         int
	TrustProxy        bool // When true, use X-Forwarded-For/X-Real-IP headers for client IP
}

RateLimitMiddleware is a placeholder for rate limiting middleware In production, this would use a proper rate limiter (e.g., token bucket, Redis)

type Route

type Route struct {
	Method      HTTPMethod
	Path        string
	Handler     RouteHandler
	Middlewares []Middleware
}

Route represents a parsed GLYPH route definition

type RouteHandler

type RouteHandler func(ctx *Context) error

RouteHandler is a function that handles a matched route

type RouteNode

type RouteNode struct {
	// contains filtered or unexported fields
}

RouteNode represents a node in the route tree

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router manages route registration and matching

func NewRouter

func NewRouter() *Router

NewRouter creates a new router instance

func (*Router) GetAllRoutes

func (r *Router) GetAllRoutes() map[HTTPMethod][]*Route

GetAllRoutes returns all registered routes

func (*Router) GetRoutes

func (r *Router) GetRoutes(method HTTPMethod) []*Route

GetRoutes returns all registered routes for a method

func (*Router) Match

func (r *Router) Match(method HTTPMethod, path string) (*Route, map[string]string, error)

Match finds a matching route for the given method and path

func (*Router) RegisterRoute

func (r *Router) RegisterRoute(route *Route) error

RegisterRoute adds a route to the router

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server represents the HTTP server

Example

ExampleServer demonstrates basic server usage

package main

import (
	"fmt"
	"net/http/httptest"

	"github.com/glyphlang/glyph/pkg/server"
)

// exampleMockInterpreter implements server.Interpreter for examples
type exampleMockInterpreter struct {
	Response interface{}
}

func (m *exampleMockInterpreter) Execute(route *server.Route, ctx *server.Context) (interface{}, error) {
	if m.Response != nil {
		return m.Response, nil
	}

	query := make(map[string]interface{})
	for k, v := range ctx.QueryParams {
		if len(v) == 1 {
			query[k] = v[0]
		} else {
			query[k] = v
		}
	}
	response := map[string]interface{}{
		"message":    "Mock response",
		"path":       ctx.Request.URL.Path,
		"method":     ctx.Request.Method,
		"pathParams": ctx.PathParams,
		"query":      query,
	}
	if ctx.Body != nil && len(ctx.Body) > 0 {
		response["body"] = ctx.Body
	}
	return response, nil
}

func main() {
	// Create a new server with mock interpreter
	srv := server.NewServer(
		server.WithInterpreter(&exampleMockInterpreter{
			Response: map[string]interface{}{
				"message": "Hello, World!",
			},
		}),
	)

	// Register a simple route
	srv.RegisterRoute(&server.Route{
		Method: server.GET,
		Path:   "/hello",
	})

	// Make a test request
	req := httptest.NewRequest("GET", "/hello", nil)
	w := httptest.NewRecorder()

	srv.GetHandler().ServeHTTP(w, req)

	fmt.Println(w.Code)
}
Output:

200
Example (CustomHandler)

ExampleServer_customHandler demonstrates custom request handlers

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http/httptest"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	srv := server.NewServer()

	// Register route with custom handler
	srv.RegisterRoute(&server.Route{
		Method: server.POST,
		Path:   "/api/users",
		Handler: func(ctx *server.Context) error {
			// Access request body
			name := ctx.Body["name"].(string)

			// Send custom response
			return server.SendJSON(ctx, 201, map[string]interface{}{
				"message": fmt.Sprintf("User %s created", name),
				"id":      "new-id-123",
			})
		},
	})

	// Make request
	body, _ := json.Marshal(map[string]string{"name": "John"})
	req := httptest.NewRequest("POST", "/api/users", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()

	srv.GetHandler().ServeHTTP(w, req)

	fmt.Println(w.Code)
}
Output:

201
Example (Middleware)

ExampleServer_middleware demonstrates middleware usage

package main

import (
	"fmt"
	"net/http/httptest"

	"github.com/glyphlang/glyph/pkg/server"
)

func main() {
	// Create middleware that adds a header
	headerMiddleware := func(next server.RouteHandler) server.RouteHandler {
		return func(ctx *server.Context) error {
			ctx.ResponseWriter.Header().Set("X-Custom", "value")
			return next(ctx)
		}
	}

	srv := server.NewServer()

	// Register route with middleware
	srv.RegisterRoute(&server.Route{
		Method:      server.GET,
		Path:        "/test",
		Middlewares: []server.Middleware{headerMiddleware},
		Handler: func(ctx *server.Context) error {
			return server.SendJSON(ctx, 200, map[string]string{"status": "ok"})
		},
	})

	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	srv.GetHandler().ServeHTTP(w, req)

	fmt.Println(w.Header().Get("X-Custom"))
}
Output:

value
Example (PathParams)

ExampleServer_pathParams demonstrates path parameter extraction

package main

import (
	"encoding/json"
	"fmt"
	"net/http/httptest"

	"github.com/glyphlang/glyph/pkg/server"
)

// exampleMockInterpreter implements server.Interpreter for examples
type exampleMockInterpreter struct {
	Response interface{}
}

func (m *exampleMockInterpreter) Execute(route *server.Route, ctx *server.Context) (interface{}, error) {
	if m.Response != nil {
		return m.Response, nil
	}

	query := make(map[string]interface{})
	for k, v := range ctx.QueryParams {
		if len(v) == 1 {
			query[k] = v[0]
		} else {
			query[k] = v
		}
	}
	response := map[string]interface{}{
		"message":    "Mock response",
		"path":       ctx.Request.URL.Path,
		"method":     ctx.Request.Method,
		"pathParams": ctx.PathParams,
		"query":      query,
	}
	if ctx.Body != nil && len(ctx.Body) > 0 {
		response["body"] = ctx.Body
	}
	return response, nil
}

func main() {
	srv := server.NewServer(
		server.WithInterpreter(&exampleMockInterpreter{}),
	)

	// Register route with path parameters
	srv.RegisterRoute(&server.Route{
		Method: server.GET,
		Path:   "/users/:id",
	})

	// Make request
	req := httptest.NewRequest("GET", "/users/123", nil)
	w := httptest.NewRecorder()

	srv.GetHandler().ServeHTTP(w, req)

	var response map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &response)

	pathParams := response["pathParams"].(map[string]interface{})
	fmt.Println(pathParams["id"])
}
Output:

123

func NewServer

func NewServer(options ...ServerOption) *Server

NewServer creates a new HTTP server instance

func (*Server) GetHandler

func (s *Server) GetHandler() *Handler

GetHandler returns the server's handler

func (*Server) GetRouter

func (s *Server) GetRouter() *Router

GetRouter returns the server's router

func (*Server) GetWebSocketServer

func (s *Server) GetWebSocketServer() *ws.Server

GetWebSocketServer returns the WebSocket server

func (*Server) RegisterHealthRoutes

func (s *Server) RegisterHealthRoutes(hm *HealthManager) error

RegisterHealthRoutes registers all health check routes with the server

func (*Server) RegisterRoute

func (s *Server) RegisterRoute(route *Route) error

RegisterRoute registers a single route

func (*Server) RegisterRoutes

func (s *Server) RegisterRoutes(routes []*Route) error

RegisterRoutes registers multiple routes

func (*Server) RegisterWebSocketRoute

func (s *Server) RegisterWebSocketRoute(path string, handler http.HandlerFunc) error

RegisterWebSocketRoute registers a WebSocket route

func (*Server) Start

func (s *Server) Start(addr string) error

Start starts the HTTP server

func (*Server) Stop

func (s *Server) Stop(ctx context.Context) error

Stop gracefully stops the HTTP server

type ServerOption

type ServerOption func(*Server)

ServerOption is a functional option for configuring the server

func WithAddr

func WithAddr(addr string) ServerOption

WithAddr sets the server address

func WithInterpreter

func WithInterpreter(interpreter Interpreter) ServerOption

WithInterpreter sets the interpreter for the server

func WithMiddleware

func WithMiddleware(middleware Middleware) ServerOption

WithMiddleware adds a global middleware to the server

type StaticHealthChecker

type StaticHealthChecker struct {
	// contains filtered or unexported fields
}

StaticHealthChecker always returns a fixed status (useful for testing)

func NewStaticHealthChecker

func NewStaticHealthChecker(name string, status HealthStatus) *StaticHealthChecker

NewStaticHealthChecker creates a health checker that always returns the same status

func (*StaticHealthChecker) Check

Check returns the static status

func (*StaticHealthChecker) Name

func (s *StaticHealthChecker) Name() string

Name returns the checker name

type UnauthorizedError

type UnauthorizedError struct {
	*BaseError
	Reason string
}

UnauthorizedError represents a 401 Unauthorized error

func NewUnauthorizedError

func NewUnauthorizedError(reason string) *UnauthorizedError

NewUnauthorizedError creates a new unauthorized error

func NewUnauthorizedErrorWithMessage

func NewUnauthorizedErrorWithMessage(message, reason string) *UnauthorizedError

NewUnauthorizedErrorWithMessage creates an unauthorized error with custom message

func (*UnauthorizedError) ToResponse

func (e *UnauthorizedError) ToResponse() *ErrorResponse

ToResponse overrides to include reason in details

type ValidationError

type ValidationError struct {
	*BaseError
	Field string
}

ValidationError represents a 400 Bad Request error

func NewValidationError

func NewValidationError(field, message string) *ValidationError

NewValidationError creates a new validation error

func NewValidationErrorWithDetails

func NewValidationErrorWithDetails(field, message, details string) *ValidationError

NewValidationErrorWithDetails creates a validation error with additional details

func (*ValidationError) ToResponse

func (e *ValidationError) ToResponse() *ErrorResponse

ToResponse overrides the base method to include field information

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL