blob: 83ff553849810916ad3fd8c88343abf8349268b6 [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package streamclient
import (
// ClientFactory is a generator function that is invoked by the Registry when a
// new Client is requested for its protocol.
type ClientFactory func(string, types.StreamName) (Client, error)
// Registry maps protocol prefix strings to their Client generator functions.
// This allows multiple Butler stream protocols (e.g., "unix:", "net.pipe:",
// etc.) to be parsed from string.
type Registry struct {
// lock protects the fields in Registry.
lock sync.Mutex
// protocols is the set of registered protocols. Each client should register
// via registerProtocol in its init() method.
protocols map[string]ClientFactory
// Register registers a new protocol and its ClientFactory.
// This can be invoked by calling NewClient with a path spec referencing that
// protocol.
func (r *Registry) Register(name string, f ClientFactory) {
defer r.lock.Unlock()
if _, ok := r.protocols[name]; ok {
panic(fmt.Errorf("streamclient: protocol already registered for [%s]", name))
if r.protocols == nil {
r.protocols = make(map[string]ClientFactory)
r.protocols[name] = f
// NewClient invokes the protocol ClientFactory generator for the
// supplied protocol/address string, returning the generated Client.
func (r *Registry) NewClient(path string, namespace types.StreamName) (Client, error) {
parts := strings.SplitN(path, ":", 2)
value := ""
if len(parts) == 2 {
value = parts[1]
defer r.lock.Unlock()
if f, ok := r.protocols[parts[0]]; ok {
return f(value, namespace)
return nil, fmt.Errorf("streamclient: no protocol registered for [%s]", parts[0])
// defaultRegistry is the default protocol registry.
var (
defaultRegistry *Registry
defaultRegistryInitOnce sync.Once
// GetDefaultRegistry returns the default client registry instance.
// Initializes the registry on first invocation.
func GetDefaultRegistry() *Registry {
defaultRegistryInitOnce.Do(func() {
defaultRegistry = &Registry{}
return defaultRegistry
// RegisterDefaultProtocols registers the default set of protocols with a
// Registry.
func RegisterDefaultProtocols(r *Registry) {
// Register common protocols.
r.Register("tcp4", tcpProtocolClientFactory("tcp4"))
r.Register("tcp6", tcpProtocolClientFactory("tcp6"))