mirror of
https://github.com/soypat/lneto.git
synced 2026-09-08 07:49:05 +00:00
tests passing babyyyyy, header is 112 bytes of pure ecstasy
This commit is contained in:
+46
-84
@@ -88,6 +88,9 @@ func (hb *headerBuf) parseFirstLine(initFlags flags) (method, uri, proto headerS
|
|||||||
|
|
||||||
methodEnd := max(0, bytes.IndexByte(b, ' '))
|
methodEnd := max(0, bytes.IndexByte(b, ' '))
|
||||||
reqURIEnd := bytes.IndexByte(b[methodEnd+1:], ' ')
|
reqURIEnd := bytes.IndexByte(b[methodEnd+1:], ' ')
|
||||||
|
if reqURIEnd >= 0 {
|
||||||
|
reqURIEnd += methodEnd + 1
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case reqURIEnd < 0:
|
case reqURIEnd < 0:
|
||||||
flags |= noHTTP11
|
flags |= noHTTP11
|
||||||
@@ -106,11 +109,7 @@ func (hb *headerBuf) parseFirstLine(initFlags flags) (method, uri, proto headerS
|
|||||||
}
|
}
|
||||||
|
|
||||||
type scannerState struct {
|
type scannerState struct {
|
||||||
err error
|
err error
|
||||||
|
|
||||||
// hLen stores header subslice len
|
|
||||||
hLen int
|
|
||||||
|
|
||||||
disableNormalizing bool
|
disableNormalizing bool
|
||||||
|
|
||||||
// by checking whether the next line contains a colon or not to tell
|
// by checking whether the next line contains a colon or not to tell
|
||||||
@@ -128,7 +127,7 @@ func (h *header) parseHeaders(ss *scannerState) (err error) {
|
|||||||
hb := &h.hbuf
|
hb := &h.hbuf
|
||||||
h.contentLength = -2
|
h.contentLength = -2
|
||||||
|
|
||||||
for kv := hb.nextKV(ss); kv.isValid(); kv = hb.nextKV(ss) {
|
for kv := hb.nextKV2(ss); kv.isValid(); kv = hb.nextKV2(ss) {
|
||||||
if h.flags.hasAny(disableSpecialHeader) {
|
if h.flags.hasAny(disableSpecialHeader) {
|
||||||
h.hbuf.headers = append(h.hbuf.headers, kv)
|
h.hbuf.headers = append(h.hbuf.headers, kv)
|
||||||
continue
|
continue
|
||||||
@@ -271,49 +270,46 @@ func readRawHeaders(dst []byte, buf string) ([]byte, int, error) {
|
|||||||
}
|
}
|
||||||
func (hb *headerBuf) noKV() argsKV { return argsKV{} }
|
func (hb *headerBuf) noKV() argsKV { return argsKV{} }
|
||||||
|
|
||||||
func (hb *headerBuf) nextKV(ss *scannerState) argsKV {
|
func (hb *headerBuf) nextKV2(ss *scannerState) argsKV {
|
||||||
if !ss.initialized {
|
if !ss.initialized {
|
||||||
ss.nextColon = -1
|
ss.nextColon = -1
|
||||||
ss.nextNewLine = -1
|
ss.nextNewLine = -1
|
||||||
ss.initialized = true
|
|
||||||
}
|
}
|
||||||
buf := hb.buf[hb.off:]
|
buf := hb.buf[hb.off:]
|
||||||
bLen := len(buf)
|
blen := len(buf)
|
||||||
if bLen >= 2 && buf[0] == rChar && buf[1] == nChar {
|
if blen >= 2 && buf[0] == '\r' && buf[1] == '\n' {
|
||||||
hb.off += 2
|
hb.off += 2
|
||||||
return hb.noKV() // \r\n\r\n Ends header.
|
return hb.noKV() // \r\n\r\n Ends header.
|
||||||
}
|
} else if blen >= 1 && buf[0] == '\n' {
|
||||||
if bLen >= 1 && buf[0] == nChar {
|
hb.off += 1
|
||||||
hb.off++
|
return hb.noKV() // \n\n Ends header.
|
||||||
return hb.noKV() // \n\n: Ends header.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var n int
|
// n is parsing offset. Will start by storing colon index.
|
||||||
|
n := 0
|
||||||
if ss.nextColon >= 0 {
|
if ss.nextColon >= 0 {
|
||||||
|
// Retake from last colon found.
|
||||||
n = ss.nextColon
|
n = ss.nextColon
|
||||||
ss.nextColon = -1
|
ss.nextColon = -1
|
||||||
} else {
|
} else {
|
||||||
n = bytes.IndexByte(buf, ':')
|
n = bytes.IndexByte(buf, ':')
|
||||||
|
x := bytes.IndexByte(buf, '\n')
|
||||||
// There can't be a \n inside the header name, check for this.
|
|
||||||
x := bytes.IndexByte(buf, nChar)
|
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
// A header name should always at some point be followed by a \n
|
// A header name should always at some point be followed by a \n
|
||||||
// even if it's the one that terminates the header block.
|
// even if it's the one that terminates the header block.
|
||||||
ss.err = errNeedMore
|
ss.err = errNeedMore
|
||||||
return hb.noKV()
|
return hb.noKV()
|
||||||
}
|
} else if x < n {
|
||||||
if x < n {
|
// There was a \n before the colon! This is invalid.
|
||||||
// There was a \n before the :
|
|
||||||
ss.err = errInvalidName
|
ss.err = errInvalidName
|
||||||
return hb.noKV()
|
return hb.noKV()
|
||||||
|
} else if n < 0 {
|
||||||
|
// No colon found, probably missing data.
|
||||||
|
ss.err = errNeedMore
|
||||||
|
return hb.noKV()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n < 0 {
|
// n stores colon position by now.
|
||||||
ss.err = errNeedMore
|
|
||||||
return hb.noKV()
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.IndexByte(buf[:n], ' ') >= 0 || bytes.IndexByte(buf[:n], '\t') >= 0 {
|
if bytes.IndexByte(buf[:n], ' ') >= 0 || bytes.IndexByte(buf[:n], '\t') >= 0 {
|
||||||
// Spaces between the header key and colon are not allowed.
|
// Spaces between the header key and colon are not allowed.
|
||||||
// See RFC 7230, Section 3.2.4.
|
// See RFC 7230, Section 3.2.4.
|
||||||
@@ -321,72 +317,38 @@ func (hb *headerBuf) nextKV(ss *scannerState) argsKV {
|
|||||||
return hb.noKV()
|
return hb.noKV()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ready to store key..
|
||||||
var resultKV argsKV
|
var resultKV argsKV
|
||||||
resultKV.key = hb.slice(buf[:n])
|
resultKV.key = hb.slice(buf[:n])
|
||||||
normalizeHeaderKey(buf[:n], ss.disableNormalizing)
|
normalizeHeaderKey(buf[:n], ss.disableNormalizing)
|
||||||
n++
|
n++ // consume colon.
|
||||||
for len(buf) > n && buf[n] == ' ' {
|
for len(buf) > n && buf[n] == ' ' {
|
||||||
n++
|
n++ // Trim leading spaces.
|
||||||
// the newline index is a relative index, and lines below trimmed `s.b` by `n`,
|
|
||||||
// so the relative newline index also shifted forward. it's safe to decrease
|
|
||||||
// to a minus value, it means it's invalid, and will find the newline again.
|
|
||||||
ss.nextNewLine--
|
|
||||||
}
|
}
|
||||||
ss.hLen += n
|
// n now points to start of value.
|
||||||
buf = buf[n:]
|
valueStart := n
|
||||||
if ss.nextNewLine >= 0 {
|
|
||||||
n = ss.nextNewLine
|
|
||||||
ss.nextNewLine = -1
|
|
||||||
} else {
|
|
||||||
n = bytes.IndexByte(buf, nChar)
|
|
||||||
}
|
|
||||||
if n < 0 {
|
|
||||||
ss.err = errNeedMore
|
|
||||||
return hb.noKV()
|
|
||||||
}
|
|
||||||
isMultiLineValue := false
|
|
||||||
for {
|
|
||||||
if n+1 >= len(buf) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if buf[n+1] != ' ' && buf[n+1] != '\t' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
d := bytes.IndexByte(buf[n+1:], nChar)
|
|
||||||
if d <= 0 {
|
|
||||||
break
|
|
||||||
} else if d == 1 && buf[n+1] == rChar {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
e := n + d + 1
|
|
||||||
if c := bytes.IndexByte(buf[n+1:e], ':'); c >= 0 {
|
|
||||||
ss.nextColon = c
|
|
||||||
ss.nextNewLine = d - c - 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
isMultiLineValue = true
|
|
||||||
n = e
|
|
||||||
}
|
|
||||||
if n >= len(buf) {
|
|
||||||
ss.err = errNeedMore
|
|
||||||
return hb.noKV()
|
|
||||||
}
|
|
||||||
oldB := buf
|
|
||||||
value := buf[:n]
|
|
||||||
ss.hLen += n + 1
|
|
||||||
buf = buf[n+1:]
|
|
||||||
|
|
||||||
if n > 0 && value[n-1] == rChar {
|
// Find end of value. Values may be multiline, in which case we must treat newlines followed by whitespace as part of the value.
|
||||||
n--
|
for {
|
||||||
|
nl := bytes.IndexByte(buf[n:], '\n')
|
||||||
|
if nl < 0 || nl+n+1 == len(buf) {
|
||||||
|
// No newline or newline is last character and can't know if is multiline.
|
||||||
|
ss.err = errNeedMore
|
||||||
|
return hb.noKV()
|
||||||
|
}
|
||||||
|
n += nl + 1 // Index of the newly found newline.
|
||||||
|
nextChar := buf[n]
|
||||||
|
if nextChar != ' ' && nextChar != '\t' {
|
||||||
|
break // End of value found.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for n > 0 && value[n-1] == ' ' {
|
|
||||||
n--
|
valueEnd := n - 1 // Trim newline.
|
||||||
|
if valueEnd > valueStart && buf[valueEnd-1] == '\r' {
|
||||||
|
valueEnd-- // Trim \r character if present before value.
|
||||||
}
|
}
|
||||||
value = value[:n]
|
resultKV.value = hb.slice(buf[valueStart:valueEnd])
|
||||||
if isMultiLineValue {
|
hb.off += n
|
||||||
value, buf, ss.hLen = normalizeHeaderValue(value, oldB, ss.hLen)
|
|
||||||
}
|
|
||||||
resultKV.value = hb.slice(value)
|
|
||||||
return resultKV
|
return resultKV
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package httpx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHeaderParseRequest(t *testing.T) {
|
||||||
|
const (
|
||||||
|
wantMethod = "GET"
|
||||||
|
wantURI = "/"
|
||||||
|
wantMessage = "hello world!"
|
||||||
|
)
|
||||||
|
req, err := http.NewRequest(wantMethod, wantURI, strings.NewReader(wantMessage))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
req.Write(&buf)
|
||||||
|
var hdr header
|
||||||
|
err = hdr.ParseBytes(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !hdr.MethodIs(wantMethod) {
|
||||||
|
t.Errorf("want method %s, got %q", wantMethod, hdr.Method())
|
||||||
|
}
|
||||||
|
if !bytes.Equal(hdr.RequestURI(), []byte(wantURI)) {
|
||||||
|
t.Errorf("want request URI %q, got %q", wantURI, hdr.RequestURI())
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-2
@@ -45,8 +45,8 @@ func (tb headerBuf) musttoken(slice headerSlice) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tb headerBuf) slice(b []byte) headerSlice {
|
func (tb headerBuf) slice(b []byte) headerSlice {
|
||||||
base := uintptr(unsafe.Pointer(&tb.buf[0]))
|
base := uintptr(unsafe.Pointer(unsafe.SliceData(tb.buf)))
|
||||||
off := uintptr(unsafe.Pointer(&b[0]))
|
off := uintptr(unsafe.Pointer(unsafe.SliceData(b)))
|
||||||
if off < base || off > base+uintptr(len(tb.buf)) {
|
if off < base || off > base+uintptr(len(tb.buf)) {
|
||||||
panic("httpx: argument buffer does not alias header buffer")
|
panic("httpx: argument buffer does not alias header buffer")
|
||||||
}
|
}
|
||||||
@@ -87,6 +87,12 @@ type header struct {
|
|||||||
flags flags
|
flags flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *header) ParseBytes(b []byte) error {
|
||||||
|
h.resetSkipNormalize()
|
||||||
|
h.hbuf.readFromBytes(b)
|
||||||
|
return h.parse()
|
||||||
|
}
|
||||||
|
|
||||||
func (h *header) Set(key, value string) {
|
func (h *header) Set(key, value string) {
|
||||||
h.SetCanonical(key, value) //TODO: implement non-canonical.
|
h.SetCanonical(key, value) //TODO: implement non-canonical.
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user