Documentation
¶
Overview ¶
Package server provides the NETCONF server-side dispatch library.
The central type is Server. Implementers register per-operation handlers via RegisterHandler, then call Serve to run the dispatch loop over an established Session. The loop reads RPCs, routes each to the matching handler by operation XML local name, and sends the reply.
Built-in behaviour:
- <close-session> is intercepted before handler lookup — Serve sends <ok/> and returns nil so the caller's goroutine can clean up.
- Unregistered operations receive an operation-not-supported rpc-error reply; the error message names the operation so callers can log it verbatim.
- Handler errors are converted to well-formed <rpc-error> replies: RPCError values are marshalled directly; other errors become a generic operation-failed rpc-error whose message field carries err.Error().
Observability ¶
Serve returns a descriptive error when the transport fails or context is cancelled; the error wraps the underlying transport/context error so callers can log it verbatim or use errors.Is/As. Operation-not-supported replies include the operation name in the error-message field, making them identifiable in captured traffic. Handler dispatch errors include the operation name and message-id in the error-message string so production logs can correlate failures.
Redaction note: RPC body may contain device configuration. Do not log at INFO or higher in production; log at DEBUG only.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SendNotification ¶
func SendNotification(sess *netconf.Session, n *netconf.Notification) error
SendNotification marshals n and sends it to the client via sess.
This function is NOT safe for concurrent use with other sends on the same session. If notifications are sent from a goroutine other than the Serve loop goroutine, the caller must serialize access to sess (e.g. via a sync.Mutex). The Serve loop itself sends replies from a single goroutine, so calling SendNotification from a separate goroutine requires external synchronization.
Error messages include the "server: SendNotification:" prefix for log correlation.
Types ¶
type Handler ¶
type Handler interface {
Handle(ctx context.Context, sess *netconf.Session, rpc *netconf.RPC) ([]byte, error)
}
Handler handles a single NETCONF operation.
Implementations receive the raw Session (for session-level metadata) and the parsed RPC (for the message-id and raw body). They are responsible for unmarshalling the body themselves — this keeps the server package ignorant of the full set of operation types.
Return semantics:
- (nil, nil) → the reply will be <rpc-reply><ok/></rpc-reply>
- (body, nil) → body is embedded verbatim as the inner XML of <rpc-reply>
- (_, RPCError) → the RPCError is marshalled and embedded in <rpc-reply>
- (_, other) → a generic operation-failed rpc-error is constructed from err.Error() and embedded in <rpc-reply>
type HandlerFunc ¶
HandlerFunc is a function adapter that implements Handler, analogous to http.HandlerFunc.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server dispatches incoming NETCONF RPCs to registered handlers.
func NewServer ¶
func NewServer() *Server
NewServer returns a Server with an empty handler registry.
func (*Server) RegisterHandler ¶
RegisterHandler registers h as the handler for operations whose XML local name is opName (e.g. "get-config", "edit-config"). A second registration for the same opName replaces the first.
func (*Server) Serve ¶
Serve runs the RPC dispatch loop over sess until one of:
- the transport returns an error (transport failure or context cancellation)
- a <close-session> RPC is received (Serve sends <ok/> and returns nil)
Serve uses a streaming decode path: each message is parsed in a single pass without materialising an intermediate []byte. Handlers that implement StreamHandler receive the decoder positioned at the operation start element and decode the body directly (zero body allocation). Plain Handler implementations continue to work unchanged — Serve materialises rpc.Body for them.
Context cancellation: cancelling ctx causes Serve to close the session transport to unblock any in-flight receive and return promptly.
Panic handling: if a Handler or StreamHandler panics, the panic propagates to the caller of Serve. Use a recover wrapper around Serve if your handlers may panic.
For each well-formed RPC, Serve:
- Extracts the message-id and operation name from the streaming decoder.
- Intercepts <close-session>: sends <ok/>, returns nil.
- Looks up the handler by operation name.
- If none found: sends an operation-not-supported rpc-error reply.
- Calls the handler (StreamHandler or Handler) and converts its result to a reply (see Handler docs).
- Sends the reply via sess.Send.
Serve is single-threaded: one handler runs at a time per session.
type StreamHandler ¶
type StreamHandler interface {
HandleStream(ctx context.Context, sess *netconf.Session, rpc *netconf.RPC, dec *xml.Decoder, opStart xml.StartElement) ([]byte, error)
}
StreamHandler is an optional interface that handlers may implement to receive the XML decoder positioned at the operation start element. Implementing StreamHandler avoids materialising rpc.Body as []byte — the handler decodes the operation body directly from the decoder stream.
HandleStream is called with dec positioned such that the next DecodeElement call with opStart will consume the complete operation element. The handler MUST call dec.DecodeElement (or consume the element completely) before returning. The rpc parameter carries the message-id; its Body field is nil.
Return semantics are identical to Handler.Handle.