johnsonjh/gfsmux

View on GitHub
mux.go

Summary

Maintainability
A
1 hr
Test Coverage
// Package gfsmux is a multiplexing library for Golang.
//
// It relies on an underlying connection to provide reliability and ordering,
// such as GFCP, and provides stream-oriented multiplexing over a single channel.
package gfsmux

import (
    "errors"
    "fmt"
    "io"
    "math"
    "time"
)

// Config is used to tune the Smux session
type Config struct {
    // SMUX Protocol version, support 1,2
    Version int

    // Disabled keepalive
    KeepAliveDisabled bool

    // KeepAliveInterval is how often to send a NOP command to the remote
    KeepAliveInterval time.Duration

    // KeepAliveTimeout is how long the session
    // will be closed if no data has arrived
    KeepAliveTimeout time.Duration

    // MaxFrameSize is used to control the maximum
    // frame size to sent to the remote
    MaxFrameSize int

    // MaxReceiveBuffer is used to control the maximum
    // number of data in the buffer pool
    MaxReceiveBuffer int

    // MaxStreamBuffer is used to control the maximum
    // number of data per stream
    MaxStreamBuffer int
}

// DefaultConfig is used to return a default Configuration
func DefaultConfig() *Config {
    return &Config{
        Version:           1,
        KeepAliveInterval: 10 * time.Second,
        KeepAliveTimeout:  30 * time.Second,
        MaxFrameSize:      32768,
        MaxReceiveBuffer:  4194304,
        MaxStreamBuffer:   65536,
    }
}

// VerifyConfig is used to verify the sanity of Configuration
func VerifyConfig(
    Config *Config,
) error {
    if !(Config.Version == 1 || Config.Version == 2) {
        return errors.New(
            "unsupported protocol version",
        )
    }
    if !Config.KeepAliveDisabled {
        if Config.KeepAliveInterval == 0 {
            return errors.New(
                "keep-alive interval must be positive",
            )
        }
        if Config.KeepAliveTimeout < Config.KeepAliveInterval {
            return fmt.Errorf(
                "keep-alive timeout must be larger than keep-alive interval",
            )
        }
    }
    if Config.MaxFrameSize <= 0 {
        return errors.New(
            "max frame size must be positive",
        )
    }
    if Config.MaxFrameSize > 65535 {
        return errors.New(
            "max frame size must not be larger than 65535",
        )
    }
    if Config.MaxReceiveBuffer <= 0 {
        return errors.New(
            "max receive buffer must be positive",
        )
    }
    if Config.MaxStreamBuffer <= 0 {
        return errors.New(
            "max stream buffer must be positive",
        )
    }
    if Config.MaxStreamBuffer > Config.MaxReceiveBuffer {
        return errors.New(
            "max stream buffer must not be larger than max receive buffer",
        )
    }
    if Config.MaxStreamBuffer > math.MaxInt32 {
        return errors.New(
            "max stream buffer cannot be larger than 2147483647",
        )
    }
    return nil
}

// Server is used to initialize a new server-side Connection.
func Server(
    Conn io.ReadWriteCloser,
    Config *Config,
) (
    *Session,
    error,
) {
    if Config == nil {
        Config = DefaultConfig()
    }
    if err := VerifyConfig(
        Config,
    ); err != nil {
        return nil, err
    }
    return newSession(
        Config,
        Conn,
        false,
    ), nil
}

// Client is used to initialize a new client-side Connection.
func Client(
    Conn io.ReadWriteCloser,
    Config *Config,
) (
    *Session,
    error,
) {
    if Config == nil {
        Config = DefaultConfig()
    }

    if err := VerifyConfig(
        Config,
    ); err != nil {
        return nil, err
    }
    return newSession(
        Config,
        Conn,
        true,
    ), nil
}