selectcache

package module
v0.0.0-...-c1129fe Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2025 License: MIT Imports: 16 Imported by: 0

README

go-select-cache

HTTP response caching middleware for Go that selectively caches responses based on content type, excluding HTML to keep dynamic content fresh.

Features

  • HTTP middleware for response caching
  • Content-type based filtering (excludes HTML by default)
  • Configurable TTL for cached responses
  • Cache statistics (hit/miss counts)
  • Support for GET and HEAD requests only

Installation

go get github.com/go-i2p/go-select-cache

Dependencies

  • github.com/patrickmn/go-cache - Used for simplified HTTP middleware caching
  • Custom TTL cache implementation with LRU eviction for advanced transport-layer caching

Quick Start

HTTP Server with Middleware Caching
package main

import (
    "encoding/json"
    "net/http"
    "time"
    
    "github.com/go-i2p/go-select-cache"
)

func main() {
    // Create cache middleware
    cache := selectcache.NewDefault()
    // Or with custom config:
    // config := selectcache.Config{
    //     DefaultTTL:           15 * time.Minute,
    //     CleanupInterval:      5 * time.Minute,
    //     ExcludeContentTypes:  []string{"text/html", "application/xhtml+xml"},
    //     IncludeStatusCodes:   []int{200},
    // }
    // cache := selectcache.New(config)

    mux := http.NewServeMux()
    mux.HandleFunc("/api/data", apiHandler)    // Will be cached
    mux.HandleFunc("/", htmlHandler)           // Will NOT be cached (HTML)
    
    // Wrap with cache middleware
    server := &http.Server{
        Addr:    ":8080",
        Handler: cache.Handler(mux),
    }
    server.ListenAndServe()
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(`{"message": "This will be cached"}`))
}

func htmlHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/html")
    w.Write([]byte("<h1>This HTML won't be cached</h1>"))
}

Configuration

type Config struct {
    // DefaultTTL is the default time-to-live for cached responses
    DefaultTTL time.Duration
    
    // CleanupInterval is how often expired items are removed  
    CleanupInterval time.Duration
    
    // ExcludeContentTypes are MIME types that should not be cached
    // Default: ["text/html", "application/xhtml+xml"]
    ExcludeContentTypes []string
    
    // IncludeStatusCodes are HTTP status codes that should be cached
    // Default: [200]
    IncludeStatusCodes []int
}
Default Configuration
config := selectcache.DefaultConfig()
// Returns:
// DefaultTTL: 15 minutes
// CleanupInterval: 5 minutes  
// ExcludeContentTypes: ["text/html", "application/xhtml+xml"]
// IncludeStatusCodes: [200]

What Gets Cached

  • Only GET and HEAD requests are cached
  • Only responses with 200 status code (configurable)
  • All content types EXCEPT those in the exclusion list
Default Behavior
  • CACHED: application/json, image/*, text/css, application/javascript, etc.
  • NOT CACHED: text/html, application/xhtml+xml (to keep HTML pages dynamic)

API Reference

Core Functions
// Create middleware with default settings
func NewDefault() *Middleware

// Create middleware with custom configuration  
func New(config Config) *Middleware

// Get default configuration
func DefaultConfig() Config
Middleware Methods
// Wrap an HTTP handler with caching
func (m *Middleware) Handler(next http.Handler) http.Handler

// Wrap an HTTP handler function with caching
func (m *Middleware) HandlerFunc(next http.HandlerFunc) http.Handler

// Get cache statistics (itemCount, hitCount, missCount)
func (m *Middleware) Stats() (int, uint64, uint64)

// Clear all cached responses
func (m *Middleware) Clear()

// Delete specific cached response by URL
func (m *Middleware) Delete(url string)
Usage Examples
// Get cache statistics
itemCount, hitCount, missCount := cache.Stats()
fmt.Printf("Cache: %d items, %d hits, %d misses\n", itemCount, hitCount, missCount)

// Add cache stats endpoint
http.HandleFunc("/cache/stats", func(w http.ResponseWriter, r *http.Request) {
    itemCount, hitCount, missCount := cache.Stats()
    fmt.Fprintf(w, "Items: %d, Hits: %d, Misses: %d", itemCount, hitCount, missCount)
})

// Clear cache endpoint  
http.HandleFunc("/cache/clear", func(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        cache.Clear()
        w.Write([]byte("Cache cleared"))
    }
})

Advanced Transport-Layer Caching

For advanced use cases, the library also provides transport-layer caching that can intercept and cache responses at the connection level:

CachingListener
package main

import (
    "net"
    "net/http"
    
    "github.com/go-i2p/go-select-cache"
)

func main() {
    // Create base listener
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    
    // Create advanced cache config
    config := selectcache.DefaultCacheConfig()
    config.MaxMemoryMB = 256
    config.MaxEntries = 5000
    
    // Wrap with caching listener
    cachingListener := selectcache.NewCachingListener(listener, config)
    defer cachingListener.Close()
    
    // Use with HTTP server
    server := &http.Server{Handler: yourHandler}
    server.Serve(cachingListener)
}
Advanced Configuration
type CacheConfig struct {
    // DefaultTTL is the default time-to-live for cached responses
    DefaultTTL time.Duration
    
    // ContentTypeTTLs provides per-content-type TTL overrides
    ContentTypeTTLs map[string]time.Duration
    
    // MaxMemoryMB is the maximum memory in megabytes for cache storage
    MaxMemoryMB int64
    
    // MaxEntries is the maximum number of cache entries
    MaxEntries int
    
    // ExcludedTypes are content types that should never be cached
    ExcludedTypes []string
    
    // EnableMetrics determines if performance metrics are collected
    EnableMetrics bool
    
    // CleanupInterval is how often expired entries are removed
    CleanupInterval time.Duration
    
    // BufferSize is the size of the read buffer for connection analysis
    BufferSize int
    
    // ConnectionTimeout is the maximum time to wait for connection analysis
    ConnectionTimeout time.Duration
}

Examples

Complete working examples are available in the example/ and examples/ directories:

# Run the basic HTTP middleware example
cd example
go run main.go

# Test with curl
curl http://localhost:8080/api/users  # Will be cached
curl http://localhost:8080/api/data   # Will be cached  
curl http://localhost:8080/           # HTML - not cached
curl http://localhost:8080/cache/stats # Check cache statistics

# Run advanced examples
cd examples/http-server
go run main.go

cd examples/tcp-server  
go run main.go

How It Works

  1. Request Filtering: Only caches GET and HEAD requests
  2. Response Capture: Uses ResponseRecorder to capture response data
  3. Content-Type Check: Excludes configured content types (HTML by default)
  4. Status Code Check: Only caches configured status codes (200 by default)
  5. Cache Storage: Stores responses in memory with TTL using patrickmn/go-cache
  6. Cache Lookup: Subsequent requests check cache first using SHA256-based keys

License

MIT License

Documentation

Overview

Package selectcache provides transport-layer caching middleware for Go's net.Listener that intercepts and caches network responses with configurable TTL management.

License: MIT

Package selectcache provides HTTP middleware for selective response caching using go-cache, with content-type based filtering to exclude HTML responses.

License: MIT (matches go-cache dependency)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateCacheKey

func GenerateCacheKey(method, path, query string, headers map[string]string) string

GenerateCacheKey creates a consistent cache key from request characteristics

Types

type CacheConfig

type CacheConfig struct {
	// DefaultTTL is the default time-to-live for cached responses
	DefaultTTL time.Duration `json:"default_ttl"`

	// ContentTypeTTLs provides per-content-type TTL overrides
	ContentTypeTTLs map[string]time.Duration `json:"content_type_ttls"`

	// MaxMemoryMB is the maximum memory in megabytes for cache storage
	MaxMemoryMB int64 `json:"max_memory_mb"`

	// MaxEntries is the maximum number of cache entries
	MaxEntries int `json:"max_entries"`

	// ExcludedTypes are content types that should never be cached
	ExcludedTypes []string `json:"excluded_types"`

	// EnableMetrics determines if performance metrics are collected
	EnableMetrics bool `json:"enable_metrics"`

	// CleanupInterval is how often expired entries are removed
	CleanupInterval time.Duration `json:"cleanup_interval"`

	// BufferSize is the size of the read buffer for connection analysis
	BufferSize int `json:"buffer_size"`

	// ConnectionTimeout is the maximum time to wait for connection analysis
	ConnectionTimeout time.Duration `json:"connection_timeout"`
}

CacheConfig holds configuration for the transport-layer caching middleware

func DefaultCacheConfig

func DefaultCacheConfig() *CacheConfig

DefaultCacheConfig returns sensible defaults for the caching middleware

func (*CacheConfig) GetTTLForContentType

func (c *CacheConfig) GetTTLForContentType(contentType string) time.Duration

GetTTLForContentType returns the TTL for a specific content type, falling back to DefaultTTL if no specific TTL is configured

func (*CacheConfig) IsContentTypeExcluded

func (c *CacheConfig) IsContentTypeExcluded(contentType string) bool

IsContentTypeExcluded checks if a content type should be excluded from caching

func (*CacheConfig) LoadFromJSON

func (c *CacheConfig) LoadFromJSON(data []byte) error

LoadFromJSON loads configuration from JSON bytes

func (*CacheConfig) ToJSON

func (c *CacheConfig) ToJSON() ([]byte, error)

ToJSON serializes the configuration to JSON

func (*CacheConfig) Validate

func (c *CacheConfig) Validate() error

Validate checks the configuration for invalid values

type CacheEntry

type CacheEntry struct {
	// Response data
	Data    []byte      `json:"data"`
	Headers http.Header `json:"headers"`

	// Timing information
	ExpiresAt  time.Time `json:"expires_at"`
	AccessTime time.Time `json:"access_time"`
	StoreTime  time.Time `json:"store_time"`

	// Metadata
	ContentType string `json:"content_type"`
	Size        int    `json:"size"`
}

CacheEntry represents a single cached response with metadata

func (*CacheEntry) IsExpired

func (e *CacheEntry) IsExpired() bool

IsExpired checks if the cache entry has expired

func (*CacheEntry) UpdateAccessTime

func (e *CacheEntry) UpdateAccessTime()

UpdateAccessTime updates the last access time for LRU tracking

type CacheMetrics

type CacheMetrics struct {
	// contains filtered or unexported fields
}

CacheMetrics collects performance metrics for the caching system

func NewCacheMetrics

func NewCacheMetrics(enabled bool) *CacheMetrics

NewCacheMetrics creates a new metrics collector

func (*CacheMetrics) GetStats

func (m *CacheMetrics) GetStats() CacheStats

GetStats returns a snapshot of current metrics

func (*CacheMetrics) IsEnabled

func (m *CacheMetrics) IsEnabled() bool

IsEnabled returns whether metrics collection is enabled

func (*CacheMetrics) RecordDeletion

func (m *CacheMetrics) RecordDeletion()

RecordDeletion increments the deletion counter

func (*CacheMetrics) RecordError

func (m *CacheMetrics) RecordError(errorType string)

RecordError increments the error counter for a specific error type

func (*CacheMetrics) RecordEviction

func (m *CacheMetrics) RecordEviction()

RecordEviction increments the eviction counter

func (*CacheMetrics) RecordHit

func (m *CacheMetrics) RecordHit()

RecordHit increments the cache hit counter

func (*CacheMetrics) RecordLookupTime

func (m *CacheMetrics) RecordLookupTime(duration time.Duration)

RecordLookupTime adds to the total lookup time for average calculation

func (*CacheMetrics) RecordMiss

func (m *CacheMetrics) RecordMiss()

RecordMiss increments the cache miss counter

func (*CacheMetrics) RecordStore

func (m *CacheMetrics) RecordStore()

RecordStore increments the cache store counter

func (*CacheMetrics) RecordStoreTime

func (m *CacheMetrics) RecordStoreTime(duration time.Duration)

RecordStoreTime adds to the total store time for average calculation

func (*CacheMetrics) Reset

func (m *CacheMetrics) Reset()

Reset clears all metrics

func (*CacheMetrics) UpdateMemoryUsage

func (m *CacheMetrics) UpdateMemoryUsage(bytes uint64, entryCount int)

UpdateMemoryUsage sets the current memory usage

type CacheStats

type CacheStats struct {
	// Operation counts
	Hits      uint64 `json:"hits"`
	Misses    uint64 `json:"misses"`
	Stores    uint64 `json:"stores"`
	Evictions uint64 `json:"evictions"`
	Deletions uint64 `json:"deletions"`

	// Calculated metrics
	HitRatio        float64 `json:"hit_ratio"`
	AvgLookupTimeMs float64 `json:"avg_lookup_time_ms"`
	AvgStoreTimeMs  float64 `json:"avg_store_time_ms"`

	// Memory usage
	TotalMemoryBytes uint64 `json:"total_memory_bytes"`
	EntryCount       int    `json:"entry_count"`
	AvgEntrySize     uint64 `json:"avg_entry_size"`

	// Error counts
	Errors map[string]uint64 `json:"errors"`
}

CacheStats represents a snapshot of cache metrics

type CachedResponse

type CachedResponse struct {
	StatusCode int
	Headers    http.Header
	Body       []byte
}

CachedResponse represents a cached HTTP response

type CachingConnection

type CachingConnection struct {
	net.Conn
	// contains filtered or unexported fields
}

CachingConnection wraps a net.Conn to provide transparent response caching

func NewCachingConnection

func NewCachingConnection(conn net.Conn, cache *TTLCache, config *CacheConfig, metrics *CacheMetrics, detector *ContentDetector) *CachingConnection

NewCachingConnection creates a new caching connection wrapper

func (*CachingConnection) Close

func (c *CachingConnection) Close() error

Close closes the connection and performs cleanup

func (*CachingConnection) GetStats

func (c *CachingConnection) GetStats() ConnectionStats

GetStats returns statistics for this connection

func (*CachingConnection) ID

func (c *CachingConnection) ID() string

ID returns the unique identifier for this connection

func (*CachingConnection) LocalAddr

func (c *CachingConnection) LocalAddr() net.Addr

LocalAddr returns the local network address

func (*CachingConnection) Read

func (c *CachingConnection) Read(b []byte) (int, error)

Read intercepts read operations to analyze requests

func (*CachingConnection) RemoteAddr

func (c *CachingConnection) RemoteAddr() net.Addr

RemoteAddr returns the remote network address

func (*CachingConnection) SetCloseCallback

func (c *CachingConnection) SetCloseCallback(callback func())

SetCloseCallback sets a callback function to be called when the connection closes

func (*CachingConnection) SetDeadline

func (c *CachingConnection) SetDeadline(t time.Time) error

SetDeadline sets both read and write deadlines

func (*CachingConnection) SetReadDeadline

func (c *CachingConnection) SetReadDeadline(t time.Time) error

SetReadDeadline sets the read deadline

func (*CachingConnection) SetWriteDeadline

func (c *CachingConnection) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the write deadline

func (*CachingConnection) Write

func (c *CachingConnection) Write(b []byte) (int, error)

Write intercepts write operations to cache responses

type CachingListener

type CachingListener struct {
	// contains filtered or unexported fields
}

CachingListener wraps a net.Listener to provide transparent caching of responses

func NewCachingListener

func NewCachingListener(listener net.Listener, config *CacheConfig) *CachingListener

NewCachingListener creates a new caching listener that wraps the provided listener

func (*CachingListener) Accept

func (cl *CachingListener) Accept() (net.Conn, error)

Accept waits for and returns the next connection to the listener

func (*CachingListener) Addr

func (cl *CachingListener) Addr() net.Addr

Addr returns the listener's network address

func (*CachingListener) ClearCache

func (cl *CachingListener) ClearCache()

ClearCache removes all cached entries

func (*CachingListener) Close

func (cl *CachingListener) Close() error

Close closes the listener and all active connections

func (*CachingListener) GetCache

func (cl *CachingListener) GetCache() *TTLCache

GetCache returns the underlying cache for management operations

func (*CachingListener) GetConfig

func (cl *CachingListener) GetConfig() *CacheConfig

GetConfig returns the cache configuration

func (*CachingListener) GetMetrics

func (cl *CachingListener) GetMetrics() *CacheMetrics

GetMetrics returns the current cache metrics

func (*CachingListener) GetStats

func (cl *CachingListener) GetStats() ListenerStats

GetStats returns comprehensive statistics about the caching listener

func (*CachingListener) UpdateConfig

func (cl *CachingListener) UpdateConfig(newConfig *CacheConfig) error

UpdateConfig updates the cache configuration (note: some changes require restart)

type Config

type Config struct {
	// DefaultTTL is the default time-to-live for cached responses
	DefaultTTL time.Duration
	// CleanupInterval is how often expired items are removed
	CleanupInterval time.Duration
	// ExcludeContentTypes are MIME types that should not be cached
	// Default: ["text/html", "application/xhtml+xml"]
	ExcludeContentTypes []string
	// IncludeStatusCodes are HTTP status codes that should be cached
	// Default: [200]
	IncludeStatusCodes []int
}

Config holds configuration for the caching middleware

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns sensible defaults for the middleware

type ConnectionStats

type ConnectionStats struct {
	ID            string `json:"id"`
	IsHTTPRequest bool   `json:"is_http_request"`
	HasCacheKey   bool   `json:"has_cache_key"`
	RequestSize   int    `json:"request_size"`
	ResponseSize  int    `json:"response_size"`
	LocalAddr     string `json:"local_addr"`
	RemoteAddr    string `json:"remote_addr"`
	Closed        bool   `json:"closed"`
}

ConnectionStats contains statistics for a caching connection

type ContentDetector

type ContentDetector struct {
	// contains filtered or unexported fields
}

ContentDetector analyzes response data to determine content type and cacheability

func NewContentDetector

func NewContentDetector(config *CacheConfig) *ContentDetector

NewContentDetector creates a new content detector with the given configuration

func (*ContentDetector) AnalyzeResponse

func (d *ContentDetector) AnalyzeResponse(response []byte, headers http.Header, statusCode int) *ResponseAnalysis

AnalyzeResponse performs comprehensive analysis of a response for caching decisions

func (*ContentDetector) DetectContentTypeFromBytes

func (d *ContentDetector) DetectContentTypeFromBytes(data []byte) string

DetectContentTypeFromBytes attempts to detect content type from response bytes This is a fallback when Content-Type header is missing or unreliable

func (*ContentDetector) GetContentType

func (d *ContentDetector) GetContentType(headers http.Header) string

GetContentType extracts and normalizes the content type from headers

func (*ContentDetector) IsHTMLContent

func (d *ContentDetector) IsHTMLContent(response []byte, headers http.Header) bool

IsHTMLContent determines if the response contains HTML content based on Content-Type header

func (*ContentDetector) ShouldCache

func (d *ContentDetector) ShouldCache(response []byte, headers http.Header, statusCode int) bool

ShouldCache determines if a response should be cached based on content analysis

type ListenerStats

type ListenerStats struct {
	CacheStats        CacheStats `json:"cache_stats"`
	ActiveConnections int        `json:"active_connections"`
	CacheSize         int        `json:"cache_size"`
	CacheMemoryUsage  uint64     `json:"cache_memory_usage"`
	ListenerAddress   string     `json:"listener_address"`
}

ListenerStats contains comprehensive statistics about the caching listener

type Middleware

type Middleware struct {
	// contains filtered or unexported fields
}

Middleware provides selective HTTP response caching

func New

func New(config Config) *Middleware

New creates a new selective cache middleware with the given configuration

func NewDefault

func NewDefault() *Middleware

NewDefault creates a middleware with default settings: - 15 minute TTL - 5 minute cleanup interval - Excludes HTML content types - Only caches 200 status responses

func (*Middleware) Clear

func (m *Middleware) Clear()

Clear removes all cached responses

func (*Middleware) Delete

func (m *Middleware) Delete(url string)

Delete removes a specific cached response by URL It reconstructs the cache key using the same logic as requests

func (*Middleware) GetCacheForTesting

func (m *Middleware) GetCacheForTesting() *cache.Cache

GetCacheForTesting returns the underlying cache for testing purposes This method should only be used in tests

func (*Middleware) Handler

func (m *Middleware) Handler(next http.Handler) http.Handler

Handler wraps an http.Handler with selective caching

func (*Middleware) HandlerFunc

func (m *Middleware) HandlerFunc(next http.HandlerFunc) http.Handler

HandlerFunc is a convenience method that wraps an http.HandlerFunc

func (*Middleware) Stats

func (m *Middleware) Stats() (itemCount int, hitCount, missCount uint64)

Stats returns cache statistics

type ResponseAnalysis

type ResponseAnalysis struct {
	StatusCode     int           `json:"status_code"`
	ContentType    string        `json:"content_type"`
	Size           int           `json:"size"`
	IsHTML         bool          `json:"is_html"`
	IsCacheable    bool          `json:"is_cacheable"`
	RecommendedTTL time.Duration `json:"recommended_ttl"`
}

ResponseAnalysis contains the results of response content analysis

type ResponseRecorder

type ResponseRecorder struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

ResponseRecorder captures HTTP responses for caching

func NewResponseRecorder

func NewResponseRecorder(w http.ResponseWriter, requestMethod string) *ResponseRecorder

NewResponseRecorder creates a new response recorder

func (*ResponseRecorder) Body

func (r *ResponseRecorder) Body() []byte

Body returns a copy of the recorded response body

func (*ResponseRecorder) Header

func (r *ResponseRecorder) Header() http.Header

Header returns the header map that will be sent by WriteHeader

func (*ResponseRecorder) Headers

func (r *ResponseRecorder) Headers() http.Header

Headers returns a copy of the recorded headers

func (*ResponseRecorder) Size

func (r *ResponseRecorder) Size() int

Size returns the size of the recorded response body

func (*ResponseRecorder) StatusCode

func (r *ResponseRecorder) StatusCode() int

StatusCode returns the recorded status code

func (*ResponseRecorder) Write

func (r *ResponseRecorder) Write(data []byte) (int, error)

Write captures the response body and writes to underlying ResponseWriter

func (*ResponseRecorder) WriteHeader

func (r *ResponseRecorder) WriteHeader(code int)

WriteHeader captures the status code and headers

type TTLCache

type TTLCache struct {
	// contains filtered or unexported fields
}

TTLCache provides thread-safe cache storage with TTL and LRU eviction

func NewTTLCache

func NewTTLCache(config *CacheConfig, metrics *CacheMetrics) *TTLCache

NewTTLCache creates a new TTL cache with the given configuration

func (*TTLCache) Clear

func (c *TTLCache) Clear()

Clear removes all cache entries

func (*TTLCache) Close

func (c *TTLCache) Close()

Close stops the cleanup routine and releases resources

func (*TTLCache) Delete

func (c *TTLCache) Delete(key string) bool

Delete removes a cache entry by key

func (*TTLCache) Get

func (c *TTLCache) Get(key string) (*CacheEntry, bool)

Get retrieves a cached entry by key

func (*TTLCache) MemoryUsage

func (c *TTLCache) MemoryUsage() uint64

MemoryUsage returns the current memory usage in bytes

func (*TTLCache) Set

func (c *TTLCache) Set(key string, data []byte, headers http.Header, ttl time.Duration) error

Set stores a cache entry with the specified TTL

func (*TTLCache) Size

func (c *TTLCache) Size() int

Size returns the current number of entries in the cache

Directories

Path Synopsis
examples
http-server command
tcp-server command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL