mirror of
https://github.com/soypat/lneto.git
synced 2026-08-13 11:23:42 +00:00
more tests, run go generate
This commit is contained in:
@@ -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) + ")"
|
||||
}
|
||||
Reference in New Issue
Block a user