Initial commit

This commit is contained in:
2026-04-04 21:27:36 +02:00
commit f21c9cf919
13 changed files with 920 additions and 0 deletions

117
internal/headers/headers.go Normal file
View File

@@ -0,0 +1,117 @@
package headers
import (
"bytes"
"fmt"
"strings"
)
type Headers struct {
headers map[string]string
}
var rn = []byte("\r\n")
func isToken(str []byte) bool {
for _, ch := range str {
found := false
if ch >= 'A' && ch <= 'Z' ||
ch >= 'a' && ch <= 'z' ||
ch >= '0' && ch <= '9' {
found = true
}
switch ch {
case '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~':
found = true
}
if !found {
return false
}
}
return true
}
func NewHeaders() *Headers {
return &Headers{map[string]string{}}
}
func parseHeader(fieldLine []byte) (string, string, error) {
parts := bytes.SplitN(fieldLine, []byte(":"), 2)
if len(parts) != 2 {
return "", "", fmt.Errorf("malformed header")
}
name := parts[0]
value := bytes.TrimSpace(parts[1])
if bytes.HasSuffix(name, []byte(" ")) {
return "", "", fmt.Errorf("malformed header")
}
return string(name), string(value), nil
}
func (h *Headers) Get(name string) (string, bool) {
v, ok := h.headers[strings.ToLower(name)]
return v, ok
}
func (h *Headers) Set(name, value string) {
name = strings.ToLower(name)
if v, ok := h.headers[name]; ok {
h.headers[name] = fmt.Sprintf("%s,%s", v, value)
} else {
h.headers[name] = value
}
}
func (h *Headers) Replace(name, value string) {
name = strings.ToLower(name)
h.headers[name] = value
}
func (h *Headers) Delete(name string) {
name = strings.ToLower(name)
delete(h.headers,name)
}
func (h *Headers) ForEach(cb func(n, v string)) {
for n, v := range h.headers {
cb(n, v)
}
}
func (h Headers) Parse(data []byte) (int, bool, error) {
read := 0
done := false
for {
idx := bytes.Index(data[read:], rn)
if idx == -1 {
break
}
if idx == 0 {
done = true
break
}
name, value, err := parseHeader(data[read : read+idx])
if err != nil {
return 0, false, err
}
if !isToken([]byte(name)) {
return 0, false, fmt.Errorf("malformed header")
}
read += idx + len(rn)
h.Set(name, value)
}
return read, done, nil
}

View File

@@ -0,0 +1,47 @@
package headers
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHeaders(t *testing.T) {
// Test: Valid single header
headers := NewHeaders()
data := []byte("Host: localhost:42069\r\n\r\n")
n, done, err := headers.Parse(data)
require.NoError(t, err)
require.NotNil(t, headers)
host, _ := headers.Get("HoST")
assert.Equal(t, "localhost:42069", host)
assert.Equal(t, 23, n)
assert.True(t, done)
// Test: Invalid spacing header
headers = NewHeaders()
data = []byte(" Host : localhost:42069 \r\n\r\n")
n, done, err = headers.Parse(data)
require.Error(t, err)
assert.Equal(t, 0, n)
assert.False(t, done)
// Test: Invalid spacing header
headers = NewHeaders()
data = []byte("H©st: localhost:42069\r\n\r\n")
n, done, err = headers.Parse(data)
require.Error(t, err)
assert.Equal(t, 0, n)
assert.False(t, done)
// Test: Invalid spacing header
headers = NewHeaders()
data = []byte("Host: localhost:42069\r\nHost: localhost:42069\r\n")
n, done, err = headers.Parse(data)
require.NoError(t, err)
require.NotNil(t, headers)
host, _ = headers.Get("HOST")
assert.Equal(t, "localhost:42069,localhost:42069", host)
assert.False(t, done)
}