mirror of https://github.com/tidwall/tile38.git
187 lines
4.0 KiB
Go
187 lines
4.0 KiB
Go
package controller
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// EndpointProtocol is the type of protocol that the endpoint represents.
|
|
type EndpointProtocol string
|
|
|
|
const (
|
|
HTTP = EndpointProtocol("http") // HTTP
|
|
Disque = EndpointProtocol("disque") // Disque
|
|
)
|
|
|
|
type EndpointManager struct {
|
|
mu sync.RWMutex // this is intentionally exposed
|
|
Endpoints map[string]*Endpoint
|
|
}
|
|
|
|
func NewEndpointCollection() *EndpointManager {
|
|
return &EndpointManager{
|
|
Endpoints: make(map[string]*Endpoint),
|
|
}
|
|
}
|
|
|
|
// Get finds an endpoint based on its url. If the enpoint does not
|
|
// exist a new only is created.
|
|
func (epc *EndpointManager) Validate(url string) error {
|
|
_, err := parseEndpoint(url)
|
|
return err
|
|
/*
|
|
pendpoint := epc.Endpoints[url]
|
|
if pendpoint == nil {
|
|
pendpoint = endpoint
|
|
epc.Endpoints[url] = pendpoint
|
|
}
|
|
return pendpoint, nil
|
|
*/
|
|
}
|
|
|
|
// We use a retain/relase on endoints.
|
|
// The calls are directed to the collection instead of
|
|
// endpoint itself to avoid having a circular reference to
|
|
// the collection.
|
|
func (epc *EndpointManager) Open(endpoint string) {
|
|
epc.mu.Lock()
|
|
defer epc.mu.Unlock()
|
|
/*
|
|
ep.referenceCount++
|
|
if ep.referenceCount == 1 {
|
|
ep.Open()
|
|
}
|
|
*/
|
|
}
|
|
|
|
func (epc *EndpointManager) Close(endpoint string) {
|
|
epc.mu.Lock()
|
|
defer epc.mu.Unlock()
|
|
/*
|
|
ep.referenceCount--
|
|
if ep.referenceCount < 0 {
|
|
panic("reference count below zero")
|
|
}
|
|
if ep.referenceCount == 0 {
|
|
ep.Close()
|
|
delete(epc.Endpoints, ep.Original)
|
|
}
|
|
*/
|
|
}
|
|
func (epc *EndpointManager) Send(endpoint, val string) error {
|
|
epc.mu.Lock()
|
|
defer epc.mu.Unlock()
|
|
return errors.New("unavailable")
|
|
return nil
|
|
}
|
|
|
|
// Endpoint represents an endpoint.
|
|
type Endpoint struct {
|
|
Protocol EndpointProtocol
|
|
Original string
|
|
Disque struct {
|
|
Host string
|
|
Port int
|
|
QueueName string
|
|
Options struct {
|
|
Replicate int
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
func (ep *Endpoint) Open() {
|
|
ep.mu.Lock()
|
|
defer ep.mu.Unlock()
|
|
println("open " + ep.Original)
|
|
// Even though open is called we should wait until the a messages
|
|
// is sent before establishing a network connection.
|
|
}
|
|
|
|
func (ep *Endpoint) Close() {
|
|
ep.mu.Lock()
|
|
defer ep.mu.Unlock()
|
|
println("close " + ep.Original)
|
|
// Make sure to forece close the network connection here.
|
|
}
|
|
|
|
func (ep *Endpoint) Send() error {
|
|
return nil
|
|
}
|
|
*/
|
|
func parseEndpoint(s string) (Endpoint, error) {
|
|
var endpoint Endpoint
|
|
endpoint.Original = s
|
|
switch {
|
|
default:
|
|
return endpoint, errors.New("unknown scheme")
|
|
case strings.HasPrefix(s, "http:"):
|
|
endpoint.Protocol = HTTP
|
|
case strings.HasPrefix(s, "https:"):
|
|
endpoint.Protocol = HTTP
|
|
case strings.HasPrefix(s, "disque:"):
|
|
endpoint.Protocol = Disque
|
|
}
|
|
s = s[strings.Index(s, ":")+1:]
|
|
if !strings.HasPrefix(s, "//") {
|
|
return endpoint, errors.New("missing the two slashes")
|
|
}
|
|
sqp := strings.Split(s[2:], "?")
|
|
sp := strings.Split(sqp[0], "/")
|
|
s = sp[0]
|
|
if s == "" {
|
|
return endpoint, errors.New("missing host")
|
|
}
|
|
if endpoint.Protocol == Disque {
|
|
dp := strings.Split(s, ":")
|
|
switch len(dp) {
|
|
default:
|
|
return endpoint, errors.New("invalid disque url")
|
|
case 1:
|
|
endpoint.Disque.Host = dp[0]
|
|
endpoint.Disque.Port = 7711
|
|
case 2:
|
|
endpoint.Disque.Host = dp[0]
|
|
n, err := strconv.ParseUint(dp[1], 10, 16)
|
|
if err != nil {
|
|
return endpoint, errors.New("invalid disque url")
|
|
}
|
|
endpoint.Disque.Port = int(n)
|
|
}
|
|
if len(sp) > 1 {
|
|
var err error
|
|
endpoint.Disque.QueueName, err = url.QueryUnescape(sp[1])
|
|
if err != nil {
|
|
return endpoint, errors.New("invalid disque queue name")
|
|
}
|
|
}
|
|
if len(sqp) > 1 {
|
|
m, err := url.ParseQuery(sqp[1])
|
|
if err != nil {
|
|
return endpoint, errors.New("invalid disque url")
|
|
}
|
|
for key, val := range m {
|
|
if len(val) == 0 {
|
|
continue
|
|
}
|
|
switch key {
|
|
case "replicate":
|
|
n, err := strconv.ParseUint(val[0], 10, 8)
|
|
if err != nil {
|
|
return endpoint, errors.New("invalid disque replicate value")
|
|
}
|
|
endpoint.Disque.Options.Replicate = int(n)
|
|
}
|
|
}
|
|
}
|
|
if endpoint.Disque.QueueName == "" {
|
|
return endpoint, errors.New("missing disque queue name")
|
|
}
|
|
|
|
}
|
|
return endpoint, nil
|
|
}
|