mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-27 14:50:52 +00:00
Added the `--short` flag to the `version` command for printing version information without styles. In this mode, only the version, Git commit hash, and build time in plain text format will be output, facilitating script parsing. Optimized Windows process detection logic to improve runtime accuracy. Removed redundant comments and simplified signal checking methods, making the code clearer and easier to maintain. refactor(protocol): Replaced string matching of data frame types with enumeration types. Unified the representation of data frame types in the protocol, using the `DataType` enumeration to improve performance and readability. Introduced a pooled buffer mechanism to improve memory efficiency in high-load scenarios. refactor(ui): Adjusted style definitions, removing hard-coded color values. Removed fixed color settings from some lipgloss styles, providing flexibility for future theme customization. ``` docs(install): Improved the version extraction function in the installation script. Added the `get_version_from_binary` function to enhance version identification capabilities, prioritizing plain mode output, ensuring accurate version number acquisition for the drip client or server across different terminal environments. perf(tcp): Improved TCP processing performance and connection management capabilities. Adjusted HTTP client transmission parameter configuration, increasing the maximum number of idle connections to accommodate higher concurrent requests. Improved error handling logic, adding special checks for common cases such as closing network connections to avoid log pollution. chore(writer): Expanded the FrameWriter queue length to improve batch write stability. Increased the FrameWriter queue size from 1024 to 2048, and released pooled resources after flushing, better handling sudden traffic spikes and reducing memory usage fluctuations.
149 lines
3.4 KiB
Go
149 lines
3.4 KiB
Go
package protocol
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
|
|
"drip/internal/shared/pool"
|
|
)
|
|
|
|
const (
|
|
FrameHeaderSize = 5
|
|
MaxFrameSize = 10 * 1024 * 1024
|
|
)
|
|
|
|
// FrameType defines the type of frame
|
|
type FrameType byte
|
|
|
|
const (
|
|
FrameTypeRegister FrameType = 0x01
|
|
FrameTypeRegisterAck FrameType = 0x02
|
|
FrameTypeHeartbeat FrameType = 0x03
|
|
FrameTypeHeartbeatAck FrameType = 0x04
|
|
FrameTypeData FrameType = 0x05
|
|
FrameTypeClose FrameType = 0x06
|
|
FrameTypeError FrameType = 0x07
|
|
)
|
|
|
|
// String returns the string representation of frame type
|
|
func (t FrameType) String() string {
|
|
switch t {
|
|
case FrameTypeRegister:
|
|
return "Register"
|
|
case FrameTypeRegisterAck:
|
|
return "RegisterAck"
|
|
case FrameTypeHeartbeat:
|
|
return "Heartbeat"
|
|
case FrameTypeHeartbeatAck:
|
|
return "HeartbeatAck"
|
|
case FrameTypeData:
|
|
return "Data"
|
|
case FrameTypeClose:
|
|
return "Close"
|
|
case FrameTypeError:
|
|
return "Error"
|
|
default:
|
|
return fmt.Sprintf("Unknown(%d)", t)
|
|
}
|
|
}
|
|
|
|
type Frame struct {
|
|
Type FrameType
|
|
Payload []byte
|
|
poolBuffer *[]byte
|
|
}
|
|
|
|
func WriteFrame(w io.Writer, frame *Frame) error {
|
|
payloadLen := len(frame.Payload)
|
|
if payloadLen > MaxFrameSize {
|
|
return fmt.Errorf("payload too large: %d bytes (max %d)", payloadLen, MaxFrameSize)
|
|
}
|
|
|
|
var header [FrameHeaderSize]byte
|
|
binary.BigEndian.PutUint32(header[0:4], uint32(payloadLen))
|
|
header[4] = byte(frame.Type)
|
|
|
|
if payloadLen == 0 {
|
|
if _, err := w.Write(header[:]); err != nil {
|
|
return fmt.Errorf("failed to write frame header: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// net.Buffers will use writev for TCP connections and falls back to
|
|
// sequential writes for other io.Writer implementations (e.g. TLS).
|
|
if _, err := (&net.Buffers{header[:], frame.Payload}).WriteTo(w); err != nil {
|
|
return fmt.Errorf("failed to write frame: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ReadFrame(r io.Reader) (*Frame, error) {
|
|
header := make([]byte, FrameHeaderSize)
|
|
if _, err := io.ReadFull(r, header); err != nil {
|
|
return nil, fmt.Errorf("failed to read frame header: %w", err)
|
|
}
|
|
|
|
payloadLen := binary.BigEndian.Uint32(header[0:4])
|
|
if payloadLen > MaxFrameSize {
|
|
return nil, fmt.Errorf("payload too large: %d bytes (max %d)", payloadLen, MaxFrameSize)
|
|
}
|
|
|
|
frameType := FrameType(header[4])
|
|
|
|
var payload []byte
|
|
var poolBuf *[]byte
|
|
|
|
if payloadLen > 0 {
|
|
if payloadLen > pool.SizeLarge {
|
|
payload = make([]byte, payloadLen)
|
|
if _, err := io.ReadFull(r, payload); err != nil {
|
|
return nil, fmt.Errorf("failed to read payload: %w", err)
|
|
}
|
|
} else {
|
|
poolBuf = pool.GetBuffer(int(payloadLen))
|
|
payload = (*poolBuf)[:payloadLen]
|
|
|
|
if _, err := io.ReadFull(r, payload); err != nil {
|
|
pool.PutBuffer(poolBuf)
|
|
return nil, fmt.Errorf("failed to read payload: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &Frame{
|
|
Type: frameType,
|
|
Payload: payload,
|
|
poolBuffer: poolBuf,
|
|
}, nil
|
|
}
|
|
|
|
func (f *Frame) Release() {
|
|
if f.poolBuffer != nil {
|
|
pool.PutBuffer(f.poolBuffer)
|
|
f.poolBuffer = nil
|
|
f.Payload = nil
|
|
}
|
|
}
|
|
|
|
// NewFrame creates a new frame
|
|
func NewFrame(frameType FrameType, payload []byte) *Frame {
|
|
return &Frame{
|
|
Type: frameType,
|
|
Payload: payload,
|
|
}
|
|
}
|
|
|
|
// NewFramePooled creates a new frame with a pooled buffer
|
|
// The poolBuffer will be automatically released after the frame is written
|
|
func NewFramePooled(frameType FrameType, payload []byte, poolBuffer *[]byte) *Frame {
|
|
return &Frame{
|
|
Type: frameType,
|
|
Payload: payload,
|
|
poolBuffer: poolBuffer,
|
|
}
|
|
}
|