Introduce go chi web framework as frontend of macaron, so that we can move routes from macaron to chi step by step (#7420)

* When route cannot be found on chi, go to macaron

* Stick chi version to 1.5.0

* Follow router log setting
This commit is contained in:
2020-11-13 20:51:07 +08:00
committed by GitHub
parent 0ae35c66f2
commit c296f4fed6
49 changed files with 4796 additions and 249 deletions

View File

@ -70,7 +70,7 @@ issues:
- path: modules/log/
linters:
- errcheck
- path: routers/routes/routes.go
- path: routers/routes/macaron.go
linters:
- dupl
- path: routers/api/v1/repo/issue_subscription.go

View File

@ -19,8 +19,6 @@ import (
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"
"gitea.com/macaron/macaron"
context2 "github.com/gorilla/context"
"github.com/unknwon/com"
"github.com/urfave/cli"
@ -135,9 +133,9 @@ func runWeb(ctx *cli.Context) error {
return err
}
}
m := routes.NewMacaron()
routes.RegisterInstallRoute(m)
err := listen(m, false)
c := routes.NewChi()
routes.RegisterInstallRoute(c)
err := listen(c, false)
select {
case <-graceful.GetManager().IsShutdown():
<-graceful.GetManager().Done()
@ -168,10 +166,10 @@ func runWeb(ctx *cli.Context) error {
}
}
// Set up Macaron
m := routes.NewMacaron()
routes.RegisterRoutes(m)
c := routes.NewChi()
routes.RegisterRoutes(c)
err := listen(m, true)
err := listen(c, true)
<-graceful.GetManager().Done()
log.Info("PID: %d Gitea Web Finished", os.Getpid())
log.Close()
@ -212,7 +210,7 @@ func setPort(port string) error {
return nil
}
func listen(m *macaron.Macaron, handleRedirector bool) error {
func listen(m http.Handler, handleRedirector bool) error {
listenAddr := setting.HTTPAddr
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)

View File

@ -117,8 +117,8 @@ func runPR() {
//routers.GlobalInit()
external.RegisterParsers()
markup.Init()
m := routes.NewMacaron()
routes.RegisterRoutes(m)
c := routes.NewChi()
routes.RegisterRoutes(c)
log.Printf("[PR] Ready for testing !\n")
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
@ -138,7 +138,7 @@ func runPR() {
*/
//Start the server
http.ListenAndServe(":8080", context2.ClearHandler(m))
http.ListenAndServe(":8080", context2.ClearHandler(c))
log.Printf("[PR] Cleaning up ...\n")
/*

1
go.mod
View File

@ -39,6 +39,7 @@ require (
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
github.com/gliderlabs/ssh v0.3.1
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
github.com/go-chi/chi v1.5.0
github.com/go-enry/go-enry/v2 v2.5.2
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.2.0

7
go.sum
View File

@ -288,6 +288,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethantkoenig/rupture v0.0.0-20181029165146-c3b3b810dc77 h1:ZLWiTTzTUBb0WEXUxobYI/RxULIzOoIP7pgfDd4p1cw=
github.com/ethantkoenig/rupture v0.0.0-20181029165146-c3b3b810dc77/go.mod h1:MkKY/CB98aVE4VxO63X5vTQKUgcn+3XP15LMASe3lYs=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
@ -323,6 +324,8 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi v1.5.0 h1:2ZcJZozJ+rj6BA0c19ffBUGXEKAT/aOLOtQjD46vBRA=
github.com/go-chi/chi v1.5.0/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
github.com/go-enry/go-enry/v2 v2.5.2 h1:3f3PFAO6JitWkPi1GQ5/m6Xu4gNL1U5soJ8QaYqJ0YQ=
github.com/go-enry/go-enry/v2 v2.5.2/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ=
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
@ -655,6 +658,7 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
@ -684,6 +688,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
@ -1025,6 +1030,7 @@ github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM=
github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y=
@ -1069,6 +1075,7 @@ github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnl
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c h1:679/gJXwrsHC3RATr0YYjZvDMJPYN7W9FGSGNoLmKxM=
github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs=
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae/go.mod h1:1fdkY6xxl6ExVs2QFv7R0F5IRZHKA8RahhB9fMC9RvM=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=

View File

@ -58,8 +58,8 @@ func TestSessionFileCreation(t *testing.T) {
oldSessionConfig := setting.SessionConfig.ProviderConfig
defer func() {
setting.SessionConfig.ProviderConfig = oldSessionConfig
mac = routes.NewMacaron()
routes.RegisterRoutes(mac)
c = routes.NewChi()
routes.RegisterRoutes(c)
}()
var config session.Options
@ -83,8 +83,8 @@ func TestSessionFileCreation(t *testing.T) {
setting.SessionConfig.ProviderConfig = string(newConfigBytes)
mac = routes.NewMacaron()
routes.RegisterRoutes(mac)
c = routes.NewChi()
routes.RegisterRoutes(c)
t.Run("NoSessionOnViewIssue", func(t *testing.T) {
defer PrintCurrentTest(t)()

View File

@ -82,7 +82,7 @@ func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL), prepare ...bo
defer prepareTestEnv(t, 1)()
}
s := http.Server{
Handler: mac,
Handler: c,
}
u, err := url.Parse(setting.AppURL)

View File

@ -34,13 +34,13 @@ import (
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"
"gitea.com/macaron/macaron"
"github.com/PuerkitoBio/goquery"
"github.com/go-chi/chi"
"github.com/stretchr/testify/assert"
"github.com/unknwon/com"
)
var mac *macaron.Macaron
var c chi.Router
type NilResponseRecorder struct {
httptest.ResponseRecorder
@ -67,8 +67,8 @@ func TestMain(m *testing.M) {
defer cancel()
initIntegrationTest()
mac = routes.NewMacaron()
routes.RegisterRoutes(mac)
c = routes.NewChi()
routes.RegisterRoutes(c)
// integration test settings...
if setting.Cfg != nil {
@ -404,7 +404,7 @@ const NoExpectedStatus = -1
func MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.ResponseRecorder {
t.Helper()
recorder := httptest.NewRecorder()
mac.ServeHTTP(recorder, req)
c.ServeHTTP(recorder, req)
if expectedStatus != NoExpectedStatus {
if !assert.EqualValues(t, expectedStatus, recorder.Code,
"Request: %s %s", req.Method, req.URL.String()) {
@ -417,7 +417,7 @@ func MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.
func MakeRequestNilResponseRecorder(t testing.TB, req *http.Request, expectedStatus int) *NilResponseRecorder {
t.Helper()
recorder := NewNilResponseRecorder()
mac.ServeHTTP(recorder, req)
c.ServeHTTP(recorder, req)
if expectedStatus != NoExpectedStatus {
if !assert.EqualValues(t, expectedStatus, recorder.Code,
"Request: %s %s", req.Method, req.URL.String()) {

View File

@ -5,7 +5,9 @@
package integrations
import (
"bytes"
"encoding/json"
"io/ioutil"
"testing"
"code.gitea.io/gitea/modules/setting"
@ -233,11 +235,21 @@ func TestRefreshTokenInvalidation(t *testing.T) {
"redirect_uri": "a",
"refresh_token": parsed.RefreshToken,
})
// tip: Why this changed, because macaron will set req.Body back when consume the req but chi will not.
bs, err := ioutil.ReadAll(refreshReq.Body)
assert.NoError(t, err)
refreshReq.Body = ioutil.NopCloser(bytes.NewReader(bs))
MakeRequest(t, refreshReq, 200)
refreshReq.Body = ioutil.NopCloser(bytes.NewReader(bs))
MakeRequest(t, refreshReq, 200)
// test with invalidation
setting.OAuth2.InvalidateRefreshTokens = true
refreshReq.Body = ioutil.NopCloser(bytes.NewReader(bs))
MakeRequest(t, refreshReq, 200)
refreshReq.Body = ioutil.NopCloser(bytes.NewReader(bs))
MakeRequest(t, refreshReq, 400)
}

View File

@ -6,11 +6,9 @@
package public
import (
"gitea.com/macaron/macaron"
)
import "net/http"
// Static implements the macaron static handler for serving assets.
func Static(opts *Options) macaron.Handler {
func Static(opts *Options) func(next http.Handler) http.Handler {
return opts.staticHandler(opts.Directory)
}

View File

@ -15,8 +15,6 @@ import (
"time"
"code.gitea.io/gitea/modules/setting"
"gitea.com/macaron/macaron"
)
// Options represents the available options to configure the macaron handler.
@ -41,7 +39,7 @@ var KnownPublicEntries = []string{
}
// Custom implements the macaron static handler for serving custom assets.
func Custom(opts *Options) macaron.Handler {
func Custom(opts *Options) func(next http.Handler) http.Handler {
return opts.staticHandler(path.Join(setting.CustomPath, "public"))
}
@ -52,7 +50,7 @@ type staticFileSystem struct {
func newStaticFileSystem(directory string) staticFileSystem {
if !filepath.IsAbs(directory) {
directory = filepath.Join(macaron.Root, directory)
directory = filepath.Join(setting.AppWorkPath, directory)
}
dir := http.Dir(directory)
return staticFileSystem{&dir}
@ -63,39 +61,43 @@ func (fs staticFileSystem) Open(name string) (http.File, error) {
}
// StaticHandler sets up a new middleware for serving static files in the
func StaticHandler(dir string, opts *Options) macaron.Handler {
func StaticHandler(dir string, opts *Options) func(next http.Handler) http.Handler {
return opts.staticHandler(dir)
}
func (opts *Options) staticHandler(dir string) macaron.Handler {
// Defaults
if len(opts.IndexFile) == 0 {
opts.IndexFile = "index.html"
}
// Normalize the prefix if provided
if opts.Prefix != "" {
// Ensure we have a leading '/'
if opts.Prefix[0] != '/' {
opts.Prefix = "/" + opts.Prefix
func (opts *Options) staticHandler(dir string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
// Defaults
if len(opts.IndexFile) == 0 {
opts.IndexFile = "index.html"
}
// Normalize the prefix if provided
if opts.Prefix != "" {
// Ensure we have a leading '/'
if opts.Prefix[0] != '/' {
opts.Prefix = "/" + opts.Prefix
}
// Remove any trailing '/'
opts.Prefix = strings.TrimRight(opts.Prefix, "/")
}
if opts.FileSystem == nil {
opts.FileSystem = newStaticFileSystem(dir)
}
// Remove any trailing '/'
opts.Prefix = strings.TrimRight(opts.Prefix, "/")
}
if opts.FileSystem == nil {
opts.FileSystem = newStaticFileSystem(dir)
}
return func(ctx *macaron.Context, log *log.Logger) {
opts.handle(ctx, log, opts)
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if !opts.handle(w, req, opts) {
next.ServeHTTP(w, req)
}
})
}
}
func (opts *Options) handle(ctx *macaron.Context, log *log.Logger, opt *Options) bool {
if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" {
func (opts *Options) handle(w http.ResponseWriter, req *http.Request, opt *Options) bool {
if req.Method != "GET" && req.Method != "HEAD" {
return false
}
file := ctx.Req.URL.Path
file := req.URL.Path
// if we have a prefix, filter requests by stripping the prefix
if opt.Prefix != "" {
if !strings.HasPrefix(file, opt.Prefix) {
@ -117,7 +119,7 @@ func (opts *Options) handle(ctx *macaron.Context, log *log.Logger, opt *Options)
}
for _, entry := range KnownPublicEntries {
if entry == parts[1] {
ctx.Resp.WriteHeader(404)
w.WriteHeader(404)
return true
}
}
@ -135,8 +137,8 @@ func (opts *Options) handle(ctx *macaron.Context, log *log.Logger, opt *Options)
// Try to serve index file
if fi.IsDir() {
// Redirect if missing trailing slash.
if !strings.HasSuffix(ctx.Req.URL.Path, "/") {
http.Redirect(ctx.Resp, ctx.Req.Request, path.Clean(ctx.Req.URL.Path+"/"), http.StatusFound)
if !strings.HasSuffix(req.URL.Path, "/") {
http.Redirect(w, req, path.Clean(req.URL.Path+"/"), http.StatusFound)
return true
}
@ -148,7 +150,7 @@ func (opts *Options) handle(ctx *macaron.Context, log *log.Logger, opt *Options)
fi, err = f.Stat()
if err != nil || fi.IsDir() {
return true
return false
}
}
@ -158,16 +160,16 @@ func (opts *Options) handle(ctx *macaron.Context, log *log.Logger, opt *Options)
// Add an Expires header to the static content
if opt.ExpiresAfter > 0 {
ctx.Resp.Header().Set("Expires", time.Now().Add(opt.ExpiresAfter).UTC().Format(http.TimeFormat))
w.Header().Set("Expires", time.Now().Add(opt.ExpiresAfter).UTC().Format(http.TimeFormat))
tag := GenerateETag(fmt.Sprint(fi.Size()), fi.Name(), fi.ModTime().UTC().Format(http.TimeFormat))
ctx.Resp.Header().Set("ETag", tag)
if ctx.Req.Header.Get("If-None-Match") == tag {
ctx.Resp.WriteHeader(304)
return false
w.Header().Set("ETag", tag)
if req.Header.Get("If-None-Match") == tag {
w.WriteHeader(304)
return true
}
}
http.ServeContent(ctx.Resp, ctx.Req.Request, file, fi.ModTime(), f)
http.ServeContent(w, req, file, fi.ModTime(), f)
return true
}

View File

@ -8,12 +8,11 @@ package public
import (
"io/ioutil"
"gitea.com/macaron/macaron"
"net/http"
)
// Static implements the macaron static handler for serving assets.
func Static(opts *Options) macaron.Handler {
func Static(opts *Options) func(next http.Handler) http.Handler {
opts.FileSystem = Assets
// we don't need to pass the directory, because the directory var is only
// used when in the options there is no FileSystem.

260
routers/routes/chi.go Normal file
View File

@ -0,0 +1,260 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package routes
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
"text/template"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
)
type routerLoggerOptions struct {
req *http.Request
Identity *string
Start *time.Time
ResponseWriter http.ResponseWriter
}
// SignedUserName returns signed user's name via context
// FIXME currently no any data stored on gin.Context but macaron.Context, so this will
// return "" before we remove macaron totally
func SignedUserName(req *http.Request) string {
if v, ok := req.Context().Value("SignedUserName").(string); ok {
return v
}
return ""
}
func setupAccessLogger(c chi.Router) {
logger := log.GetLogger("access")
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate)
c.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
next.ServeHTTP(w, req)
identity := "-"
if val := SignedUserName(req); val != "" {
identity = val
}
rw := w
buf := bytes.NewBuffer([]byte{})
err := logTemplate.Execute(buf, routerLoggerOptions{
req: req,
Identity: &identity,
Start: &start,
ResponseWriter: rw,
})
if err != nil {
log.Error("Could not set up macaron access logger: %v", err.Error())
}
err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
if err != nil {
log.Error("Could not set up macaron access logger: %v", err.Error())
}
})
})
}
// LoggerHandler is a handler that will log the routing to the default gitea log
func LoggerHandler(level log.Level) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
_ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(req.Method), req.RequestURI, req.RemoteAddr)
next.ServeHTTP(w, req)
ww := middleware.NewWrapResponseWriter(w, req.ProtoMajor)
status := ww.Status()
_ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(req.Method), req.RequestURI, log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(status)), log.ColoredTime(time.Since(start)))
})
}
}
// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
// Although similar to macaron.Recovery() the main difference is that this error will be created
// with the gitea 500 page.
func Recovery() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
defer func() {
if err := recover(); err != nil {
combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2)))
http.Error(w, combinedErr, 500)
}
}()
next.ServeHTTP(w, req)
})
}
}
func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
if storageSetting.ServeDirect {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" && req.Method != "HEAD" {
next.ServeHTTP(w, req)
return
}
if !strings.HasPrefix(req.RequestURI, "/"+prefix) {
next.ServeHTTP(w, req)
return
}
rPath := strings.TrimPrefix(req.RequestURI, "/"+prefix)
u, err := objStore.URL(rPath, path.Base(rPath))
if err != nil {
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
log.Warn("Unable to find %s %s", prefix, rPath)
http.Error(w, "file not found", 404)
return
}
log.Error("Error whilst getting URL for %s %s. Error: %v", prefix, rPath, err)
http.Error(w, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath), 500)
return
}
http.Redirect(
w,
req,
u.String(),
301,
)
})
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" && req.Method != "HEAD" {
next.ServeHTTP(w, req)
return
}
if !strings.HasPrefix(req.RequestURI, "/"+prefix) {
next.ServeHTTP(w, req)
return
}
rPath := strings.TrimPrefix(req.RequestURI, "/"+prefix)
rPath = strings.TrimPrefix(rPath, "/")
//If we have matched and access to release or issue
fr, err := objStore.Open(rPath)
if err != nil {
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
log.Warn("Unable to find %s %s", prefix, rPath)
http.Error(w, "file not found", 404)
return
}
log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err)
http.Error(w, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath), 500)
return
}
defer fr.Close()
_, err = io.Copy(w, fr)
if err != nil {
log.Error("Error whilst rendering %s %s. Error: %v", prefix, rPath, err)
http.Error(w, fmt.Sprintf("Error whilst rendering %s %s", prefix, rPath), 500)
return
}
})
}
}
// NewChi creates a chi Router
func NewChi() chi.Router {
c := chi.NewRouter()
if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE {
if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel {
c.Use(LoggerHandler(setting.RouterLogLevel))
}
}
c.Use(Recovery())
if setting.EnableAccessLog {
setupAccessLogger(c)
}
if setting.ProdMode {
log.Warn("ProdMode ignored")
}
c.Use(public.Custom(
&public.Options{
SkipLogging: setting.DisableRouterLog,
ExpiresAfter: time.Hour * 6,
},
))
c.Use(public.Static(
&public.Options{
Directory: path.Join(setting.StaticRootPath, "public"),
SkipLogging: setting.DisableRouterLog,
ExpiresAfter: time.Hour * 6,
},
))
c.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
c.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
return c
}
// RegisterInstallRoute registers the install routes
func RegisterInstallRoute(c chi.Router) {
m := NewMacaron()
RegisterMacaronInstallRoute(m)
c.NotFound(func(w http.ResponseWriter, req *http.Request) {
m.ServeHTTP(w, req)
})
c.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) {
m.ServeHTTP(w, req)
})
}
// RegisterRoutes registers gin routes
func RegisterRoutes(c chi.Router) {
// for health check
c.Head("/", func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
})
// robots.txt
if setting.HasRobotsTxt {
c.Get("/robots.txt", func(w http.ResponseWriter, req *http.Request) {
http.ServeFile(w, req, path.Join(setting.CustomPath, "robots.txt"))
})
}
m := NewMacaron()
RegisterMacaronRoutes(m)
c.NotFound(func(w http.ResponseWriter, req *http.Request) {
m.ServeHTTP(w, req)
})
c.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) {
m.ServeHTTP(w, req)
})
}

View File

@ -5,17 +5,8 @@
package routes
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
"text/template"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
@ -24,9 +15,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/metrics"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/routers"
@ -58,129 +47,6 @@ import (
"github.com/tstranex/u2f"
)
type routerLoggerOptions struct {
Ctx *macaron.Context
Identity *string
Start *time.Time
ResponseWriter *macaron.ResponseWriter
}
func setupAccessLogger(m *macaron.Macaron) {
logger := log.GetLogger("access")
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate)
m.Use(func(ctx *macaron.Context) {
start := time.Now()
ctx.Next()
identity := "-"
if val, ok := ctx.Data["SignedUserName"]; ok {
if stringVal, ok := val.(string); ok && stringVal != "" {
identity = stringVal
}
}
rw := ctx.Resp.(macaron.ResponseWriter)
buf := bytes.NewBuffer([]byte{})
err := logTemplate.Execute(buf, routerLoggerOptions{
Ctx: ctx,
Identity: &identity,
Start: &start,
ResponseWriter: &rw,
})
if err != nil {
log.Error("Could not set up macaron access logger: %v", err.Error())
}
err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
if err != nil {
log.Error("Could not set up macaron access logger: %v", err.Error())
}
})
}
// RouterHandler is a macaron handler that will log the routing to the default gitea log
func RouterHandler(level log.Level) func(ctx *macaron.Context) {
return func(ctx *macaron.Context) {
start := time.Now()
_ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(ctx.Req.Method), ctx.Req.URL.RequestURI(), ctx.RemoteAddr())
rw := ctx.Resp.(macaron.ResponseWriter)
ctx.Next()
status := rw.Status()
_ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(ctx.Req.Method), ctx.Req.URL.RequestURI(), log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(rw.Status())), log.ColoredTime(time.Since(start)))
}
}
func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) macaron.Handler {
if storageSetting.ServeDirect {
return func(ctx *macaron.Context) {
req := ctx.Req.Request
if req.Method != "GET" && req.Method != "HEAD" {
return
}
if !strings.HasPrefix(req.RequestURI, "/"+prefix) {
return
}
rPath := strings.TrimPrefix(req.RequestURI, "/"+prefix)
u, err := objStore.URL(rPath, path.Base(rPath))
if err != nil {
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
log.Warn("Unable to find %s %s", prefix, rPath)
ctx.Error(404, "file not found")
return
}
log.Error("Error whilst getting URL for %s %s. Error: %v", prefix, rPath, err)
ctx.Error(500, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath))
return
}
http.Redirect(
ctx.Resp,
req,
u.String(),
301,
)
}
}
return func(ctx *macaron.Context) {
req := ctx.Req.Request
if req.Method != "GET" && req.Method != "HEAD" {
return
}
if !strings.HasPrefix(req.RequestURI, "/"+prefix) {
return
}
rPath := strings.TrimPrefix(req.RequestURI, "/"+prefix)
rPath = strings.TrimPrefix(rPath, "/")
//If we have matched and access to release or issue
fr, err := objStore.Open(rPath)
if err != nil {
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
log.Warn("Unable to find %s %s", prefix, rPath)
ctx.Error(404, "file not found")
return
}
log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err)
ctx.Error(500, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath))
return
}
defer fr.Close()
_, err = io.Copy(ctx.Resp, fr)
if err != nil {
log.Error("Error whilst rendering %s %s. Error: %v", prefix, rPath, err)
ctx.Error(500, fmt.Sprintf("Error whilst rendering %s %s", prefix, rPath))
return
}
}
}
// NewMacaron initializes Macaron instance.
func NewMacaron() *macaron.Macaron {
gob.Register(&u2f.Challenge{})
@ -188,47 +54,19 @@ func NewMacaron() *macaron.Macaron {
if setting.RedirectMacaronLog {
loggerAsWriter := log.NewLoggerAsWriter("INFO", log.GetLogger("macaron"))
m = macaron.NewWithLogger(loggerAsWriter)
if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE {
if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel {
m.Use(RouterHandler(setting.RouterLogLevel))
}
}
} else {
m = macaron.New()
if !setting.DisableRouterLog {
m.Use(macaron.Logger())
}
}
// Access Logger is similar to Router Log but more configurable and by default is more like the NCSA Common Log format
if setting.EnableAccessLog {
setupAccessLogger(m)
}
m.Use(macaron.Recovery())
if setting.EnableGzip {
m.Use(gzip.Middleware())
}
if setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix {
m.SetURLPrefix(setting.AppSubURL)
}
m.Use(public.Custom(
&public.Options{
SkipLogging: setting.DisableRouterLog,
ExpiresAfter: setting.StaticCacheTime,
},
))
m.Use(public.Static(
&public.Options{
Directory: path.Join(setting.StaticRootPath, "public"),
SkipLogging: setting.DisableRouterLog,
ExpiresAfter: setting.StaticCacheTime,
},
))
m.Use(templates.HTMLRenderer())
m.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
m.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
mailer.InitMailRender(templates.Mailer())
localeNames, err := options.Dir("locale")
@ -294,15 +132,12 @@ func NewMacaron() *macaron.Macaron {
DisableDebug: !setting.EnablePprof,
}))
m.Use(context.Contexter())
// OK we are now set-up enough to allow us to create a nicer recovery than
// the default macaron recovery
m.Use(context.Recovery())
m.SetAutoHead(true)
return m
}
// RegisterInstallRoute registers the install routes
func RegisterInstallRoute(m *macaron.Macaron) {
// RegisterMacaronInstallRoute registers the install routes
func RegisterMacaronInstallRoute(m *macaron.Macaron) {
m.Combo("/", routers.InstallInit).Get(routers.Install).
Post(binding.BindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.NotFound(func(ctx *context.Context) {
@ -310,8 +145,8 @@ func RegisterInstallRoute(m *macaron.Macaron) {
})
}
// RegisterRoutes routes routes to Macaron
func RegisterRoutes(m *macaron.Macaron) {
// RegisterMacaronRoutes routes routes to Macaron
func RegisterMacaronRoutes(m *macaron.Macaron) {
reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})
ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView})
ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true})
@ -353,9 +188,6 @@ func RegisterRoutes(m *macaron.Macaron) {
// Especially some AJAX requests, we can reduce middleware number to improve performance.
// Routers.
// for health check
m.Head("/", func() string {
return ""
})
m.Get("/", routers.Home)
m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
@ -1146,15 +978,6 @@ func RegisterRoutes(m *macaron.Macaron) {
private.RegisterRoutes(m)
})
// robots.txt
m.Get("/robots.txt", func(ctx *context.Context) {
if setting.HasRobotsTxt {
ctx.ServeFileContent(path.Join(setting.CustomPath, "robots.txt"))
} else {
ctx.NotFound("", nil)
}
})
m.Get("/apple-touch-icon.png", func(ctx *context.Context) {
ctx.Redirect(path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301)
})

3
vendor/github.com/go-chi/chi/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
*.sw?
.vscode

20
vendor/github.com/go-chi/chi/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,20 @@
language: go
go:
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
- 1.14.x
script:
- go get -d -t ./...
- go vet ./...
- go test ./...
- >
go_version=$(go version);
if [ ${go_version:13:4} = "1.12" ]; then
go get -u golang.org/x/tools/cmd/goimports;
goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :;
fi

190
vendor/github.com/go-chi/chi/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,190 @@
# Changelog
## v4.1.2 (2020-06-02)
- fix that handles MethodNotAllowed with path variables, thank you @caseyhadden for your contribution
- fix to replace nested wildcards correctly in RoutePattern, thank you @@unmultimedio for your contribution
- History of changes: see https://github.com/go-chi/chi/compare/v4.1.1...v4.1.2
## v4.1.1 (2020-04-16)
- fix for issue https://github.com/go-chi/chi/issues/411 which allows for overlapping regexp
route to the correct handler through a recursive tree search, thanks to @Jahaja for the PR/fix!
- new middleware.RouteHeaders as a simple router for request headers with wildcard support
- History of changes: see https://github.com/go-chi/chi/compare/v4.1.0...v4.1.1
## v4.1.0 (2020-04-1)
- middleware.LogEntry: Write method on interface now passes the response header
and an extra interface type useful for custom logger implementations.
- middleware.WrapResponseWriter: minor fix
- middleware.Recoverer: a bit prettier
- History of changes: see https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0
## v4.0.4 (2020-03-24)
- middleware.Recoverer: new pretty stack trace printing (https://github.com/go-chi/chi/pull/496)
- a few minor improvements and fixes
- History of changes: see https://github.com/go-chi/chi/compare/v4.0.3...v4.0.4
## v4.0.3 (2020-01-09)
- core: fix regexp routing to include default value when param is not matched
- middleware: rewrite of middleware.Compress
- middleware: suppress http.ErrAbortHandler in middleware.Recoverer
- History of changes: see https://github.com/go-chi/chi/compare/v4.0.2...v4.0.3
## v4.0.2 (2019-02-26)
- Minor fixes
- History of changes: see https://github.com/go-chi/chi/compare/v4.0.1...v4.0.2
## v4.0.1 (2019-01-21)
- Fixes issue with compress middleware: #382 #385
- History of changes: see https://github.com/go-chi/chi/compare/v4.0.0...v4.0.1
## v4.0.0 (2019-01-10)
- chi v4 requires Go 1.10.3+ (or Go 1.9.7+) - we have deprecated support for Go 1.7 and 1.8
- router: respond with 404 on router with no routes (#362)
- router: additional check to ensure wildcard is at the end of a url pattern (#333)
- middleware: deprecate use of http.CloseNotifier (#347)
- middleware: fix RedirectSlashes to include query params on redirect (#334)
- History of changes: see https://github.com/go-chi/chi/compare/v3.3.4...v4.0.0
## v3.3.4 (2019-01-07)
- Minor middleware improvements. No changes to core library/router. Moving v3 into its
- own branch as a version of chi for Go 1.7, 1.8, 1.9, 1.10, 1.11
- History of changes: see https://github.com/go-chi/chi/compare/v3.3.3...v3.3.4
## v3.3.3 (2018-08-27)
- Minor release
- See https://github.com/go-chi/chi/compare/v3.3.2...v3.3.3
## v3.3.2 (2017-12-22)
- Support to route trailing slashes on mounted sub-routers (#281)
- middleware: new `ContentCharset` to check matching charsets. Thank you
@csucu for your community contribution!
## v3.3.1 (2017-11-20)
- middleware: new `AllowContentType` handler for explicit whitelist of accepted request Content-Types
- middleware: new `SetHeader` handler for short-hand middleware to set a response header key/value
- Minor bug fixes
## v3.3.0 (2017-10-10)
- New chi.RegisterMethod(method) to add support for custom HTTP methods, see _examples/custom-method for usage
- Deprecated LINK and UNLINK methods from the default list, please use `chi.RegisterMethod("LINK")` and `chi.RegisterMethod("UNLINK")` in an `init()` function
## v3.2.1 (2017-08-31)
- Add new `Match(rctx *Context, method, path string) bool` method to `Routes` interface
and `Mux`. Match searches the mux's routing tree for a handler that matches the method/path
- Add new `RouteMethod` to `*Context`
- Add new `Routes` pointer to `*Context`
- Add new `middleware.GetHead` to route missing HEAD requests to GET handler
- Updated benchmarks (see README)
## v3.1.5 (2017-08-02)
- Setup golint and go vet for the project
- As per golint, we've redefined `func ServerBaseContext(h http.Handler, baseCtx context.Context) http.Handler`
to `func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler`
## v3.1.0 (2017-07-10)
- Fix a few minor issues after v3 release
- Move `docgen` sub-pkg to https://github.com/go-chi/docgen
- Move `render` sub-pkg to https://github.com/go-chi/render
- Add new `URLFormat` handler to chi/middleware sub-pkg to make working with url mime
suffixes easier, ie. parsing `/articles/1.json` and `/articles/1.xml`. See comments in
https://github.com/go-chi/chi/blob/master/middleware/url_format.go for example usage.
## v3.0.0 (2017-06-21)
- Major update to chi library with many exciting updates, but also some *breaking changes*
- URL parameter syntax changed from `/:id` to `/{id}` for even more flexible routing, such as
`/articles/{month}-{day}-{year}-{slug}`, `/articles/{id}`, and `/articles/{id}.{ext}` on the
same router
- Support for regexp for routing patterns, in the form of `/{paramKey:regExp}` for example:
`r.Get("/articles/{name:[a-z]+}", h)` and `chi.URLParam(r, "name")`
- Add `Method` and `MethodFunc` to `chi.Router` to allow routing definitions such as
`r.Method("GET", "/", h)` which provides a cleaner interface for custom handlers like
in `_examples/custom-handler`
- Deprecating `mux#FileServer` helper function. Instead, we encourage users to create their
own using file handler with the stdlib, see `_examples/fileserver` for an example
- Add support for LINK/UNLINK http methods via `r.Method()` and `r.MethodFunc()`
- Moved the chi project to its own organization, to allow chi-related community packages to
be easily discovered and supported, at: https://github.com/go-chi
- *NOTE:* please update your import paths to `"github.com/go-chi/chi"`
- *NOTE:* chi v2 is still available at https://github.com/go-chi/chi/tree/v2
## v2.1.0 (2017-03-30)
- Minor improvements and update to the chi core library
- Introduced a brand new `chi/render` sub-package to complete the story of building
APIs to offer a pattern for managing well-defined request / response payloads. Please
check out the updated `_examples/rest` example for how it works.
- Added `MethodNotAllowed(h http.HandlerFunc)` to chi.Router interface
## v2.0.0 (2017-01-06)
- After many months of v2 being in an RC state with many companies and users running it in
production, the inclusion of some improvements to the middlewares, we are very pleased to
announce v2.0.0 of chi.
## v2.0.0-rc1 (2016-07-26)
- Huge update! chi v2 is a large refactor targetting Go 1.7+. As of Go 1.7, the popular
community `"net/context"` package has been included in the standard library as `"context"` and
utilized by `"net/http"` and `http.Request` to managing deadlines, cancelation signals and other
request-scoped values. We're very excited about the new context addition and are proud to
introduce chi v2, a minimal and powerful routing package for building large HTTP services,
with zero external dependencies. Chi focuses on idiomatic design and encourages the use of
stdlib HTTP handlers and middlwares.
- chi v2 deprecates its `chi.Handler` interface and requires `http.Handler` or `http.HandlerFunc`
- chi v2 stores URL routing parameters and patterns in the standard request context: `r.Context()`
- chi v2 lower-level routing context is accessible by `chi.RouteContext(r.Context()) *chi.Context`,
which provides direct access to URL routing parameters, the routing path and the matching
routing patterns.
- Users upgrading from chi v1 to v2, need to:
1. Update the old chi.Handler signature, `func(ctx context.Context, w http.ResponseWriter, r *http.Request)` to
the standard http.Handler: `func(w http.ResponseWriter, r *http.Request)`
2. Use `chi.URLParam(r *http.Request, paramKey string) string`
or `URLParamFromCtx(ctx context.Context, paramKey string) string` to access a url parameter value
## v1.0.0 (2016-07-01)
- Released chi v1 stable https://github.com/go-chi/chi/tree/v1.0.0 for Go 1.6 and older.
## v0.9.0 (2016-03-31)
- Reuse context objects via sync.Pool for zero-allocation routing [#33](https://github.com/go-chi/chi/pull/33)
- BREAKING NOTE: due to subtle API changes, previously `chi.URLParams(ctx)["id"]` used to access url parameters
has changed to: `chi.URLParam(ctx, "id")`

31
vendor/github.com/go-chi/chi/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,31 @@
# Contributing
## Prerequisites
1. [Install Go][go-install].
2. Download the sources and switch the working directory:
```bash
go get -u -d github.com/go-chi/chi
cd $GOPATH/src/github.com/go-chi/chi
```
## Submitting a Pull Request
A typical workflow is:
1. [Fork the repository.][fork] [This tip maybe also helpful.][go-fork-tip]
2. [Create a topic branch.][branch]
3. Add tests for your change.
4. Run `go test`. If your tests pass, return to the step 3.
5. Implement the change and ensure the steps from the previous step pass.
6. Run `goimports -w .`, to ensure the new code conforms to Go formatting guideline.
7. [Add, commit and push your changes.][git-help]
8. [Submit a pull request.][pull-req]
[go-install]: https://golang.org/doc/install
[go-fork-tip]: http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html
[fork]: https://help.github.com/articles/fork-a-repo
[branch]: http://learn.github.com/p/branching.html
[git-help]: https://guides.github.com
[pull-req]: https://help.github.com/articles/using-pull-requests

20
vendor/github.com/go-chi/chi/LICENSE generated vendored Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

496
vendor/github.com/go-chi/chi/README.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

49
vendor/github.com/go-chi/chi/chain.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
package chi
import "net/http"
// Chain returns a Middlewares type from a slice of middleware handlers.
func Chain(middlewares ...func(http.Handler) http.Handler) Middlewares {
return Middlewares(middlewares)
}
// Handler builds and returns a http.Handler from the chain of middlewares,
// with `h http.Handler` as the final handler.
func (mws Middlewares) Handler(h http.Handler) http.Handler {
return &ChainHandler{mws, h, chain(mws, h)}
}
// HandlerFunc builds and returns a http.Handler from the chain of middlewares,
// with `h http.Handler` as the final handler.
func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler {
return &ChainHandler{mws, h, chain(mws, h)}
}
// ChainHandler is a http.Handler with support for handler composition and
// execution.
type ChainHandler struct {
Middlewares Middlewares
Endpoint http.Handler
chain http.Handler
}
func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c.chain.ServeHTTP(w, r)
}
// chain builds a http.Handler composed of an inline middleware stack and endpoint
// handler in the order they are passed.
func chain(middlewares []func(http.Handler) http.Handler, endpoint http.Handler) http.Handler {
// Return ahead of time if there aren't any middlewares for the chain
if len(middlewares) == 0 {
return endpoint
}
// Wrap the end handler with the middleware chain
h := middlewares[len(middlewares)-1](endpoint)
for i := len(middlewares) - 2; i >= 0; i-- {
h = middlewares[i](h)
}
return h
}

134
vendor/github.com/go-chi/chi/chi.go generated vendored Normal file
View File

@ -0,0 +1,134 @@
//
// Package chi is a small, idiomatic and composable router for building HTTP services.
//
// chi requires Go 1.10 or newer.
//
// Example:
// package main
//
// import (
// "net/http"
//
// "github.com/go-chi/chi"
// "github.com/go-chi/chi/middleware"
// )
//
// func main() {
// r := chi.NewRouter()
// r.Use(middleware.Logger)
// r.Use(middleware.Recoverer)
//
// r.Get("/", func(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("root."))
// })
//
// http.ListenAndServe(":3333", r)
// }
//
// See github.com/go-chi/chi/_examples/ for more in-depth examples.
//
// URL patterns allow for easy matching of path components in HTTP
// requests. The matching components can then be accessed using
// chi.URLParam(). All patterns must begin with a slash.
//
// A simple named placeholder {name} matches any sequence of characters
// up to the next / or the end of the URL. Trailing slashes on paths must
// be handled explicitly.
//
// A placeholder with a name followed by a colon allows a regular
// expression match, for example {number:\\d+}. The regular expression
// syntax is Go's normal regexp RE2 syntax, except that regular expressions
// including { or } are not supported, and / will never be
// matched. An anonymous regexp pattern is allowed, using an empty string
// before the colon in the placeholder, such as {:\\d+}
//
// The special placeholder of asterisk matches the rest of the requested
// URL. Any trailing characters in the pattern are ignored. This is the only
// placeholder which will match / characters.
//
// Examples:
// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/"
// "/user/{name}/info" matches "/user/jsmith/info"
// "/page/*" matches "/page/intro/latest"
// "/page/*/index" also matches "/page/intro/latest"
// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01"
//
package chi
import "net/http"
// NewRouter returns a new Mux object that implements the Router interface.
func NewRouter() *Mux {
return NewMux()
}
// Router consisting of the core routing methods used by chi's Mux,
// using only the standard net/http.
type Router interface {
http.Handler
Routes
// Use appends one or more middlewares onto the Router stack.
Use(middlewares ...func(http.Handler) http.Handler)
// With adds inline middlewares for an endpoint handler.
With(middlewares ...func(http.Handler) http.Handler) Router
// Group adds a new inline-Router along the current routing
// path, with a fresh middleware stack for the inline-Router.
Group(fn func(r Router)) Router
// Route mounts a sub-Router along a `pattern`` string.
Route(pattern string, fn func(r Router)) Router
// Mount attaches another http.Handler along ./pattern/*
Mount(pattern string, h http.Handler)
// Handle and HandleFunc adds routes for `pattern` that matches
// all HTTP methods.
Handle(pattern string, h http.Handler)
HandleFunc(pattern string, h http.HandlerFunc)
// Method and MethodFunc adds routes for `pattern` that matches
// the `method` HTTP method.
Method(method, pattern string, h http.Handler)
MethodFunc(method, pattern string, h http.HandlerFunc)
// HTTP-method routing along `pattern`
Connect(pattern string, h http.HandlerFunc)
Delete(pattern string, h http.HandlerFunc)
Get(pattern string, h http.HandlerFunc)
Head(pattern string, h http.HandlerFunc)
Options(pattern string, h http.HandlerFunc)
Patch(pattern string, h http.HandlerFunc)
Post(pattern string, h http.HandlerFunc)
Put(pattern string, h http.HandlerFunc)
Trace(pattern string, h http.HandlerFunc)
// NotFound defines a handler to respond whenever a route could
// not be found.
NotFound(h http.HandlerFunc)
// MethodNotAllowed defines a handler to respond whenever a method is
// not allowed.
MethodNotAllowed(h http.HandlerFunc)
}
// Routes interface adds two methods for router traversal, which is also
// used by the `docgen` subpackage to generation documentation for Routers.
type Routes interface {
// Routes returns the routing tree in an easily traversable structure.
Routes() []Route
// Middlewares returns the list of middlewares in use by the router.
Middlewares() Middlewares
// Match searches the routing tree for a handler that matches
// the method/path - similar to routing a http request, but without
// executing the handler thereafter.
Match(rctx *Context, method, path string) bool
}
// Middlewares type is a slice of standard middleware handlers with methods
// to compose middleware chains and http.Handler's.
type Middlewares []func(http.Handler) http.Handler

172
vendor/github.com/go-chi/chi/context.go generated vendored Normal file
View File

@ -0,0 +1,172 @@
package chi
import (
"context"
"net"
"net/http"
"strings"
)
// URLParam returns the url parameter from a http.Request object.
func URLParam(r *http.Request, key string) string {
if rctx := RouteContext(r.Context()); rctx != nil {
return rctx.URLParam(key)
}
return ""
}
// URLParamFromCtx returns the url parameter from a http.Request Context.
func URLParamFromCtx(ctx context.Context, key string) string {
if rctx := RouteContext(ctx); rctx != nil {
return rctx.URLParam(key)
}
return ""
}
// RouteContext returns chi's routing Context object from a
// http.Request Context.
func RouteContext(ctx context.Context) *Context {
val, _ := ctx.Value(RouteCtxKey).(*Context)
return val
}
// ServerBaseContext wraps an http.Handler to set the request context to the
// `baseCtx`.
func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler {
fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
baseCtx := baseCtx
// Copy over default net/http server context keys
if v, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok {
baseCtx = context.WithValue(baseCtx, http.ServerContextKey, v)
}
if v, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok {
baseCtx = context.WithValue(baseCtx, http.LocalAddrContextKey, v)
}
h.ServeHTTP(w, r.WithContext(baseCtx))
})
return fn
}
// NewRouteContext returns a new routing Context object.
func NewRouteContext() *Context {
return &Context{}
}
var (
// RouteCtxKey is the context.Context key to store the request context.
RouteCtxKey = &contextKey{"RouteContext"}
)
// Context is the default routing context set on the root node of a
// request context to track route patterns, URL parameters and
// an optional routing path.
type Context struct {
Routes Routes
// Routing path/method override used during the route search.
// See Mux#routeHTTP method.
RoutePath string
RouteMethod string
// Routing pattern stack throughout the lifecycle of the request,
// across all connected routers. It is a record of all matching
// patterns across a stack of sub-routers.
RoutePatterns []string
// URLParams are the stack of routeParams captured during the
// routing lifecycle across a stack of sub-routers.
URLParams RouteParams
// The endpoint routing pattern that matched the request URI path
// or `RoutePath` of the current sub-router. This value will update
// during the lifecycle of a request passing through a stack of
// sub-routers.
routePattern string
// Route parameters matched for the current sub-router. It is
// intentionally unexported so it cant be tampered.
routeParams RouteParams
// methodNotAllowed hint
methodNotAllowed bool
}
// Reset a routing context to its initial state.
func (x *Context) Reset() {
x.Routes = nil
x.RoutePath = ""
x.RouteMethod = ""
x.RoutePatterns = x.RoutePatterns[:0]
x.URLParams.Keys = x.URLParams.Keys[:0]
x.URLParams.Values = x.URLParams.Values[:0]
x.routePattern = ""
x.routeParams.Keys = x.routeParams.Keys[:0]
x.routeParams.Values = x.routeParams.Values[:0]
x.methodNotAllowed = false
}
// URLParam returns the corresponding URL parameter value from the request
// routing context.
func (x *Context) URLParam(key string) string {
for k := len(x.URLParams.Keys) - 1; k >= 0; k-- {
if x.URLParams.Keys[k] == key {
return x.URLParams.Values[k]
}
}
return ""
}
// RoutePattern builds the routing pattern string for the particular
// request, at the particular point during routing. This means, the value
// will change throughout the execution of a request in a router. That is
// why its advised to only use this value after calling the next handler.
//
// For example,
//
// func Instrument(next http.Handler) http.Handler {
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// next.ServeHTTP(w, r)
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
// measure(w, r, routePattern)
// })
// }
func (x *Context) RoutePattern() string {
routePattern := strings.Join(x.RoutePatterns, "")
return replaceWildcards(routePattern)
}
// replaceWildcards takes a route pattern and recursively replaces all
// occurrences of "/*/" to "/".
func replaceWildcards(p string) string {
if strings.Contains(p, "/*/") {
return replaceWildcards(strings.Replace(p, "/*/", "/", -1))
}
return p
}
// RouteParams is a structure to track URL routing parameters efficiently.
type RouteParams struct {
Keys, Values []string
}
// Add will append a URL parameter to the end of the route param
func (s *RouteParams) Add(key, value string) {
s.Keys = append(s.Keys, key)
s.Values = append(s.Values, value)
}
// contextKey is a value for use with context.WithValue. It's used as
// a pointer so it fits in an interface{} without allocation. This technique
// for defining context keys was copied from Go 1.7's new use of context in net/http.
type contextKey struct {
name string
}
func (k *contextKey) String() string {
return "chi context value " + k.name
}

3
vendor/github.com/go-chi/chi/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/go-chi/chi
go 1.15

32
vendor/github.com/go-chi/chi/middleware/basic_auth.go generated vendored Normal file
View File

@ -0,0 +1,32 @@
package middleware
import (
"fmt"
"net/http"
)
// BasicAuth implements a simple middleware handler for adding basic http auth to a route.
func BasicAuth(realm string, creds map[string]string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
if !ok {
basicAuthFailed(w, realm)
return
}
credPass, credUserOk := creds[user]
if !credUserOk || pass != credPass {
basicAuthFailed(w, realm)
return
}
next.ServeHTTP(w, r)
})
}
}
func basicAuthFailed(w http.ResponseWriter, realm string) {
w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
w.WriteHeader(http.StatusUnauthorized)
}

399
vendor/github.com/go-chi/chi/middleware/compress.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
package middleware
import (
"net/http"
"strings"
)
// ContentCharset generates a handler that writes a 415 Unsupported Media Type response if none of the charsets match.
// An empty charset will allow requests with no Content-Type header or no specified charset.
func ContentCharset(charsets ...string) func(next http.Handler) http.Handler {
for i, c := range charsets {
charsets[i] = strings.ToLower(c)
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !contentEncoding(r.Header.Get("Content-Type"), charsets...) {
w.WriteHeader(http.StatusUnsupportedMediaType)
return
}
next.ServeHTTP(w, r)
})
}
}
// Check the content encoding against a list of acceptable values.
func contentEncoding(ce string, charsets ...string) bool {
_, ce = split(strings.ToLower(ce), ";")
_, ce = split(ce, "charset=")
ce, _ = split(ce, ";")
for _, c := range charsets {
if ce == c {
return true
}
}
return false
}
// Split a string in two parts, cleaning any whitespace.
func split(str, sep string) (string, string) {
var a, b string
var parts = strings.SplitN(str, sep, 2)
a = strings.TrimSpace(parts[0])
if len(parts) == 2 {
b = strings.TrimSpace(parts[1])
}
return a, b
}

View File

@ -0,0 +1,34 @@
package middleware
import (
"net/http"
"strings"
)
// AllowContentEncoding enforces a whitelist of request Content-Encoding otherwise responds
// with a 415 Unsupported Media Type status.
func AllowContentEncoding(contentEncoding ...string) func(next http.Handler) http.Handler {
allowedEncodings := make(map[string]struct{}, len(contentEncoding))
for _, encoding := range contentEncoding {
allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))] = struct{}{}
}
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
requestEncodings := r.Header["Content-Encoding"]
// skip check for empty content body or no Content-Encoding
if r.ContentLength == 0 {
next.ServeHTTP(w, r)
return
}
// All encodings in the request must be allowed
for _, encoding := range requestEncodings {
if _, ok := allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))]; !ok {
w.WriteHeader(http.StatusUnsupportedMediaType)
return
}
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
}

View File

@ -0,0 +1,51 @@
package middleware
import (
"net/http"
"strings"
)
// SetHeader is a convenience handler to set a response header key/value
func SetHeader(key, value string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(key, value)
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
}
// AllowContentType enforces a whitelist of request Content-Types otherwise responds
// with a 415 Unsupported Media Type status.
func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler {
cT := []string{}
for _, t := range contentTypes {
cT = append(cT, strings.ToLower(t))
}
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if r.ContentLength == 0 {
// skip check for empty content body
next.ServeHTTP(w, r)
return
}
s := strings.ToLower(strings.TrimSpace(r.Header.Get("Content-Type")))
if i := strings.Index(s, ";"); i > -1 {
s = s[0:i]
}
for _, t := range cT {
if t == s {
next.ServeHTTP(w, r)
return
}
}
w.WriteHeader(http.StatusUnsupportedMediaType)
}
return http.HandlerFunc(fn)
}
}

39
vendor/github.com/go-chi/chi/middleware/get_head.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
package middleware
import (
"net/http"
"github.com/go-chi/chi"
)
// GetHead automatically route undefined HEAD requests to GET handlers.
func GetHead(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
rctx := chi.RouteContext(r.Context())
routePath := rctx.RoutePath
if routePath == "" {
if r.URL.RawPath != "" {
routePath = r.URL.RawPath
} else {
routePath = r.URL.Path
}
}
// Temporary routing context to look-ahead before routing the request
tctx := chi.NewRouteContext()
// Attempt to find a HEAD handler for the routing path, if not found, traverse
// the router as through its a GET route, but proceed with the request
// with the HEAD method.
if !rctx.Routes.Match(tctx, "HEAD", routePath) {
rctx.RouteMethod = "GET"
rctx.RoutePath = routePath
next.ServeHTTP(w, r)
return
}
}
next.ServeHTTP(w, r)
})
}

Some files were not shown because too many files have changed in this diff Show More