Files
golib/socket/client
nabbar 3837f0b2bb Improvements, test & documentatons (2025-12 #1)
[file/bandwidth]
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[file/perm]
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases
- ADD function to parse form "rwx-wxr-x" or "-rwx-w-r-x"
- ADD function to ParseFileMode to convert os.FileMode to file.Perm

[file/progress]
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[ioutils/...]
- UPDATE documentation: update enhanced README and TESTING guidelines
- UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases

[logger/...]
- UPDATE documentation: update enhanced README and TESTING guidelines
- ADD documentation: add enhanced README and TESTING guidelines for sub
  packages
- UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases
- UPDATE config: remove FileBufferSize from OptionFile (rework hookfile)
- UPDATE fields: expose Store function in interface
- REWORK hookfile: rework package, use aggregator to allow multi write and
  single file
- FIX hookstderr: fix bug with NonColorable
- FIX hookstdout: fix bug with NonColorable
- FIX hookwriter: fix bug with NonColorable

[network/protocol]
- ADD function IsTCP, IsUDP, IsUnixLike to check type of protocol

[runner]
- FIX typo

[socket]
- UPDATE documentation: update enhanced README and TESTING guidelines
- ADD documentation: add enhanced README and TESTING guidelines for sub
  packages
- UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases
- REWORK server: use context compatible io.reader, io.writer, io.closer
  instead of reader / writer
- REWORK server: simplify, optimize server
- REMOVE reader, writer type
- ADD context: add new interface in root socket interface to expose
  context interface that extend context, io reader/writer/closer,
dediacted function to server (IsConnected, ...)
2025-12-02 02:56:20 +01:00
..

Socket Client Factory

Go Version License Coverage

Platform-aware factory for creating socket clients across different network protocols (TCP, UDP, UNIX) with unified interface, TLS support, and automatic protocol selection.


Table of Contents


Overview

The client package provides a unified factory for creating socket clients across different network protocols. It automatically selects the appropriate protocol-specific implementation based on configuration while providing a consistent API through the github.com/nabbar/golib/socket.Client interface.

Design Philosophy

  1. Simplicity First: Single entry point (New) for all protocol types
  2. Platform Awareness: Automatic protocol availability based on OS
  3. Type Safety: Configuration-based client creation with validation
  4. Consistent API: All clients implement socket.Client interface
  5. Zero Overhead: Factory only adds a single switch statement

Key Features

  • Unified Factory: Single New() function for all protocols
  • Platform-Aware: Automatic Unix socket support detection
  • Type-Safe: Uses config.Client struct for configuration
  • Protocol Validation: Returns error for unsupported protocols
  • TLS Support: Transparent TLS configuration for TCP clients
  • Zero Dependencies: Only delegates to sub-packages
  • Minimal Overhead: Direct delegation without wrapping
  • Panic Recovery: Automatic recovery with detailed logging

Architecture

Component Diagram

┌─────────────────────────────────────────────────────────┐
│                  client.New(cfg, def)                   │
│                   (Factory Function)                    │
└───────────────────────────┬─────────────────────────────┘
                            │
        ┌─────────────┬─────┴───────┬───────────┐
        │             │             │           │
        ▼             ▼             ▼           ▼
 ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
 │   TCP    │  │   UDP    │  │   Unix   │  │ UnixGram │
 │  Client  │  │  Client  │  │  Client  │  │  Client  │
 └──────────┘  └──────────┘  └──────────┘  └──────────┘
      │             │             │             │
      └─────────────┴──────┬──────┴─────────────┘
                           │
                 ┌─────────▼─────────┐
                 │  socket.Client    │
                 │    (Interface)    │
                 └───────────────────┘

Factory Pattern

The package implements the Factory Method pattern:

Protocol Selection Logic:

New(cfg, def) → cfg.Network.IsTCP()    → tcp.New()
             → cfg.Network.IsUDP()    → udp.New()
             → cfg.Network.IsUnix()   → unix.New() (Linux/Darwin)
             → cfg.Network.IsUnixGram() → unixgram.New() (Linux/Darwin)
             → Unknown                → ErrInvalidProtocol

Advantages:

  • Single import for all protocols
  • Consistent API across protocols
  • Easy protocol switching via configuration
  • Centralized error handling

Trade-offs:

  • Slight indirection overhead (negligible)
  • Less explicit about protocol used

Platform Support

Platform TCP UDP Unix UnixGram
Linux
Darwin/macOS
Windows
Other

Performance

Benchmarks

Factory overhead is negligible:

Operation Time Overhead
Factory Call <1µs Single switch + function call
TCP Creation ~50µs Dominated by protocol implementation
UDP Creation ~40µs Dominated by protocol implementation
Unix Creation ~35µs Dominated by protocol implementation

Conclusion: Factory adds <1% overhead compared to direct protocol package usage.

Memory Usage

Base overhead:        ~0 bytes (no state stored)
Per client:           Same as direct protocol usage
Factory function:     Stack-only allocation

No heap allocations - factory is allocation-free.

Scalability

  • Concurrent Factory Calls: Thread-safe, tested with 100 concurrent goroutines
  • Client Independence: Each client is fully independent
  • Zero Shared State: No contention between clients

Use Cases

1. Multi-Protocol Application

Problem: Application needs to support multiple protocols based on configuration.

// Configuration-driven protocol selection
cfg := loadConfig()  // Returns config.Client

cli, err := client.New(cfg, nil)
if err != nil {
    log.Fatal(err)
}
defer cli.Close()

// Same code works for TCP, UDP, Unix
ctx := context.Background()
cli.Connect(ctx)
cli.Write([]byte("data"))

Real-world: Used in microservices that communicate via TCP over network or Unix sockets locally.

2. Platform-Specific Optimization

Problem: Use Unix sockets on Linux/Darwin, fall back to TCP on Windows.

// Try Unix first (fastest)
cfg := config.Client{
    Network: protocol.NetworkUnix,
    Address: "/tmp/app.sock",
}

cli, err := client.New(cfg, nil)
if err == config.ErrInvalidProtocol {
    // Fall back to TCP on unsupported platforms
    cfg.Network = protocol.NetworkTCP
    cfg.Address = "localhost:8080"
    cli, err = client.New(cfg, nil)
}

3. TLS Configuration Management

Problem: Centralized TLS configuration for TCP clients.

// Shared TLS config
tlsCfg := loadTLSConfig()

// Create multiple TCP clients with same TLS
for _, addr := range servers {
    cfg := config.Client{
        Network: protocol.NetworkTCP,
        Address: addr,
        TLS: config.ClientTLS{
            Enabled:    true,
            ServerName: extractHost(addr),
        },
    }
    
    cli, _ := client.New(cfg, tlsCfg)
    clients = append(clients, cli)
}

4. Testing with Protocol Mocking

Problem: Test application with different protocols without changing code.

// Production: Unix socket
prodCfg := config.Client{
    Network: protocol.NetworkUnix,
    Address: "/var/run/app.sock",
}

// Test: TCP socket for easier testing
testCfg := config.Client{
    Network: protocol.NetworkTCP,
    Address: "localhost:" + strconv.Itoa(testPort),
}

// Same application code
cfg := selectConfig(isTest)
cli, _ := client.New(cfg, nil)

Quick Start

Installation

go get github.com/nabbar/golib/socket/client

TCP Client

Simple TCP connection:

package main

import (
    "context"
    "log"
    
    libptc "github.com/nabbar/golib/network/protocol"
    sckcfg "github.com/nabbar/golib/socket/config"
    sckclt "github.com/nabbar/golib/socket/client"
)

func main() {
    // Create configuration
    cfg := sckcfg.Client{
        Network: libptc.NetworkTCP,
        Address: "localhost:8080",
    }
    
    // Create client using factory
    cli, err := sckclt.New(cfg, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer cli.Close()
    
    // Connect and communicate
    ctx := context.Background()
    if err := cli.Connect(ctx); err != nil {
        log.Fatal(err)
    }
    
    cli.Write([]byte("Hello, server!"))
}

TCP with TLS

Secure TCP connection:

import (
    libtls "github.com/nabbar/golib/certificates"
)

func main() {
    // Configure TLS
    tlsCfg := libtls.NewTLSConfig()
    // ... configure certificates ...
    
    cfg := sckcfg.Client{
        Network: libptc.NetworkTCP,
        Address: "secure.example.com:443",
        TLS: sckcfg.ClientTLS{
            Enabled:    true,
            ServerName: "secure.example.com",
        },
    }
    
    cli, err := sckclt.New(cfg, tlsCfg)
    if err != nil {
        log.Fatal(err)
    }
    defer cli.Close()
    
    // Encrypted communication
    ctx := context.Background()
    cli.Connect(ctx)
    cli.Write([]byte("Secure data"))
}

UDP Client

Connectionless datagram communication:

func main() {
    cfg := sckcfg.Client{
        Network: libptc.NetworkUDP,
        Address: "localhost:9000",
    }
    
    cli, err := sckclt.New(cfg, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer cli.Close()
    
    ctx := context.Background()
    cli.Connect(ctx)
    
    // Send datagram
    cli.Write([]byte("metric:value|type"))
}

UNIX Socket Client

High-performance local IPC (Linux/Darwin only):

func main() {
    cfg := sckcfg.Client{
        Network: libptc.NetworkUnix,
        Address: "/tmp/app.sock",
    }
    
    cli, err := sckclt.New(cfg, nil)
    if err != nil {
        if err == sckcfg.ErrInvalidProtocol {
            log.Fatal("Unix sockets not supported on this platform")
        }
        log.Fatal(err)
    }
    defer cli.Close()
    
    ctx := context.Background()
    cli.Connect(ctx)
    cli.Write([]byte("command"))
}

Error Handling

Proper error handling patterns:

func main() {
    cfg := sckcfg.Client{
        Network: libptc.NetworkUnix,
        Address: "/tmp/app.sock",
    }
    
    cli, err := sckclt.New(cfg, nil)
    if err != nil {
        if err == sckcfg.ErrInvalidProtocol {
            // Protocol not supported on this platform
            // Fall back to TCP
            cfg.Network = libptc.NetworkTCP
            cfg.Address = "localhost:8080"
            cli, err = sckclt.New(cfg, nil)
            if err != nil {
                log.Fatal(err)
            }
        } else {
            log.Fatal(err)
        }
    }
    defer cli.Close()
    
    // Use client...
}

Best Practices

DO

Use Factory for Protocol Abstraction:

// ✅ Good: Configuration-driven
cfg := loadConfig()
cli, err := client.New(cfg, nil)
if err != nil {
    return err
}
defer cli.Close()

Handle Platform-Specific Protocols:

// ✅ Good: Check for platform support
cli, err := client.New(cfg, nil)
if err == config.ErrInvalidProtocol {
    // Handle unsupported protocol
    cfg.Network = protocol.NetworkTCP
    cli, err = client.New(cfg, nil)
}

Resource Management:

// ✅ Good: Always cleanup
cli, err := client.New(cfg, nil)
if err != nil {
    return err
}
defer cli.Close()  // Ensure cleanup

Context Usage:

// ✅ Good: Use context for timeouts
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

err := cli.Connect(ctx)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        return fmt.Errorf("connection timeout")
    }
    return err
}

DON'T

Don't ignore protocol errors:

// ❌ BAD: Ignoring errors
cli, _ := client.New(cfg, nil)
cli.Connect(ctx)  // May panic on nil client

// ✅ GOOD: Check errors
cli, err := client.New(cfg, nil)
if err != nil {
    return err
}

Don't assume protocol availability:

// ❌ BAD: Assume Unix sockets available
cfg := config.Client{
    Network: protocol.NetworkUnix,
    Address: "/tmp/app.sock",
}
cli, _ := client.New(cfg, nil)  // Returns error on Windows

// ✅ GOOD: Check platform support
cli, err := client.New(cfg, nil)
if err == config.ErrInvalidProtocol {
    // Fall back to TCP
}

Don't create clients without cleanup:

// ❌ BAD: No cleanup
client.New(cfg, nil)

// ✅ GOOD: Always defer Close
cli, err := client.New(cfg, nil)
if err != nil {
    return err
}
defer cli.Close()

API Reference

Factory Function

func New(cfg sckcfg.Client, def libtls.TLSConfig) (libsck.Client, error)

Parameters:

  • cfg: Client configuration (network type, address, TLS settings)
  • def: Default TLS configuration (optional, can be nil)

Returns:

  • libsck.Client: Client instance implementing socket.Client interface
  • error: Error if configuration is invalid or protocol unsupported

Behavior:

  1. Validates configuration (Validate())
  2. Switches on cfg.Network type
  3. Delegates to appropriate protocol package
  4. Returns configured client or error

Panic Recovery: All panics are caught and logged via RecoveryCaller.

Configuration

type Client struct {
    Network NetworkProtocol  // Required: TCP, UDP, Unix, UnixGram
    Address string           // Required: "host:port" or "/path/to/socket"
    TLS     ClientTLS        // Optional: TLS configuration (TCP only)
}

type ClientTLS struct {
    Enabled    bool      // Enable TLS
    Config     TLSConfig // TLS certificates and settings
    ServerName string    // Server name for verification
}

Validation Rules:

  • Network must be valid protocol constant
  • Address must be non-empty
  • Unix sockets only on Linux/Darwin

Error Codes

var (
    ErrInvalidProtocol = errors.New("invalid protocol")
)

Error Scenarios:

Error Cause Action
ErrInvalidProtocol Protocol not supported on platform Fall back to supported protocol
ErrInvalidInstance Configuration validation failed Check cfg.Network and cfg.Address
Protocol-specific errors From underlying implementation See protocol package documentation

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Code Quality

    • Follow Go best practices and idioms
    • Maintain or improve code coverage (target: >80%)
    • Pass all tests including race detector
    • Use gofmt and golint
  2. AI Usage Policy

    • AI must NEVER be used to generate package code or core functionality
    • AI assistance is limited to:
      • Testing (writing and improving tests)
      • Debugging (troubleshooting and bug resolution)
      • Documentation (comments, README, TESTING.md)
    • All AI-assisted work must be reviewed and validated by humans
  3. Testing

    • Add tests for new features
    • Use Ginkgo v2 / Gomega for test framework
    • Ensure zero race conditions
    • Test platform-specific code on target platforms
  4. Documentation

    • Update GoDoc comments for public APIs
    • Add examples for new features
    • Update README.md and TESTING.md if needed
  5. Pull Request Process

    • Fork the repository
    • Create a feature branch
    • Write clear commit messages
    • Ensure all tests pass
    • Update documentation
    • Submit PR with description of changes

Improvements & Security

Current Status

The package is production-ready with no urgent improvements or security vulnerabilities identified.

Code Quality Metrics

  • 81.2% test coverage (target: >80%)
  • Zero race conditions detected with -race flag
  • Thread-safe implementation using atomic operations
  • Panic recovery in all critical paths
  • Memory-safe with proper resource cleanup

Future Enhancements (Non-urgent)

The following enhancements could be considered for future versions:

  1. Protocol Auto-Detection: Automatically detect best protocol for given address
  2. Connection Pooling: Factory-managed connection pools per protocol
  3. Metrics Integration: Optional Prometheus metrics for factory usage
  4. Configuration Validation: Enhanced validation with detailed error messages

These are optional improvements and not required for production use. The current implementation is stable and performant.


Resources

Package Documentation

  • GoDoc - Complete API reference with function signatures, method descriptions, and runnable examples.

  • doc.go - In-depth package documentation including design philosophy, architecture diagrams, protocol selection logic, and best practices for production use.

  • TESTING.md - Comprehensive test suite documentation covering test architecture, BDD methodology with Ginkgo v2, coverage analysis (81.2%), and guidelines for writing new tests.

External References

  • Go net Package - Standard library networking primitives used by all protocol implementations.

  • Effective Go - Official Go programming guide covering best practices for interface design and error handling.

  • Factory Method Pattern - Design pattern documentation explaining the factory pattern used by this package.


AI Transparency

In compliance with EU AI Act Article 50.4: AI assistance was used for testing, documentation, and bug resolution under human supervision. All core functionality is human-designed and validated.


License

MIT License - See LICENSE file for details.

Copyright (c) 2025 Nicolas JUHEL


Maintained by: Nicolas JUHEL
Package: github.com/nabbar/golib/socket/client
Version: See releases for versioning