mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-26 06:11:49 +00:00
enhancements - Add adaptive HTTP response handling with automatic streaming for large responses (>1MB) - Implement zero-copy streaming using buffer pools for better performance - Add compression module for reduced bandwidth usage - Add GitHub Container Registry workflow for automated Docker builds - Add production-optimized Dockerfile and docker-compose configuration - Simplify background mode with -d flag and improved daemon management - Update documentation with new command syntax and deployment guides - Clean up unused code and improve error handling - Fix lipgloss style usage (remove unnecessary .Copy() calls)
151 lines
4.2 KiB
Go
151 lines
4.2 KiB
Go
package hpack
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// StaticTable implements the HPACK static table (RFC 7541 Appendix A)
|
|
// The static table is predefined and never changes
|
|
type StaticTable struct {
|
|
entries []HeaderField
|
|
nameMap map[string][]uint32 // Maps name to list of indices
|
|
}
|
|
|
|
var (
|
|
staticTableInstance *StaticTable
|
|
staticTableOnce sync.Once
|
|
)
|
|
|
|
// GetStaticTable returns the singleton static table instance
|
|
func GetStaticTable() *StaticTable {
|
|
staticTableOnce.Do(func() {
|
|
staticTableInstance = newStaticTable()
|
|
})
|
|
return staticTableInstance
|
|
}
|
|
|
|
// newStaticTable creates and initializes the static table
|
|
func newStaticTable() *StaticTable {
|
|
// RFC 7541 Appendix A - Static Table Definition
|
|
// We include the most common headers for HTTP
|
|
entries := []HeaderField{
|
|
{Name: ":authority", Value: ""},
|
|
{Name: ":method", Value: "GET"},
|
|
{Name: ":method", Value: "POST"},
|
|
{Name: ":path", Value: "/"},
|
|
{Name: ":path", Value: "/index.html"},
|
|
{Name: ":scheme", Value: "http"},
|
|
{Name: ":scheme", Value: "https"},
|
|
{Name: ":status", Value: "200"},
|
|
{Name: ":status", Value: "204"},
|
|
{Name: ":status", Value: "206"},
|
|
{Name: ":status", Value: "304"},
|
|
{Name: ":status", Value: "400"},
|
|
{Name: ":status", Value: "404"},
|
|
{Name: ":status", Value: "500"},
|
|
{Name: "accept-charset", Value: ""},
|
|
{Name: "accept-encoding", Value: "gzip, deflate"},
|
|
{Name: "accept-language", Value: ""},
|
|
{Name: "accept-ranges", Value: ""},
|
|
{Name: "accept", Value: ""},
|
|
{Name: "access-control-allow-origin", Value: ""},
|
|
{Name: "age", Value: ""},
|
|
{Name: "allow", Value: ""},
|
|
{Name: "authorization", Value: ""},
|
|
{Name: "cache-control", Value: ""},
|
|
{Name: "content-disposition", Value: ""},
|
|
{Name: "content-encoding", Value: ""},
|
|
{Name: "content-language", Value: ""},
|
|
{Name: "content-length", Value: ""},
|
|
{Name: "content-location", Value: ""},
|
|
{Name: "content-range", Value: ""},
|
|
{Name: "content-type", Value: ""},
|
|
{Name: "cookie", Value: ""},
|
|
{Name: "date", Value: ""},
|
|
{Name: "etag", Value: ""},
|
|
{Name: "expect", Value: ""},
|
|
{Name: "expires", Value: ""},
|
|
{Name: "from", Value: ""},
|
|
{Name: "host", Value: ""},
|
|
{Name: "if-match", Value: ""},
|
|
{Name: "if-modified-since", Value: ""},
|
|
{Name: "if-none-match", Value: ""},
|
|
{Name: "if-range", Value: ""},
|
|
{Name: "if-unmodified-since", Value: ""},
|
|
{Name: "last-modified", Value: ""},
|
|
{Name: "link", Value: ""},
|
|
{Name: "location", Value: ""},
|
|
{Name: "max-forwards", Value: ""},
|
|
{Name: "proxy-authenticate", Value: ""},
|
|
{Name: "proxy-authorization", Value: ""},
|
|
{Name: "range", Value: ""},
|
|
{Name: "referer", Value: ""},
|
|
{Name: "refresh", Value: ""},
|
|
{Name: "retry-after", Value: ""},
|
|
{Name: "server", Value: ""},
|
|
{Name: "set-cookie", Value: ""},
|
|
{Name: "strict-transport-security", Value: ""},
|
|
{Name: "transfer-encoding", Value: ""},
|
|
{Name: "user-agent", Value: ""},
|
|
{Name: "vary", Value: ""},
|
|
{Name: "via", Value: ""},
|
|
{Name: "www-authenticate", Value: ""},
|
|
}
|
|
|
|
// Build name index map
|
|
nameMap := make(map[string][]uint32)
|
|
for i, entry := range entries {
|
|
nameMap[entry.Name] = append(nameMap[entry.Name], uint32(i))
|
|
}
|
|
|
|
return &StaticTable{
|
|
entries: entries,
|
|
nameMap: nameMap,
|
|
}
|
|
}
|
|
|
|
// Get retrieves a header field by index (0-based)
|
|
func (st *StaticTable) Get(index uint32) (string, string, error) {
|
|
if index >= uint32(len(st.entries)) {
|
|
return "", "", fmt.Errorf("index %d out of range (static table size: %d)", index, len(st.entries))
|
|
}
|
|
|
|
field := st.entries[index]
|
|
return field.Name, field.Value, nil
|
|
}
|
|
|
|
// FindExact searches for an exact match (name and value)
|
|
// Returns the index (0-based) and true if found
|
|
func (st *StaticTable) FindExact(name, value string) (uint32, bool) {
|
|
indices, exists := st.nameMap[name]
|
|
if !exists {
|
|
return 0, false
|
|
}
|
|
|
|
for _, index := range indices {
|
|
field := st.entries[index]
|
|
if field.Value == value {
|
|
return index, true
|
|
}
|
|
}
|
|
|
|
return 0, false
|
|
}
|
|
|
|
// FindName searches for a name match
|
|
// Returns the first matching index (0-based) and true if found
|
|
func (st *StaticTable) FindName(name string) (uint32, bool) {
|
|
indices, exists := st.nameMap[name]
|
|
if !exists || len(indices) == 0 {
|
|
return 0, false
|
|
}
|
|
|
|
return indices[0], true
|
|
}
|
|
|
|
// Size returns the number of entries in the static table
|
|
func (st *StaticTable) Size() int {
|
|
return len(st.entries)
|
|
}
|