mirror of
https://github.com/soypat/lneto.git
synced 2026-07-26 10:38:47 +00:00
more tests, run go generate
This commit is contained in:
+4
-4
@@ -17,9 +17,9 @@ const _Operation_name = "requestreply"
|
||||
var _Operation_index = [...]uint8{0, 7, 12}
|
||||
|
||||
func (i Operation) String() string {
|
||||
i -= 1
|
||||
if i >= Operation(len(_Operation_index)-1) {
|
||||
return "Operation(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
idx := int(i) - 1
|
||||
if i < 1 || idx >= len(_Operation_index)-1 {
|
||||
return "Operation(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Operation_name[_Operation_index[i]:_Operation_index[i+1]]
|
||||
return _Operation_name[_Operation_index[idx]:_Operation_index[idx+1]]
|
||||
}
|
||||
|
||||
@@ -106,10 +106,11 @@ const _Op_name = "undefinedrequestreply"
|
||||
var _Op_index = [...]uint8{0, 9, 16, 21}
|
||||
|
||||
func (i Op) String() string {
|
||||
if i >= Op(len(_Op_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_Op_index)-1 {
|
||||
return "Op(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Op_name[_Op_index[i]:_Op_index[i+1]]
|
||||
return _Op_name[_Op_index[idx]:_Op_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -131,10 +132,11 @@ const _MessageType_name = "undefineddiscoverofferrequestdeclineacknakreleaseinfo
|
||||
var _MessageType_index = [...]uint8{0, 9, 17, 22, 29, 36, 39, 42, 49, 55}
|
||||
|
||||
func (i MessageType) String() string {
|
||||
if i >= MessageType(len(_MessageType_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_MessageType_index)-1 {
|
||||
return "MessageType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _MessageType_name[_MessageType_index[i]:_MessageType_index[i+1]]
|
||||
return _MessageType_name[_MessageType_index[idx]:_MessageType_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -155,9 +157,9 @@ const _ClientState_name = "initselectingrequestingboundrenewingrebindinginit-reb
|
||||
var _ClientState_index = [...]uint8{0, 4, 13, 23, 28, 36, 45, 56, 65}
|
||||
|
||||
func (i ClientState) String() string {
|
||||
i -= 1
|
||||
if i >= ClientState(len(_ClientState_index)-1) {
|
||||
return "ClientState(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
idx := int(i) - 1
|
||||
if i < 1 || idx >= len(_ClientState_index)-1 {
|
||||
return "ClientState(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _ClientState_name[_ClientState_index[i]:_ClientState_index[i+1]]
|
||||
return _ClientState_name[_ClientState_index[idx]:_ClientState_index[idx+1]]
|
||||
}
|
||||
|
||||
+15
-14
@@ -28,11 +28,11 @@ const _MsgType_name = "solicitadvertiserequestconfirmrenewrebindreplyreleasedecl
|
||||
var _MsgType_index = [...]uint8{0, 7, 16, 23, 30, 35, 41, 46, 53, 60, 71, 85, 95, 105}
|
||||
|
||||
func (i MsgType) String() string {
|
||||
i -= 1
|
||||
if i >= MsgType(len(_MsgType_index)-1) {
|
||||
return "MsgType(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
idx := int(i) - 1
|
||||
if i < 1 || idx >= len(_MsgType_index)-1 {
|
||||
return "MsgType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _MsgType_name[_MsgType_index[i]:_MsgType_index[i+1]]
|
||||
return _MsgType_name[_MsgType_index[idx]:_MsgType_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -51,11 +51,11 @@ const _ClientState_name = "initsolicitingrequestingboundrenewingrebinding"
|
||||
var _ClientState_index = [...]uint8{0, 4, 14, 24, 29, 37, 46}
|
||||
|
||||
func (i ClientState) String() string {
|
||||
i -= 1
|
||||
if i >= ClientState(len(_ClientState_index)-1) {
|
||||
return "ClientState(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
idx := int(i) - 1
|
||||
if i < 1 || idx >= len(_ClientState_index)-1 {
|
||||
return "ClientState(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _ClientState_name[_ClientState_index[i]:_ClientState_index[i+1]]
|
||||
return _ClientState_name[_ClientState_index[idx]:_ClientState_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -134,10 +134,11 @@ const _StatusCode_name = "successunspec-failno-addrs-availno-bindingnot-on-linku
|
||||
var _StatusCode_index = [...]uint8{0, 7, 18, 32, 42, 53, 66}
|
||||
|
||||
func (i StatusCode) String() string {
|
||||
if i >= StatusCode(len(_StatusCode_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_StatusCode_index)-1 {
|
||||
return "StatusCode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _StatusCode_name[_StatusCode_index[i]:_StatusCode_index[i+1]]
|
||||
return _StatusCode_name[_StatusCode_index[idx]:_StatusCode_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -153,9 +154,9 @@ const _DUIDType_name = "duid-lltduid-enduid-ll"
|
||||
var _DUIDType_index = [...]uint8{0, 8, 15, 22}
|
||||
|
||||
func (i DUIDType) String() string {
|
||||
i -= 1
|
||||
if i >= DUIDType(len(_DUIDType_index)-1) {
|
||||
return "DUIDType(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
idx := int(i) - 1
|
||||
if i < 1 || idx >= len(_DUIDType_index)-1 {
|
||||
return "DUIDType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _DUIDType_name[_DUIDType_index[i]:_DUIDType_index[i+1]]
|
||||
return _DUIDType_name[_DUIDType_index[idx]:_DUIDType_index[idx+1]]
|
||||
}
|
||||
|
||||
+6
-4
@@ -119,10 +119,11 @@ const _RCode_name = "successformat errorserver failurename errornot implementedr
|
||||
var _RCode_index = [...]uint8{0, 7, 19, 33, 43, 58, 65}
|
||||
|
||||
func (i RCode) String() string {
|
||||
if i >= RCode(len(_RCode_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_RCode_index)-1 {
|
||||
return "RCode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _RCode_name[_RCode_index[i]:_RCode_index[i+1]]
|
||||
return _RCode_name[_RCode_index[idx]:_RCode_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -138,8 +139,9 @@ const _OpCode_name = "Standard queryInverse queryServer status request"
|
||||
var _OpCode_index = [...]uint8{0, 14, 27, 48}
|
||||
|
||||
func (i OpCode) String() string {
|
||||
if i >= OpCode(len(_OpCode_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_OpCode_index)-1 {
|
||||
return "OpCode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _OpCode_name[_OpCode_index[i]:_OpCode_index[i+1]]
|
||||
return _OpCode_name[_OpCode_index[idx]:_OpCode_index[idx+1]]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
package httphi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
func nopBackoff(consecutiveBackoffs uint) time.Duration { return lneto.BackoffFlagNop }
|
||||
|
||||
// newExchange returns an Exchange acquired on conn, ready to serve a request.
|
||||
func newExchange(t *testing.T, conn conn, bufferSize int, normalizeKeys bool) *Exchange {
|
||||
t.Helper()
|
||||
exch := new(Exchange)
|
||||
exch.Configure(make([]byte, 2*bufferSize), bufferSize, normalizeKeys)
|
||||
if !exch.Acquire(conn) {
|
||||
t.Fatal("fresh exchange failed to acquire connection")
|
||||
}
|
||||
return exch
|
||||
}
|
||||
|
||||
// serve runs a single exchange to completion on the calling goroutine.
|
||||
func serve(t *testing.T, request string, mux Mux) *rwconn {
|
||||
t.Helper()
|
||||
const bufferSize = 1024
|
||||
conn := newConn(request)
|
||||
conn.Hangup() // Whole request already pending, nothing more will arrive.
|
||||
exch := newExchange(t, conn, bufferSize, false)
|
||||
err := Handle(exch, mux, nopBackoff)
|
||||
if err != nil {
|
||||
t.Fatalf("Handle(%q): %s", request, err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
// WriteHeader must emit a complete status line terminated in CRLF followed by
|
||||
// the end-of-headers CRLF, for every status code including the longest text.
|
||||
func TestExchangeWriteHeader(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
code int
|
||||
want string
|
||||
}{
|
||||
{code: 200, want: "HTTP/1.1 200 OK\r\n\r\n"},
|
||||
{code: 404, want: "HTTP/1.1 404 Not Found\r\n\r\n"},
|
||||
{code: 500, want: "HTTP/1.1 500 Internal Server Error\r\n\r\n"},
|
||||
// Longest status text in status.go: worst case for the status line buffer.
|
||||
{code: 511, want: "HTTP/1.1 511 Network Authentication Required\r\n\r\n"},
|
||||
} {
|
||||
exch := newExchange(t, newConn(""), 128, false)
|
||||
exch.WriteHeader(test.code)
|
||||
if got := exch.rw.(*rwconn).ViewWritten(); got != test.want {
|
||||
t.Errorf("code %d: want %q, got %q", test.code, test.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Status line is written once: a second WriteHeader must not reach the wire.
|
||||
func TestExchangeWriteHeaderOnce(t *testing.T) {
|
||||
conn := newConn("")
|
||||
exch := newExchange(t, conn, 128, false)
|
||||
exch.WriteHeader(404)
|
||||
exch.WriteHeader(500)
|
||||
const want = "HTTP/1.1 404 Not Found\r\n\r\n"
|
||||
if got := conn.ViewWritten(); got != want {
|
||||
t.Errorf("want %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
// Write with no prior WriteHeader must flush a 200 header ahead of the body.
|
||||
func TestExchangeWriteFlushesHeader(t *testing.T) {
|
||||
const body = "hello"
|
||||
conn := newConn("")
|
||||
exch := newExchange(t, conn, 128, false)
|
||||
n, err := exch.Write([]byte(body))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != len(body) {
|
||||
t.Errorf("want %d bytes written, got %d", len(body), n)
|
||||
}
|
||||
const want = "HTTP/1.1 200 OK\r\n\r\n" + body
|
||||
if got := conn.ViewWritten(); got != want {
|
||||
t.Errorf("want %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeSetHeader(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
normalize bool
|
||||
set [][2]string
|
||||
want string // Header block emitted after the status line.
|
||||
}{
|
||||
{name: "none", want: "\r\n"},
|
||||
{name: "single", set: [][2]string{{"Content-Type", "text/plain"}}, want: "Content-Type:text/plain\r\n\r\n"},
|
||||
{
|
||||
name: "multiple",
|
||||
set: [][2]string{{"Content-Type", "text/plain"}, {"Content-Length", "5"}},
|
||||
want: "Content-Type:text/plain\r\nContent-Length:5\r\n\r\n",
|
||||
},
|
||||
{
|
||||
name: "normalized key",
|
||||
normalize: true,
|
||||
set: [][2]string{{"content-TYPE", "text/plain"}},
|
||||
want: "Content-Type:text/plain\r\n\r\n",
|
||||
},
|
||||
{
|
||||
name: "key kept verbatim when not normalizing",
|
||||
set: [][2]string{{"content-TYPE", "text/plain"}},
|
||||
want: "content-TYPE:text/plain\r\n\r\n",
|
||||
},
|
||||
{name: "empty value", set: [][2]string{{"X-Empty", ""}}, want: "X-Empty:\r\n\r\n"},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
conn := newConn("")
|
||||
exch := newExchange(t, conn, 128, test.normalize)
|
||||
for _, kv := range test.set {
|
||||
if !exch.SetHeader(kv[0], kv[1]) {
|
||||
t.Fatalf("SetHeader(%q,%q) reported insufficient memory", kv[0], kv[1])
|
||||
}
|
||||
}
|
||||
exch.WriteHeader(200)
|
||||
got, found := strings.CutPrefix(conn.ViewWritten(), "HTTP/1.1 200 OK\r\n")
|
||||
if !found {
|
||||
t.Fatalf("want 200 status line, got %q", conn.ViewWritten())
|
||||
}
|
||||
if got != test.want {
|
||||
t.Errorf("want header block %q, got %q", test.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// SetHeader must refuse to write past the buffer and say so, never panic nor
|
||||
// emit a truncated field.
|
||||
func TestExchangeSetHeaderOOM(t *testing.T) {
|
||||
const bufferSize = 32
|
||||
conn := newConn("")
|
||||
exch := newExchange(t, conn, bufferSize, false)
|
||||
if exch.SetHeader("X-Big", strings.Repeat("v", 4*bufferSize)) {
|
||||
t.Fatal("want insufficient memory reported for oversized header value")
|
||||
}
|
||||
exch.WriteHeader(200)
|
||||
if got := conn.ViewWritten(); strings.Contains(got, "X-Big") {
|
||||
t.Errorf("dropped header must not appear in response, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Request line and fields the handler observes, over a spread of well formed
|
||||
// and awkward but legal request headers.
|
||||
func TestHandleRequestFields(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
request string
|
||||
wantMethod string
|
||||
wantURI string
|
||||
wantHost string
|
||||
}{
|
||||
{
|
||||
name: "minimal",
|
||||
request: "GET / HTTP/1.1\r\nHost: h\r\n\r\n",
|
||||
wantMethod: "GET", wantURI: "/", wantHost: "h",
|
||||
},
|
||||
{
|
||||
name: "query string",
|
||||
request: "GET /search?q=go&n=1 HTTP/1.1\r\nHost: h\r\n\r\n",
|
||||
wantMethod: "GET", wantURI: "/search?q=go&n=1", wantHost: "h",
|
||||
},
|
||||
{
|
||||
name: "no fields",
|
||||
request: "GET /x HTTP/1.1\r\n\r\n",
|
||||
wantMethod: "GET", wantURI: "/x", wantHost: "",
|
||||
},
|
||||
{
|
||||
name: "post",
|
||||
request: "POST /submit HTTP/1.1\r\nHost: h\r\nContent-Length: 0\r\n\r\n",
|
||||
wantMethod: "POST", wantURI: "/submit", wantHost: "h",
|
||||
},
|
||||
{
|
||||
name: "extension method",
|
||||
request: "FROBNICATE / HTTP/1.1\r\nHost: h\r\n\r\n",
|
||||
wantMethod: "FROBNICATE", wantURI: "/", wantHost: "h",
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var gotMethod, gotURI, gotHost string
|
||||
var sm sliceMux
|
||||
sm.Handle(test.wantURI, func(ex *Exchange) {
|
||||
gotMethod = string(ex.RequestMethod())
|
||||
gotURI = string(ex.RequestURI())
|
||||
gotHost = string(ex.RequestHeader("Host"))
|
||||
ex.WriteHeader(200)
|
||||
})
|
||||
serve(t, test.request, &sm)
|
||||
|
||||
if gotMethod != test.wantMethod {
|
||||
t.Errorf("want method %q, got %q", test.wantMethod, gotMethod)
|
||||
}
|
||||
if gotURI != test.wantURI {
|
||||
t.Errorf("want URI %q, got %q", test.wantURI, gotURI)
|
||||
}
|
||||
if gotHost != test.wantHost {
|
||||
t.Errorf("want Host %q, got %q", test.wantHost, gotHost)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Malformed request headers must fail the exchange, never reach a handler.
|
||||
func TestHandleMalformedRequest(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
request string
|
||||
}{
|
||||
{name: "no colon in field", request: "GET / HTTP/1.1\r\nBadFieldNoColon\r\n\r\n"},
|
||||
{name: "no protocol", request: "GET /\r\nHost: h\r\n\r\n"},
|
||||
{name: "empty request line", request: "\r\n\r\n"},
|
||||
{name: "truncated header", request: "GET / HTTP/1.1\r\nHost: h\r\n"},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var handled bool
|
||||
var sm sliceMux
|
||||
sm.Handle("/", func(ex *Exchange) { handled = true })
|
||||
conn := newConn(test.request)
|
||||
conn.Hangup()
|
||||
exch := newExchange(t, conn, 1024, false)
|
||||
if err := Handle(exch, &sm, nopBackoff); err == nil {
|
||||
t.Error("want error on malformed request, got nil")
|
||||
}
|
||||
if handled {
|
||||
t.Error("handler must not run on malformed request")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// No registered handler must yield 404, not an empty response.
|
||||
func TestHandleNoHandler(t *testing.T) {
|
||||
var sm sliceMux
|
||||
sm.Handle("GET /", func(ex *Exchange) { t.Error("handler must not run") })
|
||||
conn := serve(t, "GET /nowhere HTTP/1.1\r\nHost: h\r\n\r\n", &sm)
|
||||
const want = "HTTP/1.1 404 Not Found\r\n\r\n"
|
||||
if got := conn.ViewWritten(); got != want {
|
||||
t.Errorf("want %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
// A handler that writes nothing must still produce a valid response.
|
||||
func TestHandleSilentHandler(t *testing.T) {
|
||||
var sm sliceMux
|
||||
sm.Handle("/", func(ex *Exchange) {})
|
||||
conn := serve(t, "GET / HTTP/1.1\r\nHost: h\r\n\r\n", &sm)
|
||||
const want = "HTTP/1.1 200 OK\r\n\r\n"
|
||||
if got := conn.ViewWritten(); got != want {
|
||||
t.Errorf("want %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
// Body bytes arriving in the same segment as the header must be readable.
|
||||
func TestExchangeReadBody(t *testing.T) {
|
||||
const body = "message body"
|
||||
var got string
|
||||
var readErr error
|
||||
var sm sliceMux
|
||||
sm.Handle("POST /", func(ex *Exchange) {
|
||||
dst := make([]byte, len(body))
|
||||
n, err := ex.ReadBody(dst)
|
||||
got, readErr = string(dst[:n]), err
|
||||
ex.WriteHeader(200)
|
||||
})
|
||||
serve(t, "POST / HTTP/1.1\r\nHost: h\r\nContent-Length: 12\r\n\r\n"+body, &sm)
|
||||
|
||||
if readErr != nil {
|
||||
t.Fatal(readErr)
|
||||
}
|
||||
if got != body {
|
||||
t.Errorf("want body %q, got %q", body, got)
|
||||
}
|
||||
}
|
||||
+16
-2
@@ -1,6 +1,7 @@
|
||||
package httphi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
@@ -16,6 +17,8 @@ import (
|
||||
|
||||
//go:generate stringer -type Method,status -linecomment -output stringers.go
|
||||
|
||||
var errNoRequestProto = errors.New("httphi: request line with no HTTP version")
|
||||
|
||||
type conn = io.ReadWriteCloser
|
||||
|
||||
// Router hosts concurrent safe data.
|
||||
@@ -160,6 +163,13 @@ func Handle(exch *Exchange, mux Mux, backoff lneto.BackoffStrategy) error {
|
||||
exch.respRemains = reqhdr.BufferReceived() - parsed
|
||||
exch.respHeaderOff = uint16(parsed)
|
||||
exch.respHeaderLen = 0
|
||||
if len(reqhdr.Protocol()) == 0 {
|
||||
// Request line with no HTTP version is a HTTP/0.9 simple-request, which
|
||||
// httpraw tolerates. It is not a valid HTTP/1.1 request-line, RFC 9112 3.
|
||||
exch.WriteHeader(int(StatusBadRequest))
|
||||
exch.rw.Close()
|
||||
return errNoRequestProto
|
||||
}
|
||||
// Mux URI.
|
||||
uri := reqhdr.RequestURI()
|
||||
meth := reqhdr.Method()
|
||||
@@ -272,9 +282,13 @@ func (r *Router) info(msg string, attrs ...slog.Attr) {
|
||||
internal.LogAttrs(r.log, slog.LevelInfo, msg, attrs...)
|
||||
}
|
||||
|
||||
// maxStatusLine bounds the response status line: "HTTP/1.1 " + 3 digit code +
|
||||
// " " + longest [StatusText] + CRLF.
|
||||
const maxStatusLine = len("HTTP/1.1 ") + 3 + 1 + len("Network Authentication Required") + 2
|
||||
|
||||
type Exchange struct {
|
||||
used atomic.Bool
|
||||
respTopBuf [32]byte
|
||||
respTopBuf [maxStatusLine]byte
|
||||
respTopWritten uint8
|
||||
|
||||
rawbuf []byte
|
||||
@@ -357,7 +371,7 @@ func (exch *Exchange) StageWriteStatus(code int) {
|
||||
n += copy(exch.respTopBuf[n:], text)
|
||||
exch.respTopBuf[n] = '\r'
|
||||
exch.respTopBuf[n+1] = '\n'
|
||||
exch.respTopWritten = uint8(n)
|
||||
exch.respTopWritten = uint8(n + 2)
|
||||
}
|
||||
|
||||
func (exch *Exchange) WriteHeader(code int) {
|
||||
|
||||
@@ -16,11 +16,16 @@ import (
|
||||
|
||||
// rwconn is a in-memory conn. The router handles connections on another
|
||||
// goroutine so every field is guarded; onClose lets tests await the handler.
|
||||
//
|
||||
// A drained rwconn reads (0,nil) as a live socket with no data pending would,
|
||||
// so a partial request can be completed with AddReadable mid-handling. Tests
|
||||
// that need the peer to hang up call Hangup.
|
||||
type rwconn struct {
|
||||
mu sync.Mutex
|
||||
readable bytes.Buffer
|
||||
written bytes.Buffer
|
||||
closed bool
|
||||
hangup bool
|
||||
onClose chan struct{}
|
||||
deadline time.Time
|
||||
}
|
||||
@@ -33,6 +38,14 @@ func newConn(request string) *rwconn {
|
||||
return r
|
||||
}
|
||||
|
||||
// Hangup makes reads past the pending data return [io.EOF], as a peer that
|
||||
// closed its side of the connection would.
|
||||
func (r *rwconn) Hangup() {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.hangup = true
|
||||
}
|
||||
|
||||
func (r *rwconn) Close() error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
@@ -63,7 +76,10 @@ func (r *rwconn) Read(b []byte) (int, error) {
|
||||
} else if r.deadlineExceeded() {
|
||||
return 0, context.DeadlineExceeded
|
||||
} else if r.readable.Len() == 0 {
|
||||
return 0, io.EOF
|
||||
if r.hangup {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return 0, nil // No data pending, handler backs off and retries.
|
||||
}
|
||||
return r.readable.Read(b)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// Code generated by "stringer -type Method,status -linecomment -output stringers.go"; DO NOT EDIT.
|
||||
|
||||
package httphi
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[MethUndefined-0]
|
||||
_ = x[MethGet-1]
|
||||
_ = x[MethHead-2]
|
||||
_ = x[MethPost-3]
|
||||
_ = x[MethPut-4]
|
||||
_ = x[MethPatch-5]
|
||||
_ = x[MethDelete-6]
|
||||
_ = x[MethConnect-7]
|
||||
_ = x[MethOptions-8]
|
||||
_ = x[MethTrace-9]
|
||||
_ = x[MethUnknown-10]
|
||||
}
|
||||
|
||||
const _Method_name = "undefinedGETHEADPOSTPUTPATCHDELETECONNECTOPTIONSTRACEunknown"
|
||||
|
||||
var _Method_index = [...]uint8{0, 9, 12, 16, 20, 23, 28, 34, 41, 48, 53, 60}
|
||||
|
||||
func (i Method) String() string {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_Method_index)-1 {
|
||||
return "Method(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Method_name[_Method_index[idx]:_Method_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[StatusContinue-100]
|
||||
_ = x[StatusSwitchingProtocols-101]
|
||||
_ = x[StatusProcessing-102]
|
||||
_ = x[StatusEarlyHints-103]
|
||||
_ = x[StatusOK-200]
|
||||
_ = x[StatusCreated-201]
|
||||
_ = x[StatusAccepted-202]
|
||||
_ = x[StatusNonAuthoritativeInfo-203]
|
||||
_ = x[StatusNoContent-204]
|
||||
_ = x[StatusResetContent-205]
|
||||
_ = x[StatusPartialContent-206]
|
||||
_ = x[StatusMultiStatus-207]
|
||||
_ = x[StatusAlreadyReported-208]
|
||||
_ = x[StatusIMUsed-226]
|
||||
_ = x[StatusMultipleChoices-300]
|
||||
_ = x[StatusMovedPermanently-301]
|
||||
_ = x[StatusFound-302]
|
||||
_ = x[StatusSeeOther-303]
|
||||
_ = x[StatusNotModified-304]
|
||||
_ = x[StatusUseProxy-305]
|
||||
_ = x[StatusTemporaryRedirect-307]
|
||||
_ = x[StatusPermanentRedirect-308]
|
||||
_ = x[StatusBadRequest-400]
|
||||
_ = x[StatusUnauthorized-401]
|
||||
_ = x[StatusPaymentRequired-402]
|
||||
_ = x[StatusForbidden-403]
|
||||
_ = x[StatusNotFound-404]
|
||||
_ = x[StatusMethodNotAllowed-405]
|
||||
_ = x[StatusNotAcceptable-406]
|
||||
_ = x[StatusProxyAuthRequired-407]
|
||||
_ = x[StatusRequestTimeout-408]
|
||||
_ = x[StatusConflict-409]
|
||||
_ = x[StatusGone-410]
|
||||
_ = x[StatusLengthRequired-411]
|
||||
_ = x[StatusPreconditionFailed-412]
|
||||
_ = x[StatusRequestEntityTooLarge-413]
|
||||
_ = x[StatusRequestURITooLong-414]
|
||||
_ = x[StatusUnsupportedMediaType-415]
|
||||
_ = x[StatusRequestedRangeNotSatisfiable-416]
|
||||
_ = x[StatusExpectationFailed-417]
|
||||
_ = x[StatusTeapot-418]
|
||||
_ = x[StatusMisdirectedRequest-421]
|
||||
_ = x[StatusUnprocessableEntity-422]
|
||||
_ = x[StatusLocked-423]
|
||||
_ = x[StatusFailedDependency-424]
|
||||
_ = x[StatusTooEarly-425]
|
||||
_ = x[StatusUpgradeRequired-426]
|
||||
_ = x[StatusPreconditionRequired-428]
|
||||
_ = x[StatusTooManyRequests-429]
|
||||
_ = x[StatusRequestHeaderFieldsTooLarge-431]
|
||||
_ = x[StatusUnavailableForLegalReasons-451]
|
||||
_ = x[StatusInternalServerError-500]
|
||||
_ = x[StatusNotImplemented-501]
|
||||
_ = x[StatusBadGateway-502]
|
||||
_ = x[StatusServiceUnavailable-503]
|
||||
_ = x[StatusGatewayTimeout-504]
|
||||
_ = x[StatusHTTPVersionNotSupported-505]
|
||||
_ = x[StatusVariantAlsoNegotiates-506]
|
||||
_ = x[StatusInsufficientStorage-507]
|
||||
_ = x[StatusLoopDetected-508]
|
||||
_ = x[StatusNotExtended-510]
|
||||
_ = x[StatusNetworkAuthenticationRequired-511]
|
||||
}
|
||||
|
||||
const _status_name = "ContinueSwitching ProtocolsProcessingEarly HintsOKCreatedAcceptedNon-Authoritative InformationNo ContentReset ContentPartial ContentMulti-StatusAlready ReportedIM UsedMultiple ChoicesMoved PermanentlyFoundSee OtherNot ModifiedUse ProxyTemporary RedirectPermanent RedirectBad RequestUnauthorizedPayment RequiredForbiddenNot FoundMethod Not AllowedNot AcceptableProxy Authentication RequiredRequest TimeoutConflictGoneLength RequiredPrecondition FailedRequest Entity Too LargeRequest URI Too LongUnsupported Media TypeRequested Range Not SatisfiableExpectation FailedI'm a teapotMisdirected RequestUnprocessable EntityLockedFailed DependencyToo EarlyUpgrade RequiredPrecondition RequiredToo Many RequestsRequest Header Fields Too LargeUnavailable For Legal ReasonsInternal Server ErrorNot ImplementedBad GatewayService UnavailableGateway TimeoutHTTP Version Not SupportedVariant Also NegotiatesInsufficient StorageLoop DetectedNot ExtendedNetwork Authentication Required"
|
||||
|
||||
var _status_map = map[status]string{
|
||||
100: _status_name[0:8],
|
||||
101: _status_name[8:27],
|
||||
102: _status_name[27:37],
|
||||
103: _status_name[37:48],
|
||||
200: _status_name[48:50],
|
||||
201: _status_name[50:57],
|
||||
202: _status_name[57:65],
|
||||
203: _status_name[65:94],
|
||||
204: _status_name[94:104],
|
||||
205: _status_name[104:117],
|
||||
206: _status_name[117:132],
|
||||
207: _status_name[132:144],
|
||||
208: _status_name[144:160],
|
||||
226: _status_name[160:167],
|
||||
300: _status_name[167:183],
|
||||
301: _status_name[183:200],
|
||||
302: _status_name[200:205],
|
||||
303: _status_name[205:214],
|
||||
304: _status_name[214:226],
|
||||
305: _status_name[226:235],
|
||||
307: _status_name[235:253],
|
||||
308: _status_name[253:271],
|
||||
400: _status_name[271:282],
|
||||
401: _status_name[282:294],
|
||||
402: _status_name[294:310],
|
||||
403: _status_name[310:319],
|
||||
404: _status_name[319:328],
|
||||
405: _status_name[328:346],
|
||||
406: _status_name[346:360],
|
||||
407: _status_name[360:389],
|
||||
408: _status_name[389:404],
|
||||
409: _status_name[404:412],
|
||||
410: _status_name[412:416],
|
||||
411: _status_name[416:431],
|
||||
412: _status_name[431:450],
|
||||
413: _status_name[450:474],
|
||||
414: _status_name[474:494],
|
||||
415: _status_name[494:516],
|
||||
416: _status_name[516:547],
|
||||
417: _status_name[547:565],
|
||||
418: _status_name[565:577],
|
||||
421: _status_name[577:596],
|
||||
422: _status_name[596:616],
|
||||
423: _status_name[616:622],
|
||||
424: _status_name[622:639],
|
||||
425: _status_name[639:648],
|
||||
426: _status_name[648:664],
|
||||
428: _status_name[664:685],
|
||||
429: _status_name[685:702],
|
||||
431: _status_name[702:733],
|
||||
451: _status_name[733:762],
|
||||
500: _status_name[762:783],
|
||||
501: _status_name[783:798],
|
||||
502: _status_name[798:809],
|
||||
503: _status_name[809:828],
|
||||
504: _status_name[828:843],
|
||||
505: _status_name[843:869],
|
||||
506: _status_name[869:892],
|
||||
507: _status_name[892:912],
|
||||
508: _status_name[912:925],
|
||||
510: _status_name[925:937],
|
||||
511: _status_name[937:968],
|
||||
}
|
||||
|
||||
func (i status) String() string {
|
||||
if str, ok := _status_map[i]; ok {
|
||||
return str
|
||||
}
|
||||
return "status(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
@@ -33,8 +33,9 @@ const _FieldClass_name = "undefinedsourcedestinationprotocolversiontypesizeflags
|
||||
var _FieldClass_index = [...]uint8{0, 9, 15, 26, 34, 41, 45, 49, 54, 68, 76, 83, 90, 94, 101, 112, 114, 123, 131}
|
||||
|
||||
func (i FieldClass) String() string {
|
||||
if i >= FieldClass(len(_FieldClass_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_FieldClass_index)-1 {
|
||||
return "FieldClass(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _FieldClass_name[_FieldClass_index[i]:_FieldClass_index[i+1]]
|
||||
return _FieldClass_name[_FieldClass_index[idx]:_FieldClass_index[idx+1]]
|
||||
}
|
||||
|
||||
@@ -66,10 +66,11 @@ const _CodeDestinationUnreachable_name = "net unreachablehost unreachableprotoco
|
||||
var _CodeDestinationUnreachable_index = [...]uint8{0, 15, 31, 51, 67, 98, 117}
|
||||
|
||||
func (i CodeDestinationUnreachable) String() string {
|
||||
if i >= CodeDestinationUnreachable(len(_CodeDestinationUnreachable_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_CodeDestinationUnreachable_index)-1 {
|
||||
return "CodeDestinationUnreachable(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _CodeDestinationUnreachable_name[_CodeDestinationUnreachable_index[i]:_CodeDestinationUnreachable_index[i+1]]
|
||||
return _CodeDestinationUnreachable_name[_CodeDestinationUnreachable_index[idx]:_CodeDestinationUnreachable_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -86,8 +87,9 @@ const _CodeRedirect_name = "redirect for networkredirect for hostredirect for To
|
||||
var _CodeRedirect_index = [...]uint8{0, 20, 37, 61, 82}
|
||||
|
||||
func (i CodeRedirect) String() string {
|
||||
if i >= CodeRedirect(len(_CodeRedirect_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_CodeRedirect_index)-1 {
|
||||
return "CodeRedirect(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _CodeRedirect_name[_CodeRedirect_index[i]:_CodeRedirect_index[i+1]]
|
||||
return _CodeRedirect_name[_CodeRedirect_index[idx]:_CodeRedirect_index[idx+1]]
|
||||
}
|
||||
|
||||
@@ -66,10 +66,11 @@ const _CodeDestinationUnreachable_name = "no route to destinationcommunication a
|
||||
var _CodeDestinationUnreachable_index = [...]uint8{0, 23, 64, 94, 113, 129, 172, 199}
|
||||
|
||||
func (i CodeDestinationUnreachable) String() string {
|
||||
if i >= CodeDestinationUnreachable(len(_CodeDestinationUnreachable_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_CodeDestinationUnreachable_index)-1 {
|
||||
return "CodeDestinationUnreachable(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _CodeDestinationUnreachable_name[_CodeDestinationUnreachable_index[i]:_CodeDestinationUnreachable_index[i+1]]
|
||||
return _CodeDestinationUnreachable_name[_CodeDestinationUnreachable_index[idx]:_CodeDestinationUnreachable_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
@@ -85,8 +86,9 @@ const _CodeParameterProblem_name = "erroneous header field encounteredunrecogniz
|
||||
var _CodeParameterProblem_index = [...]uint8{0, 34, 75, 111}
|
||||
|
||||
func (i CodeParameterProblem) String() string {
|
||||
if i >= CodeParameterProblem(len(_CodeParameterProblem_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_CodeParameterProblem_index)-1 {
|
||||
return "CodeParameterProblem(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _CodeParameterProblem_name[_CodeParameterProblem_index[i]:_CodeParameterProblem_index[i+1]]
|
||||
return _CodeParameterProblem_name[_CodeParameterProblem_index[idx]:_CodeParameterProblem_index[idx+1]]
|
||||
}
|
||||
|
||||
@@ -29,8 +29,9 @@ const _LinkMode_name = "down10M-H10M-F100M-H100M-F100M-T41000M-H1000M-F2.5G-F5G-
|
||||
var _LinkMode_index = [...]uint8{0, 4, 9, 14, 20, 26, 33, 40, 47, 53, 57, 62, 67, 72, 78}
|
||||
|
||||
func (i LinkMode) String() string {
|
||||
if i >= LinkMode(len(_LinkMode_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_LinkMode_index)-1 {
|
||||
return "LinkMode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _LinkMode_name[_LinkMode_index[i]:_LinkMode_index[i+1]]
|
||||
return _LinkMode_name[_LinkMode_index[idx]:_LinkMode_index[idx+1]]
|
||||
}
|
||||
|
||||
+4
-3
@@ -212,16 +212,17 @@ func _() {
|
||||
_ = x[ErrAlreadyRegistered-17]
|
||||
_ = x[ErrTruncatedFrame-18]
|
||||
_ = x[ErrMissingHALConfig-19]
|
||||
_ = x[ErrBadState-20]
|
||||
}
|
||||
|
||||
const (
|
||||
_errGeneric_name_0 = "lneto-bug(use build tag \"debugheaplog\")packet droppedincorrect checksumzero source(port/addr)zero destination(port/addr)short bufferbuffer fullinvalid addressunsupportedmismatchmismatched lengthinvalid configuration"
|
||||
_errGeneric_name_1 = "invalid fieldinvalid length fieldresource exhaustedprotocol already registeredtruncated framemissing HAL configuration"
|
||||
_errGeneric_name_1 = "invalid fieldinvalid length fieldresource exhaustedprotocol already registeredtruncated framemissing HAL configurationoperation invalid in current state"
|
||||
)
|
||||
|
||||
var (
|
||||
_errGeneric_index_0 = [...]uint8{0, 39, 53, 71, 93, 120, 132, 143, 158, 169, 177, 194, 215}
|
||||
_errGeneric_index_1 = [...]uint8{0, 13, 33, 51, 78, 93, 118}
|
||||
_errGeneric_index_1 = [...]uint8{0, 13, 33, 51, 78, 93, 118, 152}
|
||||
)
|
||||
|
||||
func (i errGeneric) String() string {
|
||||
@@ -229,7 +230,7 @@ func (i errGeneric) String() string {
|
||||
case 1 <= i && i <= 12:
|
||||
i -= 1
|
||||
return _errGeneric_name_0[_errGeneric_index_0[i]:_errGeneric_index_0[i+1]]
|
||||
case 14 <= i && i <= 19:
|
||||
case 14 <= i && i <= 20:
|
||||
i -= 14
|
||||
return _errGeneric_name_1[_errGeneric_index_1[i]:_errGeneric_index_1[i+1]]
|
||||
default:
|
||||
|
||||
+3
-2
@@ -26,10 +26,11 @@ const _State_name = "CLOSEDLISTENSYN-RECEIVEDSYN-SENTESTABLISHEDFIN-WAIT-1FIN-WA
|
||||
var _State_index = [...]uint8{0, 6, 12, 24, 32, 43, 53, 63, 70, 79, 89, 97}
|
||||
|
||||
func (i State) String() string {
|
||||
if i >= State(len(_State_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_State_index)-1 {
|
||||
return "State(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _State_name[_State_index[i]:_State_index[i+1]]
|
||||
return _State_name[_State_index[idx]:_State_index[idx+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
|
||||
Reference in New Issue
Block a user