Initial commit
This commit is contained in:
117
internal/headers/headers.go
Normal file
117
internal/headers/headers.go
Normal 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
|
||||
}
|
||||
47
internal/headers/headers_test.go
Normal file
47
internal/headers/headers_test.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user