Files
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the
Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in
NOTICE, alongside LICENSE. The header lint accepts any copyright line above the
SPDX line, so it no longer demands an upstream one.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 17:50:35 -07:00

101 lines
2.7 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package nosql
import (
"net/url"
"strconv"
"strings"
)
// The file contains common KV connection functions
// ToKVURI converts old style connections to a KV URI
//
// A KV URI matches the pattern:
//
// kv://[username:password@]host[:port][/database][?[option=value]*]
// kvs://[username:password@]host[:port][/database][?[option=value]*]
// kv+socket://[username:password@]path[/database][?[option=value]*]
// kv+sentinel://[password@]host1 [: port1][, host2 [:port2]][, hostN [:portN]][/ database][?[option=value]*]
// kv+cluster://[password@]host1 [: port1][, host2 [:port2]][, hostN [:portN]][/ database][?[option=value]*]
//
// We have previously used a URI like:
// addrs=127.0.0.1:6379 db=0
// network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
//
// We need to convert this old style to the new style
func ToKVURI(connection string) *url.URL {
uri, err := url.Parse(connection)
if err == nil && strings.HasPrefix(uri.Scheme, "kv") {
// OK we're going to assume that this is a reasonable KV URI
return uri
}
// Let's set a nice default
uri, _ = url.Parse("kv://127.0.0.1:6379/0")
network := "tcp"
query := uri.Query()
// OK so there are two types: Space delimited and Comma delimited
// Let's assume that we have a space delimited string - as this is the most common
fields := strings.Fields(connection)
if len(fields) == 1 {
// It's a comma delimited string, then...
fields = strings.Split(connection, ",")
}
for _, f := range fields {
items := strings.SplitN(f, "=", 2)
if len(items) < 2 {
continue
}
switch strings.ToLower(items[0]) {
case "network":
if items[1] == "unix" {
uri.Scheme = "kv+socket"
}
network = items[1]
case "addrs":
uri.Host = items[1]
// now we need to handle the clustering
if strings.Contains(items[1], ",") && network == "tcp" {
uri.Scheme = "kv+cluster"
}
case "addr":
uri.Host = items[1]
case "password":
uri.User = url.UserPassword(uri.User.Username(), items[1])
case "username":
password, set := uri.User.Password()
if !set {
uri.User = url.User(items[1])
} else {
uri.User = url.UserPassword(items[1], password)
}
case "db":
uri.Path = "/" + items[1]
case "idle_timeout":
_, err := strconv.Atoi(items[1])
if err == nil {
query.Add("idle_timeout", items[1]+"s")
} else {
query.Add("idle_timeout", items[1])
}
default:
// Other options become query params
query.Add(items[0], items[1])
}
}
// Finally we need to fix up the Host if we have a unix port
if uri.Scheme == "kv+socket" {
query.Set("db", uri.Path)
uri.Path = uri.Host
uri.Host = ""
}
uri.RawQuery = query.Encode()
return uri
}