167 lines
3.8 KiB
Go
167 lines
3.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/flate"
|
|
"io"
|
|
"sync"
|
|
)
|
|
|
|
// CompressorConfig holds configuration for compression
|
|
type CompressorConfig struct {
|
|
Level int // Compression level (1-9, flate.BestSpeed to flate.BestCompression)
|
|
MinSize int // Minimum packet size to compress
|
|
CompressionMarker byte // Byte marker to identify compressed packets
|
|
}
|
|
|
|
// DefaultCompressorConfig returns default compressor configuration
|
|
func DefaultCompressorConfig() *CompressorConfig {
|
|
return &CompressorConfig{
|
|
Level: flate.DefaultCompression,
|
|
MinSize: 128,
|
|
CompressionMarker: 0x5A, // Same as original EQ2 implementation
|
|
}
|
|
}
|
|
|
|
// Compressor implements compression middleware using DEFLATE
|
|
type Compressor struct {
|
|
config *CompressorConfig
|
|
writerPool sync.Pool
|
|
readerPool sync.Pool
|
|
bufferPool sync.Pool
|
|
closeOnce sync.Once
|
|
}
|
|
|
|
// NewCompressor creates a new compression middleware
|
|
func NewCompressor(config *CompressorConfig) *Compressor {
|
|
if config == nil {
|
|
config = DefaultCompressorConfig()
|
|
}
|
|
|
|
c := &Compressor{
|
|
config: config,
|
|
}
|
|
|
|
// Initialize writer pool
|
|
c.writerPool.New = func() interface{} {
|
|
writer, _ := flate.NewWriter(nil, config.Level)
|
|
return writer
|
|
}
|
|
|
|
// Initialize reader pool
|
|
c.readerPool.New = func() interface{} {
|
|
return flate.NewReader(nil)
|
|
}
|
|
|
|
// Initialize buffer pool
|
|
c.bufferPool.New = func() interface{} {
|
|
return &bytes.Buffer{}
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
// ProcessOutbound implements Middleware.ProcessOutbound
|
|
func (c *Compressor) ProcessOutbound(data []byte, next func([]byte) (int, error)) (int, error) {
|
|
if len(data) < c.config.MinSize {
|
|
return next(data)
|
|
}
|
|
|
|
compressed, err := c.compress(data)
|
|
if err != nil || len(compressed) >= len(data) {
|
|
// Compression failed or didn't help - send original
|
|
return next(data)
|
|
}
|
|
|
|
return next(compressed)
|
|
}
|
|
|
|
// ProcessInbound implements Middleware.ProcessInbound
|
|
func (c *Compressor) ProcessInbound(data []byte, next func([]byte) (int, error)) (int, error) {
|
|
if len(data) < 2 {
|
|
return next(data)
|
|
}
|
|
|
|
// Check for compression marker
|
|
if data[0] != c.config.CompressionMarker {
|
|
return next(data)
|
|
}
|
|
|
|
decompressed, err := c.decompress(data[1:])
|
|
if err != nil {
|
|
// Decompression failed - pass through original
|
|
return next(data)
|
|
}
|
|
|
|
return next(decompressed)
|
|
}
|
|
|
|
func (c *Compressor) compress(data []byte) ([]byte, error) {
|
|
// Get buffer from pool
|
|
buf := c.bufferPool.Get().(*bytes.Buffer)
|
|
defer c.bufferPool.Put(buf)
|
|
buf.Reset()
|
|
|
|
// Write compression marker
|
|
buf.WriteByte(c.config.CompressionMarker)
|
|
|
|
// Get writer from pool
|
|
writer := c.writerPool.Get().(*flate.Writer)
|
|
defer c.writerPool.Put(writer)
|
|
|
|
writer.Reset(buf)
|
|
|
|
// Compress data
|
|
if _, err := writer.Write(data); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := writer.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Return copy of compressed data
|
|
result := make([]byte, buf.Len())
|
|
copy(result, buf.Bytes())
|
|
return result, nil
|
|
}
|
|
|
|
func (c *Compressor) decompress(data []byte) ([]byte, error) {
|
|
// Get reader from pool
|
|
reader := c.readerPool.Get().(io.ReadCloser)
|
|
defer c.readerPool.Put(reader)
|
|
|
|
// Reset reader with new data
|
|
if resetter, ok := reader.(flate.Resetter); ok {
|
|
resetter.Reset(bytes.NewReader(data), nil)
|
|
} else {
|
|
reader.Close()
|
|
reader = flate.NewReader(bytes.NewReader(data))
|
|
}
|
|
defer reader.Close()
|
|
|
|
// Get buffer from pool
|
|
buf := c.bufferPool.Get().(*bytes.Buffer)
|
|
defer c.bufferPool.Put(buf)
|
|
buf.Reset()
|
|
|
|
// Decompress data
|
|
if _, err := io.Copy(buf, reader); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Return copy of decompressed data
|
|
result := make([]byte, buf.Len())
|
|
copy(result, buf.Bytes())
|
|
return result, nil
|
|
}
|
|
|
|
// Close implements Middleware.Close
|
|
func (c *Compressor) Close() error {
|
|
c.closeOnce.Do(func() {
|
|
// Close any active readers/writers in pools
|
|
// Note: flate writers/readers don't need explicit cleanup
|
|
})
|
|
return nil
|
|
}
|