server

package
v0.0.5 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package server defines the server lifecycle abstraction and concrete HTTP (gin) and gRPC servers, wired to service registration for graceful deregister-before-stop.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HTTPRouteNames

func HTTPRouteNames() []string

HTTPRouteNames returns the sorted names of all registered route modules.

func NewGinEngine

func NewGinEngine(opts *options.ServerOptions) (*gin.Engine, error)

NewGinEngine returns a gin engine with the unified middleware chain already applied, ready for the caller to register routes with the full gin API. The returned engine is accepted by MeshServer; a raw gin.New() is rejected there so middleware can never be silently bypassed.

func RegisterHTTPRoute

func RegisterHTTPRoute(name string, r HTTPRoute)

RegisterHTTPRoute registers a route module under name. Business packages call this from init().

func Serve

func Serve(ctx context.Context, srv Server) error

Serve starts srv and blocks until ctx is canceled or srv exits. On cancellation it gracefully stops srv within a timeout budget.

Types

type ErrorRenderer

type ErrorRenderer func(c *gin.Context, err error)

ErrorRenderer writes a failed call. It has the same signature as codec.RenderError so the framework default can be named directly.

type GRPCOption

type GRPCOption func(*GRPCServer)

GRPCOption configures a GRPCServer.

func WithGRPCRegistrar

func WithGRPCRegistrar(r registry.Registrar, inst *registry.ServiceInstance) GRPCOption

WithGRPCRegistrar attaches a registrar and the instance to register on Start and deregister on Stop.

type GRPCServer

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

GRPCServer is a grpc-go server with optional service registration.

func NewGRPCServer

func NewGRPCServer(addr string, register func(grpc.ServiceRegistrar), opts ...grpc.ServerOption) *GRPCServer

NewGRPCServer creates a gRPC server. register is invoked with the server on Start to register business services.

func (*GRPCServer) Start

func (s *GRPCServer) Start(ctx context.Context) error

func (*GRPCServer) Stop

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

func (*GRPCServer) WithRegistrar

func (s *GRPCServer) WithRegistrar(r registry.Registrar, inst *registry.ServiceInstance) *GRPCServer

WithRegistrar is a fluent variant of WithGRPCRegistrar.

type HTTPOption

type HTTPOption func(*HTTPServer)

HTTPOption configures an HTTPServer.

func WithRegistrar

func WithRegistrar(r registry.Registrar, inst *registry.ServiceInstance) HTTPOption

WithRegistrar attaches a registrar and the instance to register on Start and deregister on Stop.

type HTTPRoute

type HTTPRoute interface {
	// Name is a stable identifier for the route module.
	Name() string
	// RouteGroups returns the module's declarative route groups.
	RouteGroups() []*RouteGroup
}

HTTPRoute is a pluggable HTTP route module: it self-describes its name and declares its routes as RouteGroups (declarative data rather than imperative engine mutation). Business packages call RegisterHTTPRoute from init(); the composition root discovers them via AllHTTPRoutes(), so adding a route module no longer requires touching the composition root (open/closed principle), mirroring registry.Backend and middleware.Register. A *RouteGroup satisfies this interface directly, so the usual registration is a single group.

func AllHTTPRoutes

func AllHTTPRoutes() []HTTPRoute

AllHTTPRoutes returns all registered route modules in sorted name order, for the composition root to assemble.

func GetHTTPRoute

func GetHTTPRoute(name string) (HTTPRoute, error)

GetHTTPRoute returns the route module registered under name.

type HTTPServer

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

HTTPServer is a gin-backed HTTP server with optional service registration.

func NewHTTPServer

func NewHTTPServer(addr string, handler http.Handler, opts ...HTTPOption) *HTTPServer

NewHTTPServer creates an HTTP server serving the given handler.

func (*HTTPServer) Start

func (s *HTTPServer) Start(ctx context.Context) error

func (*HTTPServer) Stop

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

type MeshServer

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

MeshServer is the facade for a onexmesh service instance. It aggregates the HTTP and gRPC servers, the unified middleware chain and the registry into a single runnable unit, and owns the runtime initialization (slog + OpenTelemetry) for the options it is built from. Configure it with the WithXxx options, then run it via Run; a concurrent or out-of-band shutdown is available via GracefulStop.

func NewMeshServer

func NewMeshServer(opts *options.ServerOptions, mopts ...MeshServerOption) *MeshServer

NewMeshServer builds a MeshServer from the given options and configuration options. It does not initialize or serve anything; assembly and runtime initialization happen on Run.

func (*MeshServer) GracefulStop

func (s *MeshServer) GracefulStop(ctx context.Context) error

GracefulStop gracefully stops the running service group by canceling its run context and waiting for Run's cleanup (including deregistration) to finish, honoring ctx timeout. It is safe to call before Run (no-op) or concurrently with Run.

func (*MeshServer) Options

func (s *MeshServer) Options() *options.ServerOptions

Options returns the server options this instance was built from.

func (*MeshServer) Run

func (s *MeshServer) Run(ctx context.Context) error

Run initializes the runtime capabilities (slog + OpenTelemetry), assembles the HTTP/gRPC service group, starts it and blocks until ctx is canceled or a server fails, then gracefully stops and deregisters the servers before returning. It mirrors onexstack's GenericAPIServer.Run contract, so its method value satisfies onexstack's app.RunFunc.

type MeshServerOption

type MeshServerOption func(*MeshServer)

MeshServerOption configures a MeshServer.

func WithGRPCRegister

func WithGRPCRegister(fn func(grpc.ServiceRegistrar)) MeshServerOption

WithGRPCRegister supplies a raw gRPC service registration callback (the generated RegisterXxxServer). Prefer WithService, which carries this for you.

func WithGinEngine

func WithGinEngine(engine *gin.Engine) MeshServerOption

WithGinEngine supplies an externally-built gin engine (via NewGinEngine) so the caller can register native gin routes directly. Any WithService services are then applied on top of the engine's root router group.

func WithRoute

func WithRoute(routes ...HTTPRoute) MeshServerOption

WithRoute appends native-gin HTTP route plugins (HTTPRoute). Useful for path/query/streaming endpoints with no proto definition.

func WithService

func WithService(svcs ...Service) MeshServerOption

WithService appends proto-first services. Each Service declares both the gRPC registration (Service.Register) and the proto-first HTTP routes (Service.Methods), so one implementation serves both protocols.

type Method

type Method struct {
	// Name is the gRPC method name, e.g. "SayHello".
	Name string
	// Method is the HTTP verb, e.g. "GET".
	Method string
	// Path is the HTTP path template, e.g. "/helloworld/{name}".
	Path string
	// Body is the body binding: "*" for the whole message, "" for none.
	Body string
	// Status is the HTTP status a successful call returns. Zero means 200, so a
	// Method built by NewMethod needs nothing set and existing generated code is
	// unaffected.
	//
	// It exists because the status is not derivable from the verb. A contract that
	// returns 201 for resource creation and 204 for deletion cannot be served by a
	// fixed 200: the two are told apart by what the operation *means*, not by
	// whether it is a POST. Set it from the contract, in the composition root,
	// where the contract is already being read.
	Status int

	// Handler is the unified business logic, shared with gRPC.
	Handler middleware.Handler
	// Render renders a successful (non-nil) response. Nil means codec.Render,
	// the framework's generic JSON/protobuf codec.
	//
	// It exists because the wire shape of a response is a decision the contract
	// owns, not the transport. The generic codec marshals the generated Go struct
	// with encoding/json, which drops zero-valued fields and emits a
	// google.protobuf.Timestamp as {"seconds":...}: fine for a debug endpoint,
	// wrong for a contract that requires zero values to appear and timestamps as
	// RFC3339. Leaving it nil keeps the current behaviour, so existing generated
	// code is unaffected.
	Render Renderer
	// RenderError renders a failed call. Nil means codec.RenderError, the
	// framework's errorsx envelope ({"code":<http status>,"reason":...}).
	//
	// It is a separate hook because the two shapes are separately owned: a
	// contract that documents {"code":"Domain.Specific"} — a string reason, no
	// numeric status in the body — cannot be served by the errorsx envelope, and
	// answering the wrong one is invisible to a client that only checks the HTTP
	// status.
	RenderError ErrorRenderer
	// Validate applies the request's default values and runs its validation. It
	// is called after the request is bound from path/query/body and before the
	// handler sees it, which is the only point where both hold: before binding
	// there is no message to default, and after the handler the request may
	// already have been written.
	//
	// It exists because binding and judging are different jobs with different
	// owners. The framework knows how to turn a request into a message; whether
	// the message means anything — a size a caller may ask for, a field the
	// contract requires, a name that is already taken — is the contract's
	// business, and it is the same judgement for every transport. Leaving it nil
	// keeps the current behaviour, so existing generated code is unaffected.
	Validate ValidateHook
	// contains filtered or unexported fields
}

Method declares one proto RPC method's HTTP surface. It is a data descriptor: Path carries the grpc-gateway-style "{field}" template, Body the body binding ("" or "*"), newReq the proto-first request factory and Handler the business logic. The composition root turns it into a gin route via httpHandler.

func NewMethod

func NewMethod[Req, Resp proto.Message](
	name, method, path, body string,
	h func(context.Context, Req) (Resp, error),
) Method

NewMethod builds a proto-first Method from a strongly-typed business function h. The Req/Resp type parameters are constrained to proto.Message, so request and response types are enforced to be protobuf at compile time, and the same h backs both the gRPC method (via the generated RegisterXxxServer) and the HTTP route (via httpHandler).

NewMethod derives the request factory from Req, so a caller never passes one: a constructor closure would restate what h's signature already says, and the only shape it can usefully take is "allocate a zero Req". Inference runs from h alone, so the generated call is

server.NewMethod("SayHello", "GET", "/helloworld/{name}", "", srv.SayHello)

func (Method) Apply

func (m Method) Apply(g *gin.RouterGroup)

Apply registers the Method's HTTP route onto g.

func (Method) StatusOrDefault

func (m Method) StatusOrDefault() int

StatusOrDefault returns the status a successful call returns, defaulting to 200 for a Method that declares none.

type Renderer

type Renderer func(c *gin.Context, status int, v any)

Renderer writes a successful response. It has the same signature as codec.Render so the framework default can be named directly.

type Route

type Route struct {
	// Method is the HTTP verb, e.g. http.MethodGet.
	Method string
	// Path is the gin route template, e.g. "/users/:id".
	Path string
	// GinHandler is the native gin handler for path/query/streaming cases.
	GinHandler gin.HandlerFunc
}

Route declares a single native-gin HTTP endpoint as data rather than behavior. RouteGroup's fluent verbs build these for you; construct one directly only for the rare case of a pre-built route list. Proto-first body-based endpoints belong in Service.Method instead.

func NewGinRoute

func NewGinRoute(method, path string, h gin.HandlerFunc) Route

NewGinRoute builds a native gin Route for the path/query/streaming cases.

type RouteGroup

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

RouteGroup is a fluent, declarative HTTP route group. It mirrors gin's RouterGroup chaining — Group / Use / GET / POST / ... — but instead of mutating a gin engine it records routes as data, so the composition root assembles the engine in one place. A RouteGroup satisfies HTTPRoute, so it can be registered directly via RegisterHTTPRoute.

func NewGroup

func NewGroup(prefix string, mws ...middleware.Middleware) *RouteGroup

NewGroup returns a route group under the given path prefix and group-level middleware. An empty prefix registers routes at the parent's root.

func (*RouteGroup) Apply

func (g *RouteGroup) Apply(parent *gin.RouterGroup)

Apply registers the group, its routes and its nested children onto parent.

func (*RouteGroup) DELETE

func (g *RouteGroup) DELETE(path string, h gin.HandlerFunc) *RouteGroup

DELETE registers a native gin DELETE route and returns g.

func (*RouteGroup) GET

func (g *RouteGroup) GET(path string, h gin.HandlerFunc) *RouteGroup

GET registers a native gin GET route and returns g.

func (*RouteGroup) Group

func (g *RouteGroup) Group(prefix string, mws ...middleware.Middleware) *RouteGroup

Group creates a nested child group under the given relative prefix and returns it, mirroring gin's RouterGroup.Group.

func (*RouteGroup) HEAD

func (g *RouteGroup) HEAD(path string, h gin.HandlerFunc) *RouteGroup

HEAD registers a native gin HEAD route and returns g.

func (*RouteGroup) Handle

func (g *RouteGroup) Handle(method, path string, h gin.HandlerFunc) *RouteGroup

Handle registers a native gin route for an arbitrary method and returns g.

func (*RouteGroup) Name

func (g *RouteGroup) Name() string

Name returns the group's self-descriptive name, set via SetName.

func (*RouteGroup) OPTIONS

func (g *RouteGroup) OPTIONS(path string, h gin.HandlerFunc) *RouteGroup

OPTIONS registers a native gin OPTIONS route and returns g.

func (*RouteGroup) PATCH

func (g *RouteGroup) PATCH(path string, h gin.HandlerFunc) *RouteGroup

PATCH registers a native gin PATCH route and returns g.

func (*RouteGroup) POST

func (g *RouteGroup) POST(path string, h gin.HandlerFunc) *RouteGroup

POST registers a native gin POST route and returns g.

func (*RouteGroup) PUT

func (g *RouteGroup) PUT(path string, h gin.HandlerFunc) *RouteGroup

PUT registers a native gin PUT route and returns g.

func (*RouteGroup) RouteGroups

func (g *RouteGroup) RouteGroups() []*RouteGroup

RouteGroups returns g itself, satisfying the HTTPRoute interface.

func (*RouteGroup) SetName

func (g *RouteGroup) SetName(name string) *RouteGroup

SetName sets the group's self-descriptive name and returns g for chaining.

func (*RouteGroup) Use

func (g *RouteGroup) Use(mws ...middleware.Middleware) *RouteGroup

Use appends group-level middleware and returns g for chaining.

type Server

type Server interface {
	// Start runs the server and blocks until it stops or errors. A graceful
	// stop returns nil.
	Start(ctx context.Context) error
	// Stop gracefully shuts the server down, honoring ctx timeout.
	Stop(ctx context.Context) error
}

Server is the unified lifecycle for all servers (HTTP/gRPC).

type Service

type Service struct {
	// Name is the service name, e.g. "helloworld.Greeter".
	Name string
	// Register registers the gRPC business service (the generated
	// RegisterXxxServer, or additional streaming services). Nil disables gRPC.
	Register func(grpc.ServiceRegistrar)
	// Methods declares the proto-first HTTP routes; each Method's Handler is the
	// same strongly-typed function the gRPC service exposes.
	Methods []Method
	// RouteGroups declares native-gin HTTP routes (path/query params, no proto).
	RouteGroups []*RouteGroup
}

Service declares a business service served over gRPC and/or HTTP. A single proto service produces one Service: its gRPC methods are registered via Register (the generated RegisterXxxServer plus any streaming services), and its HTTP routes are declared as proto-first Methods whose request/response types are enforced to be protobuf messages at compile time. RouteGroups is the native-gin escape hatch for pure-HTTP path/query cases with no proto definition. Protocol gating (grpc | http | both) happens in the composition root via opts.Mesh.Protocol.

func NewService

func NewService(name string, methods ...Method) Service

NewService builds a Service from a set of proto-first Methods.

type ServiceEntry

type ServiceEntry struct {
	Name   string
	Server Server
}

ServiceEntry couples a name with a Server for lifecycle management.

type ServiceGroup

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

ServiceGroup manages a set of servers: they start concurrently and stop in reverse registration order (later-started services stop first), following the go-zero ServiceGroup convention.

func NewServiceGroup

func NewServiceGroup() *ServiceGroup

NewServiceGroup creates an empty ServiceGroup.

func (*ServiceGroup) Add

func (g *ServiceGroup) Add(name string, s Server)

Add appends a server to the group.

func (*ServiceGroup) Start

func (g *ServiceGroup) Start(ctx context.Context) error

Start starts all servers concurrently and blocks until one fails or the context is canceled.

func (*ServiceGroup) Stop

func (g *ServiceGroup) Stop(ctx context.Context) error

Stop stops all servers in reverse registration order, serially. It attempts to stop every server and aggregates any errors, so one failing stop does not leak the remaining servers.

type ValidateHook added in v0.0.3

type ValidateHook func(ctx context.Context, req proto.Message) error

ValidateHook defaults and validates a bound request. An error is rendered with RenderError and the handler is not called.

Jump to

Keyboard shortcuts

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