Skip to content

Instantly share code, notes, and snippets.

@scottcagno
Last active September 25, 2023 21:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottcagno/eba6e8ecd18511de3e713f272a313d62 to your computer and use it in GitHub Desktop.
Save scottcagno/eba6e8ecd18511de3e713f272a313d62 to your computer and use it in GitHub Desktop.
Web framework idea...
package app
import (
"fmt"
"log/slog"
"net/http"
"os"
"sort"
"strings"
"sync"
)
type route struct {
method string
pattern string
h Handler
}
func (r route) String() string {
return fmt.Sprintf("route{method=%q, pattern=%q, h: <Handler>}", r.method, r.pattern)
}
type routes []route
func (r routes) Len() int { return len(r) }
func (r routes) Less(i, j int) bool { return r[i].pattern < r[j].pattern }
func (r routes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
type App struct {
rt routes
logger *slog.Logger
pool sync.Pool
}
func (a *App) String() string {
var ss string
for _, r := range a.rt {
ss += fmt.Sprintf("%s\n", r)
}
return ss
}
func New() *App {
app := &App{
rt: make(routes, 0),
logger: slog.New(slog.NewTextHandler(os.Stderr, nil)),
}
app.pool.New = func() any {
return new(Ctx)
}
return app
}
func (a *App) Get(pattern string, h Handler) {
a.add(http.MethodGet, pattern, h)
}
func (a *App) Post(pattern string, h Handler) {
a.add(http.MethodPost, pattern, h)
}
func (a *App) Put(pattern string, h Handler) {
a.add(http.MethodPut, pattern, h)
}
func (a *App) Delete(pattern string, h Handler) {
a.add(http.MethodDelete, pattern, h)
}
func (a *App) add(method, pattern string, h Handler) {
_, exists := a.get(method, pattern)
if exists {
// do not add any duplicates
return
}
a.rt = append(
a.rt, route{
method: method,
pattern: pattern,
h: h,
},
)
if !sort.IsSorted(a.rt) {
sort.Stable(a.rt)
}
}
func (a *App) get(method, pattern string) (Handler, bool) {
i, found := sort.Find(
a.rt.Len(), func(i int) int {
return strings.Compare(pattern, a.rt[i].pattern)
},
)
if !found {
return nil, false
}
if a.rt[i].method != method {
return nil, false
}
return a.rt[i].h, true
}
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(http.StatusBadRequest)
return
}
h, found := a.get(r.Method, r.URL.Path)
if !found {
http.NotFound(w, r)
return
}
c := a.pool.Get().(*Ctx)
c.Reset(w, r)
if err := h.ServeRoute(c); err != nil {
a.logger.Info(err.Error())
}
a.pool.Put(c)
}
func (a *App) Listen(addr string) error {
return http.ListenAndServe(addr, a)
}
func statsHandler(app *App) Handler {
fn := func(c *Ctx) error {
ss := "<h1>Routes</h1>"
for _, r := range app.rt {
ss += fmt.Sprintf("<a href=\"%s\">%s</a>", r.pattern, r.pattern)
ss += "<br>"
}
return c.HTML(http.StatusOK, ss)
}
return HandlerFunc(fn)
}
package app
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/scottcagno/go-zoomenvelopes/pkg/app/old"
)
var routesData = []Route{
{"GET", "/"},
{"GET", "/cmd.html"},
{"GET", "/code.html"},
{"GET", "/contrib.html"},
{"GET", "/contribute.html"},
{"GET", "/debugging_with_gdb.html"},
{"GET", "/docs.html"},
{"GET", "/effective_go.html"},
{"GET", "/files.log"},
{"GET", "/gccgo_contribute.html"},
{"GET", "/gccgo_install.html"},
{"GET", "/go-logo-black.png"},
{"GET", "/go-logo-blue.png"},
{"GET", "/go-logo-white.png"},
{"GET", "/go1.1.html"},
{"GET", "/go1.2.html"},
{"GET", "/go1.html"},
{"GET", "/go1compat.html"},
{"GET", "/go_faq.html"},
{"GET", "/go_mem.html"},
{"GET", "/go_spec.html"},
{"GET", "/help.html"},
{"GET", "/ie.css"},
{"GET", "/install-source.html"},
{"GET", "/install.html"},
{"GET", "/logo-153x55.png"},
{"GET", "/Makefile"},
{"GET", "/root.html"},
{"GET", "/share.png"},
{"GET", "/sieve.gif"},
{"GET", "/tos.html"},
{"GET", "/articles/"},
{"GET", "/articles/go_command.html"},
{"GET", "/articles/index.html"},
{"GET", "/articles/wiki/"},
{"GET", "/articles/wiki/edit.html"},
{"GET", "/articles/wiki/final-noclosure.go"},
{"GET", "/articles/wiki/final-noerror.go"},
{"GET", "/articles/wiki/final-parsetemplate.go"},
{"GET", "/articles/wiki/final-template.go"},
{"GET", "/articles/wiki/final.go"},
{"GET", "/articles/wiki/get.go"},
{"GET", "/articles/wiki/http-sample.go"},
{"GET", "/articles/wiki/index.html"},
{"GET", "/articles/wiki/Makefile"},
{"GET", "/articles/wiki/notemplate.go"},
{"GET", "/articles/wiki/part1-noerror.go"},
{"GET", "/articles/wiki/part1.go"},
{"GET", "/articles/wiki/part2.go"},
{"GET", "/articles/wiki/part3-errorhandling.go"},
{"GET", "/articles/wiki/part3.go"},
{"GET", "/articles/wiki/test.bash"},
{"GET", "/articles/wiki/test_edit.good"},
{"GET", "/articles/wiki/test_Test.txt.good"},
{"GET", "/articles/wiki/test_view.good"},
{"GET", "/articles/wiki/view.html"},
{"GET", "/codewalk/"},
{"GET", "/codewalk/codewalk.css"},
{"GET", "/codewalk/codewalk.js"},
{"GET", "/codewalk/codewalk.xml"},
{"GET", "/codewalk/functions.xml"},
{"GET", "/codewalk/markov.go"},
{"GET", "/codewalk/markov.xml"},
{"GET", "/codewalk/pig.go"},
{"GET", "/codewalk/popout.png"},
{"GET", "/codewalk/run"},
{"GET", "/codewalk/sharemem.xml"},
{"GET", "/codewalk/urlpoll.go"},
{"GET", "/devel/"},
{"GET", "/devel/release.html"},
{"GET", "/devel/weekly.html"},
{"GET", "/gopher/"},
{"GET", "/gopher/appenginegopher.jpg"},
{"GET", "/gopher/appenginegophercolor.jpg"},
{"GET", "/gopher/appenginelogo.gif"},
{"GET", "/gopher/bumper.png"},
{"GET", "/gopher/bumper192x108.png"},
{"GET", "/gopher/bumper320x180.png"},
{"GET", "/gopher/bumper480x270.png"},
{"GET", "/gopher/bumper640x360.png"},
{"GET", "/gopher/doc.png"},
{"GET", "/gopher/frontpage.png"},
{"GET", "/gopher/gopherbw.png"},
{"GET", "/gopher/gophercolor.png"},
{"GET", "/gopher/gophercolor16x16.png"},
{"GET", "/gopher/help.png"},
{"GET", "/gopher/pkg.png"},
{"GET", "/gopher/project.png"},
{"GET", "/gopher/ref.png"},
{"GET", "/gopher/run.png"},
{"GET", "/gopher/talks.png"},
{"GET", "/gopher/pencil/"},
{"GET", "/gopher/pencil/gopherhat.jpg"},
{"GET", "/gopher/pencil/gopherhelmet.jpg"},
{"GET", "/gopher/pencil/gophermega.jpg"},
{"GET", "/gopher/pencil/gopherrunning.jpg"},
{"GET", "/gopher/pencil/gopherswim.jpg"},
{"GET", "/gopher/pencil/gopherswrench.jpg"},
{"GET", "/play/"},
{"GET", "/play/fib.go"},
{"GET", "/play/hello.go"},
{"GET", "/play/life.go"},
{"GET", "/play/peano.go"},
{"GET", "/play/pi.go"},
{"GET", "/play/sieve.go"},
{"GET", "/play/solitaire.go"},
{"GET", "/play/tree.go"},
{"GET", "/progs/"},
{"GET", "/progs/cgo1.go"},
{"GET", "/progs/cgo2.go"},
{"GET", "/progs/cgo3.go"},
{"GET", "/progs/cgo4.go"},
{"GET", "/progs/defer.go"},
{"GET", "/progs/defer.out"},
{"GET", "/progs/defer2.go"},
{"GET", "/progs/defer2.out"},
{"GET", "/progs/eff_bytesize.go"},
{"GET", "/progs/eff_bytesize.out"},
{"GET", "/progs/eff_qr.go"},
{"GET", "/progs/eff_sequence.go"},
{"GET", "/progs/eff_sequence.out"},
{"GET", "/progs/eff_unused1.go"},
{"GET", "/progs/eff_unused2.go"},
{"GET", "/progs/error.go"},
{"GET", "/progs/error2.go"},
{"GET", "/progs/error3.go"},
{"GET", "/progs/error4.go"},
{"GET", "/progs/go1.go"},
{"GET", "/progs/gobs1.go"},
{"GET", "/progs/gobs2.go"},
{"GET", "/progs/image_draw.go"},
{"GET", "/progs/image_package1.go"},
{"GET", "/progs/image_package1.out"},
{"GET", "/progs/image_package2.go"},
{"GET", "/progs/image_package2.out"},
{"GET", "/progs/image_package3.go"},
{"GET", "/progs/image_package3.out"},
{"GET", "/progs/image_package4.go"},
{"GET", "/progs/image_package4.out"},
{"GET", "/progs/image_package5.go"},
{"GET", "/progs/image_package5.out"},
{"GET", "/progs/image_package6.go"},
{"GET", "/progs/image_package6.out"},
{"GET", "/progs/interface.go"},
{"GET", "/progs/interface2.go"},
{"GET", "/progs/interface2.out"},
{"GET", "/progs/json1.go"},
{"GET", "/progs/json2.go"},
{"GET", "/progs/json2.out"},
{"GET", "/progs/json3.go"},
{"GET", "/progs/json4.go"},
{"GET", "/progs/json5.go"},
{"GET", "/progs/run"},
{"GET", "/progs/slices.go"},
{"GET", "/progs/timeout1.go"},
{"GET", "/progs/timeout2.go"},
{"GET", "/progs/update.bash"},
}
type Route struct {
Method string
Path string
}
func bench(b *testing.B, router http.Handler, routes []Route) {
r := httptest.NewRequest("GET", "/", nil)
u := r.URL
w := httptest.NewRecorder()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, route := range routes {
r.Method = route.Method
u.Path = route.Path
router.ServeHTTP(w, r)
code := w.Result().StatusCode
if code != 200 {
b.Errorf("wanted \"200 OK\", got \"%d %s\"\n", code, http.StatusText(code))
}
}
}
}
func loadStdLibRoutes(mux *http.ServeMux, routes []Route) {
method := func(method string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if r.Method != method {
http.Error(w, http.StatusText(405), 405)
return
}
r.Header.Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("OK"))
if err != nil {
http.Error(w, http.StatusText(400), 400)
return
}
}
return http.HandlerFunc(fn)
}
for _, r := range routes {
switch r.Method {
case "GET":
mux.Handle(r.Path, method(http.MethodGet))
case "POST":
mux.Handle(r.Path, method(http.MethodPost))
case "PUT":
mux.Handle(r.Path, method(http.MethodPut))
case "DELETE":
mux.Handle(r.Path, method(http.MethodDelete))
}
}
}
func loadOldAppRoutes(mux *old.Router, routes []Route) {
handler := func() old.RouteHandler {
return old.RouteHandlerFunc(
func(c *old.Ctx) error {
return c.String(http.StatusOK, "OK")
},
)
}
for _, r := range routes {
switch r.Method {
case "GET":
mux.Get(r.Path, handler())
case "POST":
mux.Post(r.Path, handler())
case "PUT":
mux.Put(r.Path, handler())
case "DELETE":
mux.Delete(r.Path, handler())
}
}
}
func loadAppRoutes(mux *App, routes []Route) {
handler := func(c *Ctx) error {
return c.Text(http.StatusOK, "OK")
}
for _, r := range routes {
switch r.Method {
case "GET":
mux.Get(r.Path, HandlerFunc(handler))
case "POST":
mux.Post(r.Path, HandlerFunc(handler))
case "PUT":
mux.Put(r.Path, HandlerFunc(handler))
case "DELETE":
mux.Delete(r.Path, HandlerFunc(handler))
}
}
}
func BenchmarkStdLibRoutes(b *testing.B) {
mux := http.NewServeMux()
loadStdLibRoutes(mux, routesData)
bench(b, mux, routesData)
}
func BenchmarkOldAppRoutes(b *testing.B) {
mux := old.NewRouter()
loadOldAppRoutes(mux, routesData)
bench(b, mux, routesData)
}
func BenchmarkAppRoutes(b *testing.B) {
app := New()
loadAppRoutes(app, routesData)
bench(b, app, routesData)
}
func BenchmarkAll(b *testing.B) {
tests := []struct {
name string
fn func(b *testing.B)
}{
{
"stdlib",
BenchmarkStdLibRoutes,
},
{
"oldapp",
BenchmarkOldAppRoutes,
},
{
"app",
BenchmarkAppRoutes,
},
}
for _, tt := range tests {
b.Run(tt.name, tt.fn)
}
}
package app
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"net/http"
)
type Ctx struct {
w *Response
r *http.Request
}
func (c *Ctx) writeContentType(value string) {
header := c.w.Header()
if header.Get("Content-Type") == "" {
header.Set("Content-Type", value)
}
}
func (c *Ctx) Raw(code int, contentType string, b []byte) (err error) {
c.writeContentType(contentType)
c.w.WriteHeader(code)
_, err = c.w.Write(b)
return
}
func (c *Ctx) WriteHeader(code int, key, val string) {
c.w.Header().Set(key, val)
c.w.WriteHeader(code)
}
func (c *Ctx) Text(code int, data string) error {
//c.WriteHeader(code, "Content-Type", "text/plain")
//_, err := c.w.Write([]byte(data))
//return err
return c.Raw(code, "text/plain", []byte(data))
}
func (c *Ctx) HTML(code int, data string) error {
//c.WriteHeader(code, "Content-Type", "text/html")
//_, err := c.w.Write([]byte(data))
//return err
return c.Raw(code, "text/html", []byte(data))
}
func (c *Ctx) JSON(code int, data any) error {
c.w.Header().Set("content-type", "application/json")
c.w.WriteHeader(code)
b, err := json.Marshal(data)
if err != nil {
return err
}
_, err = c.w.Write(b)
return err
}
func (c *Ctx) XML(code int, data any) error {
c.w.Header().Set("content-type", "application/xml")
c.w.WriteHeader(code)
b, err := xml.Marshal(data)
if err != nil {
return err
}
_, err = c.w.Write(b)
return err
}
func (c *Ctx) Reset(w http.ResponseWriter, r *http.Request) {
if c.w == nil {
c.w = &Response{writer: w}
}
c.w.reset(w)
c.r = r
}
func (c *Ctx) Get(key string) any {
return nil
}
func (c *Ctx) Put(key string, val any) {
}
func (c *Ctx) NoContent(code int) error {
c.w.WriteHeader(code)
return nil
}
func (c *Ctx) Redirect(code int, url string) error {
if code < 300 || code > 308 {
return http.ErrAbortHandler
}
c.w.Header().Set("Content-Location", url)
c.w.WriteHeader(code)
return nil
}
func RedirectHandler(code int, url string) Handler {
return HandlerFunc(
func(c *Ctx) error {
if code < 300 || code > 308 {
return http.ErrBodyNotAllowed
}
c.w.Header().Set("Content-Location", url)
c.w.WriteHeader(code)
return nil
},
)
}
func CodeFromError(err error) int {
return StatusCode(err.Error())
}
func ErrorFromCode(code int) error {
return errors.New(http.StatusText(code))
}
func (c *Ctx) Error(err error) {
ErrorHandler(err, c)
}
func ErrorHandler(err error, c *Ctx) {
w := c.w
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(CodeFromError(err))
fmt.Fprintln(w, err)
}
var NotFound = func(c *Ctx) error {
return ErrorFromCode(http.StatusNotFound)
}
var MethodNotAllowed = func(c *Ctx) error {
return ErrorFromCode(http.StatusMethodNotAllowed)
}
var ExpectationFailed = func(c *Ctx) error {
return ErrorFromCode(http.StatusExpectationFailed)
}
var InternalServerError = func(c *Ctx) error {
return ErrorFromCode(http.StatusInternalServerError)
}
package app
type Handler interface {
ServeRoute(c *Ctx) error
}
type HandlerFunc func(c *Ctx) error
func (h HandlerFunc) ServeRoute(c *Ctx) error {
return h(c)
}
package app
import (
"bufio"
"net"
"net/http"
)
// response wraps a http.ResponseWriter and implements its interface to be
// used by an HTTP handler to construct and HTTP response.
// See [http.ResponseWriter](https://pkg.go.dev/net/http#ResponseWriter)
type Response struct {
writer http.ResponseWriter
status int
size int64
committed bool
}
// newResponse creates and returns a new instance of a *response
func newResponse(w http.ResponseWriter) *Response {
return &Response{writer: w}
}
// Header implements the http.ResponseWriter interface to allow an HTTP
// handler to return the header map for the writer that will be sent by
// WriteHeader. Changing the header after a call to WriteHeader (or Write)
// has no effect unless the modified headers were declared as trailers by
// setting the "Trailer" header before the call to WriteHeader. To suppress
// implicit response headers, set their value to nil.
func (r *Response) Header() http.Header {
return r.writer.Header()
}
// Write implements the http.ResponseWriter interface to allow an HTTP handler
// to write the data to the connection as part of an HTTP reply. If WriteHeader
// has not yet been called, Write calls WriteHeader(http.StatusOK) before
// writing the data. If the Header does not contain a Content-Type line, Write
// adds a Content-Type set to the result of passing the initial 512 bytes of
// written data to DetectContentType. Additionally, if the total size of all
// written data is under a few KB and there are no Flush calls, the Content-Length
// header is added automatically.
func (r *Response) Write(bytes []byte) (int, error) {
if !r.committed {
if r.status == 0 {
r.status = http.StatusOK
}
r.WriteHeader(r.status)
}
n, err := r.writer.Write(bytes)
r.size += int64(n)
return n, err
}
// WriteHeader implements the http.ResponseWriter interface to allow an HTTP
// handler to write a status code to the response. WriteHeader sends an HTTP
// response header with status code. If WriteHeader is not called explicitly,
// the first call to Write will trigger an implicit WriteHeader(http.StatusOK).
// Thus, explicit calls to WriteHeader are mainly use to send error codes.
func (r *Response) WriteHeader(statusCode int) {
if r.committed {
return
}
r.status = statusCode
r.writer.WriteHeader(r.status)
r.committed = true
}
// Flush implements the http.Flusher interface to allow an HTTP handler to flush
// buffered data to the client.
// See [http.Flusher](https://pkg.go.dev/net/http#Flusher)
func (r *Response) Flush() {
r.writer.(http.Flusher).Flush()
}
// Hijack implements the http.Hijacker interface to allow an HTTP handler to
// take over the connection.
// See [http.Hijacker](https://pkg.go.dev/net/http#Hijacker)
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return r.writer.(http.Hijacker).Hijack()
}
// reset is here to reset a response
func (r *Response) reset(w http.ResponseWriter) {
r.writer = w
r.status = http.StatusOK
r.size = 0
r.committed = false
}
package app
const (
StatusContinue = 100 // RFC 9110, 15.2.1
StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2
StatusProcessing = 102 // RFC 2518, 10.1
StatusEarlyHints = 103 // RFC 8297
StatusOK = 200 // RFC 9110, 15.3.1
StatusCreated = 201 // RFC 9110, 15.3.2
StatusAccepted = 202 // RFC 9110, 15.3.3
StatusNonAuthoritativeInfo = 203 // RFC 9110, 15.3.4
StatusNoContent = 204 // RFC 9110, 15.3.5
StatusResetContent = 205 // RFC 9110, 15.3.6
StatusPartialContent = 206 // RFC 9110, 15.3.7
StatusMultiStatus = 207 // RFC 4918, 11.1
StatusAlreadyReported = 208 // RFC 5842, 7.1
StatusIMUsed = 226 // RFC 3229, 10.4.1
StatusMultipleChoices = 300 // RFC 9110, 15.4.1
StatusMovedPermanently = 301 // RFC 9110, 15.4.2
StatusFound = 302 // RFC 9110, 15.4.3
StatusSeeOther = 303 // RFC 9110, 15.4.4
StatusNotModified = 304 // RFC 9110, 15.4.5
StatusUseProxy = 305 // RFC 9110, 15.4.6
_ = 306 // RFC 9110, 15.4.7 (Unused)
StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8
StatusPermanentRedirect = 308 // RFC 9110, 15.4.9
StatusBadRequest = 400 // RFC 9110, 15.5.1
StatusUnauthorized = 401 // RFC 9110, 15.5.2
StatusPaymentRequired = 402 // RFC 9110, 15.5.3
StatusForbidden = 403 // RFC 9110, 15.5.4
StatusNotFound = 404 // RFC 9110, 15.5.5
StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6
StatusNotAcceptable = 406 // RFC 9110, 15.5.7
StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8
StatusRequestTimeout = 408 // RFC 9110, 15.5.9
StatusConflict = 409 // RFC 9110, 15.5.10
StatusGone = 410 // RFC 9110, 15.5.11
StatusLengthRequired = 411 // RFC 9110, 15.5.12
StatusPreconditionFailed = 412 // RFC 9110, 15.5.13
StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14
StatusRequestURITooLong = 414 // RFC 9110, 15.5.15
StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16
StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17
StatusExpectationFailed = 417 // RFC 9110, 15.5.18
StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused)
StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20
StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21
StatusLocked = 423 // RFC 4918, 11.3
StatusFailedDependency = 424 // RFC 4918, 11.4
StatusTooEarly = 425 // RFC 8470, 5.2.
StatusUpgradeRequired = 426 // RFC 9110, 15.5.22
StatusPreconditionRequired = 428 // RFC 6585, 3
StatusTooManyRequests = 429 // RFC 6585, 4
StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
StatusInternalServerError = 500 // RFC 9110, 15.6.1
StatusNotImplemented = 501 // RFC 9110, 15.6.2
StatusBadGateway = 502 // RFC 9110, 15.6.3
StatusServiceUnavailable = 503 // RFC 9110, 15.6.4
StatusGatewayTimeout = 504 // RFC 9110, 15.6.5
StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6
StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1
StatusInsufficientStorage = 507 // RFC 4918, 11.5
StatusLoopDetected = 508 // RFC 5842, 7.2
StatusNotExtended = 510 // RFC 2774, 7
StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)
// StatusText returns a text for the HTTP status code. It returns the empty
// string if the code is unknown.
func StatusText(code int) string {
switch code {
case StatusContinue:
return "Continue"
case StatusSwitchingProtocols:
return "Switching Protocols"
case StatusProcessing:
return "Processing"
case StatusEarlyHints:
return "Early Hints"
case StatusOK:
return "OK"
case StatusCreated:
return "Created"
case StatusAccepted:
return "Accepted"
case StatusNonAuthoritativeInfo:
return "Non-Authoritative Information"
case StatusNoContent:
return "No Content"
case StatusResetContent:
return "Reset Content"
case StatusPartialContent:
return "Partial Content"
case StatusMultiStatus:
return "Multi-Status"
case StatusAlreadyReported:
return "Already Reported"
case StatusIMUsed:
return "IM Used"
case StatusMultipleChoices:
return "Multiple Choices"
case StatusMovedPermanently:
return "Moved Permanently"
case StatusFound:
return "Found"
case StatusSeeOther:
return "See Other"
case StatusNotModified:
return "Not Modified"
case StatusUseProxy:
return "Use Proxy"
case StatusTemporaryRedirect:
return "Temporary Redirect"
case StatusPermanentRedirect:
return "Permanent Redirect"
case StatusBadRequest:
return "Bad Request"
case StatusUnauthorized:
return "Unauthorized"
case StatusPaymentRequired:
return "Payment Required"
case StatusForbidden:
return "Forbidden"
case StatusNotFound:
return "Not Found"
case StatusMethodNotAllowed:
return "Method Not Allowed"
case StatusNotAcceptable:
return "Not Acceptable"
case StatusProxyAuthRequired:
return "Proxy Authentication Required"
case StatusRequestTimeout:
return "Request Timeout"
case StatusConflict:
return "Conflict"
case StatusGone:
return "Gone"
case StatusLengthRequired:
return "Length Required"
case StatusPreconditionFailed:
return "Precondition Failed"
case StatusRequestEntityTooLarge:
return "Request Entity Too Large"
case StatusRequestURITooLong:
return "Request URI Too Long"
case StatusUnsupportedMediaType:
return "Unsupported Media Type"
case StatusRequestedRangeNotSatisfiable:
return "Requested Range Not Satisfiable"
case StatusExpectationFailed:
return "Expectation Failed"
case StatusTeapot:
return "I'm a teapot"
case StatusMisdirectedRequest:
return "Misdirected Request"
case StatusUnprocessableEntity:
return "Unprocessable Entity"
case StatusLocked:
return "Locked"
case StatusFailedDependency:
return "Failed Dependency"
case StatusTooEarly:
return "Too Early"
case StatusUpgradeRequired:
return "Upgrade Required"
case StatusPreconditionRequired:
return "Precondition Required"
case StatusTooManyRequests:
return "Too Many Requests"
case StatusRequestHeaderFieldsTooLarge:
return "Request Header Fields Too Large"
case StatusUnavailableForLegalReasons:
return "Unavailable For Legal Reasons"
case StatusInternalServerError:
return "Internal Server Error"
case StatusNotImplemented:
return "Not Implemented"
case StatusBadGateway:
return "Bad Gateway"
case StatusServiceUnavailable:
return "Service Unavailable"
case StatusGatewayTimeout:
return "Gateway Timeout"
case StatusHTTPVersionNotSupported:
return "HTTP Version Not Supported"
case StatusVariantAlsoNegotiates:
return "Variant Also Negotiates"
case StatusInsufficientStorage:
return "Insufficient Storage"
case StatusLoopDetected:
return "Loop Detected"
case StatusNotExtended:
return "Not Extended"
case StatusNetworkAuthenticationRequired:
return "Network Authentication Required"
default:
return ""
}
}
func StatusCode(status string) int {
switch status {
case "Continue":
return StatusContinue
case "Switching Protocols":
return StatusSwitchingProtocols
case "Processing":
return StatusProcessing
case "Early Hints":
return StatusEarlyHints
case "OK":
return StatusOK
case "Created":
return StatusCreated
case "Accepted":
return StatusAccepted
case "Non-Authoritative Information":
return StatusNonAuthoritativeInfo
case "No Content":
return StatusNoContent
case "Reset Content":
return StatusResetContent
case "Partial Content":
return StatusPartialContent
case "Multi-Status":
return StatusMultiStatus
case "Already Reported":
return StatusAlreadyReported
case "IM Used":
return StatusIMUsed
case "Multiple Choices":
return StatusMultipleChoices
case "Moved Permanently":
return StatusMovedPermanently
case "Found":
return StatusFound
case "See Other":
return StatusSeeOther
/*
case StatusNotModified:
return "Not Modified"
case StatusUseProxy:
return "Use Proxy"
case StatusTemporaryRedirect:
return "Temporary Redirect"
case StatusPermanentRedirect:
return "Permanent Redirect"
case StatusBadRequest:
return "Bad Request"
case StatusUnauthorized:
return "Unauthorized"
case StatusPaymentRequired:
return "Payment Required"
case StatusForbidden:
return "Forbidden"
case StatusNotFound:
return "Not Found"
case StatusMethodNotAllowed:
return "Method Not Allowed"
case StatusNotAcceptable:
return "Not Acceptable"
case StatusProxyAuthRequired:
return "Proxy Authentication Required"
case StatusRequestTimeout:
return "Request Timeout"
case StatusConflict:
return "Conflict"
case StatusGone:
return "Gone"
case StatusLengthRequired:
return "Length Required"
case StatusPreconditionFailed:
return "Precondition Failed"
case StatusRequestEntityTooLarge:
return "Request Entity Too Large"
case StatusRequestURITooLong:
return "Request URI Too Long"
case StatusUnsupportedMediaType:
return "Unsupported Media Type"
case StatusRequestedRangeNotSatisfiable:
return "Requested Range Not Satisfiable"
case StatusExpectationFailed:
return "Expectation Failed"
case StatusTeapot:
return "I'm a teapot"
case StatusMisdirectedRequest:
return "Misdirected Request"
case StatusUnprocessableEntity:
return "Unprocessable Entity"
case StatusLocked:
return "Locked"
case StatusFailedDependency:
return "Failed Dependency"
case StatusTooEarly:
return "Too Early"
case StatusUpgradeRequired:
return "Upgrade Required"
case StatusPreconditionRequired:
return "Precondition Required"
case StatusTooManyRequests:
return "Too Many Requests"
case StatusRequestHeaderFieldsTooLarge:
return "Request Header Fields Too Large"
case StatusUnavailableForLegalReasons:
return "Unavailable For Legal Reasons"
case StatusInternalServerError:
return "Internal Server Error"
case StatusNotImplemented:
return "Not Implemented"
case StatusBadGateway:
return "Bad Gateway"
case StatusServiceUnavailable:
return "Service Unavailable"
case StatusGatewayTimeout:
return "Gateway Timeout"
case StatusHTTPVersionNotSupported:
return "HTTP Version Not Supported"
case StatusVariantAlsoNegotiates:
return "Variant Also Negotiates"
case StatusInsufficientStorage:
return "Insufficient Storage"
case StatusLoopDetected:
return "Loop Detected"
case StatusNotExtended:
return "Not Extended"
case StatusNetworkAuthenticationRequired:
return "Network Authentication Required"
*/
default:
return 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment