Major refactoring.
+ Many lib functions have been split out into separate packages. + Adding cert/key generation tooling. + Add new time.Duration parser.
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
// 3. HTTP_PROXY
|
||||
//
|
||||
// Both uppercase and lowercase variable names are honored.
|
||||
package lib
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -468,8 +468,8 @@ func (s *socks5ContextDialer) DialContext(ctx context.Context, network, address
|
||||
|
||||
// tlsWrappingDialer performs a TLS handshake over an existing base dialer.
|
||||
type tlsWrappingDialer struct {
|
||||
base ContextDialer
|
||||
tcfg *tls.Config
|
||||
base ContextDialer
|
||||
tcfg *tls.Config
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
1
lib/duration/duration.go
Normal file
1
lib/duration/duration.go
Normal file
@@ -0,0 +1 @@
|
||||
package duration
|
||||
@@ -1,4 +1,4 @@
|
||||
package lib
|
||||
package fetch
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib/hosts"
|
||||
"git.wntrmute.dev/kyle/goutils/fileutil"
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
"git.wntrmute.dev/kyle/goutils/lib/dialer"
|
||||
)
|
||||
|
||||
// Note: Previously this package exposed a FetcherOpts type. It has been
|
||||
@@ -61,18 +63,18 @@ func ParseServer(host string) (*ServerFetcher, error) {
|
||||
}
|
||||
|
||||
func (sf *ServerFetcher) String() string {
|
||||
return fmt.Sprintf("tls://%s", net.JoinHostPort(sf.host, Itoa(sf.port, -1)))
|
||||
return fmt.Sprintf("tls://%s", net.JoinHostPort(sf.host, lib.Itoa(sf.port, -1)))
|
||||
}
|
||||
|
||||
func (sf *ServerFetcher) GetChain() ([]*x509.Certificate, error) {
|
||||
opts := DialerOpts{
|
||||
opts := dialer.DialerOpts{
|
||||
TLSConfig: &tls.Config{
|
||||
InsecureSkipVerify: sf.insecure, // #nosec G402 - no shit sherlock
|
||||
RootCAs: sf.roots,
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := DialTLS(context.Background(), net.JoinHostPort(sf.host, Itoa(sf.port, -1)), opts)
|
||||
conn, err := dialer.DialTLS(context.Background(), net.JoinHostPort(sf.host, lib.Itoa(sf.port, -1)), opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial server: %w", err)
|
||||
}
|
||||
80
lib/lib.go
80
lib/lib.go
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -112,6 +113,85 @@ func Duration(d time.Duration) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// IsDigit checks if a byte is a decimal digit.
|
||||
func IsDigit(b byte) bool {
|
||||
return b >= '0' && b <= '9'
|
||||
}
|
||||
|
||||
// ParseDuration parses a duration string into a time.Duration.
|
||||
// It supports standard units (ns, us/µs, ms, s, m, h) plus extended units:
|
||||
// d (days, 24h), w (weeks, 7d), y (years, 365d).
|
||||
// Units can be combined without spaces, e.g., "1y2w3d4h5m6s".
|
||||
// Case-insensitive. Years and days are approximations (no leap seconds/months).
|
||||
// Returns an error for invalid input.
|
||||
func ParseDuration(s string) (time.Duration, error) {
|
||||
s = strings.ToLower(s) // Normalize to lowercase for case-insensitivity.
|
||||
if s == "" {
|
||||
return 0, fmt.Errorf("empty duration string")
|
||||
}
|
||||
|
||||
var total time.Duration
|
||||
i := 0
|
||||
for i < len(s) {
|
||||
// Parse the number part.
|
||||
start := i
|
||||
for i < len(s) && IsDigit(s[i]) {
|
||||
i++
|
||||
}
|
||||
if start == i {
|
||||
return 0, fmt.Errorf("expected number at position %d", start)
|
||||
}
|
||||
numStr := s[start:i]
|
||||
num, err := strconv.ParseUint(numStr, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid number %q: %w", numStr, err)
|
||||
}
|
||||
|
||||
// Parse the unit part.
|
||||
if i >= len(s) {
|
||||
return 0, fmt.Errorf("expected unit after number %q", numStr)
|
||||
}
|
||||
unitStart := i
|
||||
i++ // Consume the first char of the unit.
|
||||
unit := s[unitStart:i]
|
||||
|
||||
// Handle potential two-char units like "ms".
|
||||
if unit == "m" && i < len(s) && s[i] == 's' {
|
||||
i++ // Consume the 's'.
|
||||
unit = "ms"
|
||||
}
|
||||
|
||||
// Convert to duration based on unit.
|
||||
var d time.Duration
|
||||
switch unit {
|
||||
case "ns":
|
||||
d = time.Nanosecond * time.Duration(num)
|
||||
case "us", "µs":
|
||||
d = time.Microsecond * time.Duration(num)
|
||||
case "ms":
|
||||
d = time.Millisecond * time.Duration(num)
|
||||
case "s":
|
||||
d = time.Second * time.Duration(num)
|
||||
case "m":
|
||||
d = time.Minute * time.Duration(num)
|
||||
case "h":
|
||||
d = time.Hour * time.Duration(num)
|
||||
case "d":
|
||||
d = 24 * time.Hour * time.Duration(num)
|
||||
case "w":
|
||||
d = 7 * 24 * time.Hour * time.Duration(num)
|
||||
case "y":
|
||||
d = 365 * 24 * time.Hour * time.Duration(num) // Approximate, non-leap year.
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown unit %q at position %d", s[unitStart:i], unitStart)
|
||||
}
|
||||
|
||||
total += d
|
||||
}
|
||||
|
||||
return total, nil
|
||||
}
|
||||
|
||||
type HexEncodeMode uint8
|
||||
|
||||
const (
|
||||
|
||||
@@ -2,10 +2,46 @@ package lib_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
)
|
||||
|
||||
func TestParseDuration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected time.Duration
|
||||
wantErr bool
|
||||
}{
|
||||
// Valid durations
|
||||
{"hour", "1h", time.Hour, false},
|
||||
{"day", "2d", 2 * 24 * time.Hour, false},
|
||||
{"minute", "3m", 3 * time.Minute, false},
|
||||
{"second", "4s", 4 * time.Second, false},
|
||||
|
||||
// Edge cases
|
||||
{"zero seconds", "0s", 0, false},
|
||||
{"empty string", "", 0, true},
|
||||
{"no numeric before unit", "h", 0, true},
|
||||
{"invalid unit", "1x", 0, true},
|
||||
{"non-numeric input", "abc", 0, true},
|
||||
{"missing unit", "10", 0, true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := lib.ParseDuration(tc.input)
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Fatalf("unexpected error: %v, wantErr: %v", err, tc.wantErr)
|
||||
}
|
||||
if got != tc.expected {
|
||||
t.Fatalf("expected %v, got %v", tc.expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHexEncode_LowerUpper(t *testing.T) {
|
||||
b := []byte{0x0f, 0xa1, 0x00, 0xff}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user