Update vendor, move to labstack/echo/v4 Fixes #698

This commit is contained in:
Wim 2019-01-31 17:06:36 +01:00
parent f8a1ab4622
commit c81c0dd22a
265 changed files with 32014 additions and 7207 deletions

View File

@ -8,8 +8,8 @@ import (
"github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/config"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/echo/middleware" "github.com/labstack/echo/v4/middleware"
"github.com/zfjagann/golang-ring" "github.com/zfjagann/golang-ring"
) )
@ -117,12 +117,7 @@ func (b *API) handleStream(c echo.Context) error {
return err return err
} }
c.Response().Flush() c.Response().Flush()
closeNotifier := c.Response().CloseNotify()
for { for {
select {
case <-closeNotifier:
return nil
default:
msg := b.Messages.Dequeue() msg := b.Messages.Dequeue()
if msg != nil { if msg != nil {
if err := json.NewEncoder(c.Response()).Encode(msg); err != nil { if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
@ -132,5 +127,4 @@ func (b *API) handleStream(c echo.Context) error {
} }
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
} }
}
} }

29
go.mod
View File

@ -6,7 +6,6 @@ require (
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
github.com/bwmarrin/discordgo v0.19.0 github.com/bwmarrin/discordgo v0.19.0
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
github.com/dgrijalva/jwt-go v0.0.0-20170508165458-6c8dedd55f8a // indirect
github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify v1.4.7
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc // indirect github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc // indirect
@ -18,10 +17,9 @@ require (
github.com/hpcloud/tail v1.0.0 // indirect github.com/hpcloud/tail v1.0.0 // indirect
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7
github.com/jtolds/gls v4.2.1+incompatible // indirect github.com/jtolds/gls v4.2.1+incompatible // indirect
github.com/kardianos/osext v0.0.0-20170207191655-9b883c5eb462 // indirect github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
github.com/kr/pretty v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect
github.com/labstack/echo v3.3.5+incompatible github.com/labstack/echo/v4 v4.0.0
github.com/labstack/gommon v0.2.1 // indirect
github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect
@ -30,37 +28,30 @@ require (
github.com/matterbridge/gozulipbot v0.0.0-20180507190239-b6bb12d33544 github.com/matterbridge/gozulipbot v0.0.0-20180507190239-b6bb12d33544
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
github.com/mattermost/mattermost-server v5.5.0+incompatible github.com/mattermost/mattermost-server v5.5.0+incompatible
github.com/mattn/go-colorable v0.0.0-20170210172801-5411d3eea597 // indirect
github.com/mattn/go-isatty v0.0.0-20170216235908-dda3de49cbfc // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
github.com/nicksnyder/go-i18n v1.4.0 // indirect github.com/nicksnyder/go-i18n v1.4.0 // indirect
github.com/nlopes/slack v0.4.1-0.20181111125009-5963eafd777b github.com/nlopes/slack v0.5.0
github.com/onsi/ginkgo v1.6.0 // indirect github.com/onsi/ginkgo v1.6.0 // indirect
github.com/onsi/gomega v1.4.1 // indirect github.com/onsi/gomega v1.4.1 // indirect
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83 github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
github.com/peterhellberg/emojilib v0.0.0-20180820090156-eeb3823dab9a github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320
github.com/pkg/errors v0.8.0 // indirect github.com/pkg/errors v0.8.0 // indirect
github.com/rs/xid v1.2.1 github.com/rs/xid v1.2.1
github.com/russross/blackfriday v2.0.0+incompatible github.com/russross/blackfriday v2.0.0+incompatible
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
github.com/shazow/ssh-chat v0.0.0-20181028152505-f36d7eb9ccc6 github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect
github.com/sirupsen/logrus v1.2.0 github.com/sirupsen/logrus v1.3.0
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
github.com/spf13/cast v1.3.0 // indirect github.com/spf13/viper v1.3.1
github.com/spf13/pflag v1.0.3 // indirect github.com/stretchr/testify v1.3.0
github.com/spf13/viper v1.2.1
github.com/stretchr/testify v1.2.2
github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/valyala/bytebufferpool v0.0.0-20160817181652-e746df99fe4a // indirect
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6 github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f
@ -70,10 +61,8 @@ require (
go.uber.org/atomic v1.3.2 // indirect go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1 // indirect go.uber.org/zap v1.9.1 // indirect
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 // indirect
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 // indirect golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 // indirect
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect
golang.org/x/sys v0.0.0-20181116161606-93218def8b18 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect

83
go.sum
View File

@ -6,14 +6,19 @@ github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg= github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc= github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE= github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY= github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX87a2zVnnbPPickIM9Gf9OIhsIgWQ= github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX87a2zVnnbPPickIM9Gf9OIhsIgWQ=
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY= github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
github.com/dgrijalva/jwt-go v0.0.0-20170508165458-6c8dedd55f8a h1:MuHMeSsXbNEeUyxjB7T9P8s1+5k8OLTC/M27qsVwixM= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v0.0.0-20170508165458-6c8dedd55f8a/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE= github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
@ -40,8 +45,8 @@ github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3q
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/osext v0.0.0-20170207191655-9b883c5eb462 h1:oSOOTPHkCzMeu1vJ0nHxg5+XZBdMMjNa+6NPnm8arok= github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
github.com/kardianos/osext v0.0.0-20170207191655-9b883c5eb462/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@ -49,10 +54,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo v3.3.5+incompatible h1:9PfxPUmasKzeJor9uQTaXLT6WUG/r+vSTmvXxvv3JO4= github.com/labstack/echo/v4 v4.0.0 h1:q1GH+caIXPP7H2StPIdzy/ez9CO0EepqYeUg6vi9SWM=
github.com/labstack/echo v3.3.5+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno=
github.com/labstack/gommon v0.2.1 h1:C+I4NYknueQncqKYZQ34kHsLZJVeB5KwPUhnO0nmbpU= github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.1/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 h1:dDEQN5oaa0WOzEiPDSbOugW/e2I/SWY98HYRdcwmGfY= github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 h1:dDEQN5oaa0WOzEiPDSbOugW/e2I/SWY98HYRdcwmGfY=
github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk= github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk=
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU= github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU=
@ -71,13 +76,12 @@ github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU= github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU= github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU=
github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y= github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y=
github.com/mattn/go-colorable v0.0.0-20170210172801-5411d3eea597 h1:hGizH4aMDFFt1iOA4HNKC13lqIBoCyxIjWcAnWIy7aU= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.0-20170210172801-5411d3eea597/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.0-20170216235908-dda3de49cbfc h1:pK7tzC30erKOTfEDCYGvPZQCkmM9X5iSmmAR5m9x3Yc= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.0-20170216235908-dda3de49cbfc/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteTqeSpenyTrOVj5zkiyCaflLa8B+CD0324otT+o= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteTqeSpenyTrOVj5zkiyCaflLa8B+CD0324otT+o=
@ -86,8 +90,8 @@ github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9Dt
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff/go.mod h1:B8jLfIIPn2sKyWr0D7cL2v7tnrDD5z291s2Zypdu89E= github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff/go.mod h1:B8jLfIIPn2sKyWr0D7cL2v7tnrDD5z291s2Zypdu89E=
github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I= github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I=
github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
github.com/nlopes/slack v0.4.1-0.20181111125009-5963eafd777b h1:8ncrr7Xps0GafXIxBzrq1qSjy1zhiCDp/9C4cOrE+GU= github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0=
github.com/nlopes/slack v0.4.1-0.20181111125009-5963eafd777b/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
@ -98,8 +102,8 @@ github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 h1:/CPgDYrfeK2LMK6xcU
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterhellberg/emojilib v0.0.0-20180820090156-eeb3823dab9a h1:zAss6STq7oejKWTMEUYDUKYZhqXe0xALo8pJhJ3JJAs= github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0=
github.com/peterhellberg/emojilib v0.0.0-20180820090156-eeb3823dab9a/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA= github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -112,43 +116,45 @@ github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxT
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0= github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI= github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
github.com/shazow/ssh-chat v0.0.0-20181028152505-f36d7eb9ccc6 h1:qNoZx1RWPGKiqfs8ZZAYsYtw3ejo3HIF7iECaeaJhFk= github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio=
github.com/shazow/ssh-chat v0.0.0-20181028152505-f36d7eb9ccc6/go.mod h1:SA/9+Wy3zV0UvPjttpGgs90FS9ZZ5D/LTffnVqdIBE8= github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg= github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg=
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.2.1 h1:bIcUwXqLseLF3BDAZduuNfekWG87ibtFxi59Bq+oI9M= github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/valyala/bytebufferpool v0.0.0-20160817181652-e746df99fe4a h1:AOcehBWpFhYPYw0ioDTppQzgI8pAAahVCiMSKTp9rbo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/valyala/bytebufferpool v0.0.0-20160817181652-e746df99fe4a/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6 h1:/WULP+6asFz569UbOwg87f3iDT7T+GF5/vjLmL51Pdk= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 h1:CHgPZh8bFkZmislPrr/0gd7MciDAX+JJB70A2/5Lvmo=
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0= gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI= gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI=
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk= gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk=
@ -167,21 +173,20 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180119074636-ee41a25c63fb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 h1:kkXA53yGe04D0adEYJwEVQjeBppL01Exg+fnMjfUraU= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 h1:BkNcmLtAVeWe9h5k0jt24CQgaG5vb4x/doFbAiEC/Ho= golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 h1:BkNcmLtAVeWe9h5k0jt24CQgaG5vb4x/doFbAiEC/Ho=
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116161606-93218def8b18 h1:Wh+XCfg3kNpjhdq2LXrsiOProjtQZKme5XUx7VcxwAw= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
golang.org/x/sys v0.0.0-20181116161606-93218def8b18/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -193,5 +198,5 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXL
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,11 +1,15 @@
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html) # jwt-go
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go) [![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go)
[![GoDoc](https://godoc.org/github.com/dgrijalva/jwt-go?status.svg)](https://godoc.org/github.com/dgrijalva/jwt-go)
**BREAKING CHANGES:*** Version 3.0.0 is here. It includes _a lot_ of changes including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html)
**NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided. **NEW VERSION COMING:** There have been a lot of improvements suggested since the version 3.0.0 released in 2016. I'm working now on cutting two different releases: 3.2.0 will contain any non-breaking changes or enhancements. 4.0.0 will follow shortly which will include breaking changes. See the 4.0.0 milestone to get an idea of what's coming. If you have other ideas, or would like to participate in 4.0.0, now's the time. If you depend on this library and don't want to be interrupted, I recommend you use your dependency mangement tool to pin to version 3.
**SECURITY NOTICE:** Some older versions of Go have a security issue in the cryotp/elliptic. Recommendation is to upgrade to at least 1.8.3. See issue #216 for more detail.
**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided.
## What the heck is a JWT? ## What the heck is a JWT?
@ -47,7 +51,10 @@ This library is considered production ready. Feedback and feature requests are
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases). This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases).
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`. It will do the right thing WRT semantic versioning. While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v3`. It will do the right thing WRT semantic versioning.
**BREAKING CHANGES:***
* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
## Usage Tips ## Usage Tips
@ -68,6 +75,14 @@ Symmetric signing methods, such as HSA, use only a single secret. This is probab
Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification.
### Signing Methods and Key Types
Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones:
* The [HMAC signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation
* The [RSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation
* The [ECDSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation
### JWT and OAuth ### JWT and OAuth
It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication.
@ -82,4 +97,4 @@ Without going too far down the rabbit hole, here's a description of the interact
Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go). Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in to documentation. The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation.

View File

@ -1,5 +1,18 @@
## `jwt-go` Version History ## `jwt-go` Version History
#### 3.2.0
* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation
* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate
* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before.
* Deprecated `ParseFromRequestWithClaims` to simplify API in the future.
#### 3.1.0
* Improvements to `jwt` command line tool
* Added `SkipClaimsValidation` option to `Parser`
* Documentation updates
#### 3.0.0 #### 3.0.0
* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code * **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code

View File

@ -14,6 +14,7 @@ var (
) )
// Implements the ECDSA family of signing methods signing methods // Implements the ECDSA family of signing methods signing methods
// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification
type SigningMethodECDSA struct { type SigningMethodECDSA struct {
Name string Name string
Hash crypto.Hash Hash crypto.Hash

View File

@ -7,6 +7,7 @@ import (
) )
// Implements the HMAC-SHA family of signing methods signing methods // Implements the HMAC-SHA family of signing methods signing methods
// Expects key type of []byte for both signing and validation
type SigningMethodHMAC struct { type SigningMethodHMAC struct {
Name string Name string
Hash crypto.Hash Hash crypto.Hash
@ -90,5 +91,5 @@ func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string,
return EncodeSegment(hasher.Sum(nil)), nil return EncodeSegment(hasher.Sum(nil)), nil
} }
return "", ErrInvalidKey return "", ErrInvalidKeyType
} }

View File

@ -21,55 +21,9 @@ func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
} }
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
parts := strings.Split(tokenString, ".") token, parts, err := p.ParseUnverified(tokenString, claims)
if len(parts) != 3 {
return nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
}
var err error
token := &Token{Raw: tokenString}
// parse Header
var headerBytes []byte
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
return token, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
}
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
}
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
}
// parse Claims
var claimBytes []byte
token.Claims = claims
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
}
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
if p.UseJSONNumber {
dec.UseNumber()
}
// JSON Decode. Special case for map type to avoid weird pointer behavior
if c, ok := token.Claims.(MapClaims); ok {
err = dec.Decode(&c)
} else {
err = dec.Decode(&claims)
}
// Handle decode error
if err != nil { if err != nil {
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} return token, err
}
// Lookup signature method
if method, ok := token.Header["alg"].(string); ok {
if token.Method = GetSigningMethod(method); token.Method == nil {
return token, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
}
} else {
return token, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
} }
// Verify signing method is in the required set // Verify signing method is in the required set
@ -96,6 +50,9 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
} }
if key, err = keyFunc(token); err != nil { if key, err = keyFunc(token); err != nil {
// keyFunc returned an error // keyFunc returned an error
if ve, ok := err.(*ValidationError); ok {
return token, ve
}
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
} }
@ -129,3 +86,63 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
return token, vErr return token, vErr
} }
// WARNING: Don't use this method unless you know what you're doing
//
// This method parses the token but doesn't validate the signature. It's only
// ever useful in cases where you know the signature is valid (because it has
// been checked previously in the stack) and you want to extract values from
// it.
func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
parts = strings.Split(tokenString, ".")
if len(parts) != 3 {
return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
}
token = &Token{Raw: tokenString}
// parse Header
var headerBytes []byte
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
}
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
}
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
}
// parse Claims
var claimBytes []byte
token.Claims = claims
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
}
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
if p.UseJSONNumber {
dec.UseNumber()
}
// JSON Decode. Special case for map type to avoid weird pointer behavior
if c, ok := token.Claims.(MapClaims); ok {
err = dec.Decode(&c)
} else {
err = dec.Decode(&claims)
}
// Handle decode error
if err != nil {
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
}
// Lookup signature method
if method, ok := token.Header["alg"].(string); ok {
if token.Method = GetSigningMethod(method); token.Method == nil {
return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
}
} else {
return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
}
return token, parts, nil
}

View File

@ -7,6 +7,7 @@ import (
) )
// Implements the RSA family of signing methods signing methods // Implements the RSA family of signing methods signing methods
// Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation
type SigningMethodRSA struct { type SigningMethodRSA struct {
Name string Name string
Hash crypto.Hash Hash crypto.Hash
@ -44,7 +45,7 @@ func (m *SigningMethodRSA) Alg() string {
} }
// Implements the Verify method from SigningMethod // Implements the Verify method from SigningMethod
// For this signing method, must be an rsa.PublicKey structure. // For this signing method, must be an *rsa.PublicKey structure.
func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error {
var err error var err error
@ -73,7 +74,7 @@ func (m *SigningMethodRSA) Verify(signingString, signature string, key interface
} }
// Implements the Sign method from SigningMethod // Implements the Sign method from SigningMethod
// For this signing method, must be an rsa.PrivateKey structure. // For this signing method, must be an *rsa.PrivateKey structure.
func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) {
var rsaKey *rsa.PrivateKey var rsaKey *rsa.PrivateKey
var ok bool var ok bool

View File

@ -39,6 +39,38 @@ func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
return pkey, nil return pkey, nil
} }
// Parse PEM encoded PKCS1 or PKCS8 private key protected with password
func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) {
var err error
// Parse PEM block
var block *pem.Block
if block, _ = pem.Decode(key); block == nil {
return nil, ErrKeyMustBePEMEncoded
}
var parsedKey interface{}
var blockDecrypted []byte
if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil {
return nil, err
}
if parsedKey, err = x509.ParsePKCS1PrivateKey(blockDecrypted); err != nil {
if parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil {
return nil, err
}
}
var pkey *rsa.PrivateKey
var ok bool
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {
return nil, ErrNotRSAPrivateKey
}
return pkey, nil
}
// Parse PEM encoded PKCS1 or PKCS8 public key // Parse PEM encoded PKCS1 or PKCS8 public key
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
var err error var err error

View File

@ -1,4 +1,4 @@
//+build go1.8 //+build go1.8,!openbsd
package osext package osext

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build !go1.8,linux !go1.8,netbsd !go1.8,solaris !go1.8,dragonfly // +build !go1.8,android !go1.8,linux !go1.8,netbsd !go1.8,solaris !go1.8,dragonfly
package osext package osext
@ -16,7 +16,7 @@ import (
func executable() (string, error) { func executable() (string, error) {
switch runtime.GOOS { switch runtime.GOOS {
case "linux": case "linux", "android":
const deletedTag = " (deleted)" const deletedTag = " (deleted)"
execpath, err := os.Readlink("/proc/self/exe") execpath, err := os.Readlink("/proc/self/exe")
if err != nil { if err != nil {

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build !go1.8,darwin !go1.8,freebsd !go1.8,openbsd // +build !go1.8,darwin !go1.8,freebsd openbsd
package osext package osext

View File

@ -1,14 +0,0 @@
language: go
go:
- 1.9.x
- 1.10.x
- tip
install:
- make dependency
script:
- make test
after_success:
- bash <(curl -s https://codecov.io/bash)
matrix:
allow_failures:
- go: tip

View File

@ -1,75 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
name = "github.com/dgrijalva/jwt-go"
packages = ["."]
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
[[projects]]
name = "github.com/labstack/gommon"
packages = ["bytes","color","log","random"]
revision = "6fe1405d73ec4bd4cd8a4ac8e2a2b2bf95d03954"
version = "0.2.4"
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
name = "github.com/valyala/bytebufferpool"
packages = ["."]
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
[[projects]]
branch = "master"
name = "github.com/valyala/fasttemplate"
packages = ["."]
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["acme","acme/autocert"]
revision = "182114d582623c1caa54f73de9c7224e23a48487"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "c28acc882ebcbfbe8ce9f0f14b9ac26ee138dd51"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "9c7b45e80fe353405800cf01f429b3a203cfb8d4468a04c64a908e11a98ea764"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,42 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/dgrijalva/jwt-go"
version = "3.2.0"
[[constraint]]
name = "github.com/labstack/gommon"
version = "0.2.4"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.1"
[[constraint]]
branch = "master"
name = "github.com/valyala/fasttemplate"
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"

View File

@ -1,17 +0,0 @@
DEP_VERSION=0.4.1
dependency:
curl -fsSL -o ${GOPATH}/bin/dep https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64
chmod +x ${GOPATH}/bin/dep
dep ensure
test:
echo "" > coverage.txt
for d in $(shell go list ./... | grep -v vendor); do \
go test -race -coverprofile=profile.out -covermode=atomic $$d || exit 1; \
[ -f profile.out ] && cat profile.out >> coverage.txt && rm profile.out; \
done
tag:
@git tag `grep -P '^\tversion = ' echo.go|cut -f2 -d'"'`
@git tag|grep -v ^v

16
vendor/github.com/labstack/echo/v4/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,16 @@
language: go
go:
- 1.11.x
- tip
env:
- GO111MODULE=on
install:
- go get -v golang.org/x/lint/golint
script:
- golint -set_exit_status ./...
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
matrix:
allow_failures:
- go: tip

3
vendor/github.com/labstack/echo/v4/Makefile generated vendored Normal file
View File

@ -0,0 +1,3 @@
tag:
@git tag `grep -P '^\tversion = ' echo.go|cut -f2 -d'"'`
@git tag|grep -v ^v

View File

@ -10,6 +10,20 @@
[![Twitter](https://img.shields.io/badge/twitter-@labstack-55acee.svg?style=flat-square)](https://twitter.com/labstack) [![Twitter](https://img.shields.io/badge/twitter-@labstack-55acee.svg?style=flat-square)](https://twitter.com/labstack)
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo/master/LICENSE) [![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo/master/LICENSE)
## Supported Go versions
As of version 4.0.0, Echo is available as a [Go module](https://github.com/golang/go/wiki/Modules).
Therefore a Go version capable of understanding /vN suffixed imports is required:
- 1.9.7+
- 1.10.3+
- 1.11+
Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended
way of using Echo going forward.
For older versions, please use the latest v3 tag.
## Feature Overview ## Feature Overview
- Optimized HTTP router which smartly prioritize routes - Optimized HTTP router which smartly prioritize routes
@ -32,7 +46,7 @@ Date: 2018/03/15<br>
Source: https://github.com/vishr/web-framework-benchmark<br> Source: https://github.com/vishr/web-framework-benchmark<br>
Lower is better! Lower is better!
<img src="https://api.labstack.com/chart/bar?values=37223,55382,2985,5265|42013,59865,3350,6424&labels=Static,GitHub%20API,Parse%20API,Gplus%20API&titles=Echo,Gin&colors=lightseagreen,goldenrod&x_title=Routes&y_title=ns/op"> <img src="https://i.imgur.com/I32VdMJ.png">
## [Guide](https://echo.labstack.com/guide) ## [Guide](https://echo.labstack.com/guide)
@ -44,8 +58,8 @@ package main
import ( import (
"net/http" "net/http"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/echo/middleware" "github.com/labstack/echo/v4/middleware"
) )
func main() { func main() {
@ -90,6 +104,7 @@ func hello(c echo.Context) error {
- Improve/fix documentation - Improve/fix documentation
## Credits ## Credits
- [Vishal Rana](https://github.com/vishr) - Author - [Vishal Rana](https://github.com/vishr) - Author
- [Nitin Rana](https://github.com/nr17) - Consultant - [Nitin Rana](https://github.com/nr17) - Consultant
- [Contributors](https://github.com/labstack/echo/graphs/contributors) - [Contributors](https://github.com/labstack/echo/graphs/contributors)

View File

@ -31,9 +31,9 @@ type (
func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
req := c.Request() req := c.Request()
if req.ContentLength == 0 { if req.ContentLength == 0 {
if req.Method == GET || req.Method == DELETE { if req.Method == http.MethodGet || req.Method == http.MethodDelete {
if err = b.bindData(i, c.QueryParams(), "query"); err != nil { if err = b.bindData(i, c.QueryParams(), "query"); err != nil {
return NewHTTPError(http.StatusBadRequest, err.Error()) return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
} }
return return
} }
@ -44,30 +44,28 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
case strings.HasPrefix(ctype, MIMEApplicationJSON): case strings.HasPrefix(ctype, MIMEApplicationJSON):
if err = json.NewDecoder(req.Body).Decode(i); err != nil { if err = json.NewDecoder(req.Body).Decode(i); err != nil {
if ute, ok := err.(*json.UnmarshalTypeError); ok { if ute, ok := err.(*json.UnmarshalTypeError); ok {
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, offset=%v", ute.Type, ute.Value, ute.Offset)) return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset)).SetInternal(err)
} else if se, ok := err.(*json.SyntaxError); ok { } else if se, ok := err.(*json.SyntaxError); ok {
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error())) return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error())).SetInternal(err)
} else {
return NewHTTPError(http.StatusBadRequest, err.Error())
} }
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
} }
case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML): case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):
if err = xml.NewDecoder(req.Body).Decode(i); err != nil { if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
if ute, ok := err.(*xml.UnsupportedTypeError); ok { if ute, ok := err.(*xml.UnsupportedTypeError); ok {
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())) return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())).SetInternal(err)
} else if se, ok := err.(*xml.SyntaxError); ok { } else if se, ok := err.(*xml.SyntaxError); ok {
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error())) return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error())).SetInternal(err)
} else {
return NewHTTPError(http.StatusBadRequest, err.Error())
} }
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
} }
case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm): case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):
params, err := c.FormParams() params, err := c.FormParams()
if err != nil { if err != nil {
return NewHTTPError(http.StatusBadRequest, err.Error()) return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
} }
if err = b.bindData(i, params, "form"); err != nil { if err = b.bindData(i, params, "form"); err != nil {
return NewHTTPError(http.StatusBadRequest, err.Error()) return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
} }
default: default:
return ErrUnsupportedMediaType return ErrUnsupportedMediaType
@ -96,14 +94,29 @@ func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag
inputFieldName = typeField.Name inputFieldName = typeField.Name
// If tag is nil, we inspect if the field is a struct. // If tag is nil, we inspect if the field is a struct.
if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct { if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
err := b.bindData(structField.Addr().Interface(), data, tag) if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
if err != nil {
return err return err
} }
continue continue
} }
} }
inputValue, exists := data[inputFieldName] inputValue, exists := data[inputFieldName]
if !exists {
// Go json.Unmarshal supports case insensitive binding. However the
// url params are bound case sensitive which is inconsistent. To
// fix this we must check all of the map values in a
// case-insensitive search.
inputFieldName = strings.ToLower(inputFieldName)
for k, v := range data {
if strings.ToLower(k) == inputFieldName {
inputValue = v
exists = true
break
}
}
}
if !exists { if !exists {
continue continue
} }
@ -126,10 +139,9 @@ func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag
} }
} }
val.Field(i).Set(slice) val.Field(i).Set(slice)
} else { } else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
return err return err
}
} }
} }
return nil return nil

View File

@ -204,6 +204,7 @@ type (
const ( const (
defaultMemory = 32 << 20 // 32 MB defaultMemory = 32 << 20 // 32 MB
indexPage = "index.html" indexPage = "index.html"
defaultIndent = " "
) )
func (c *context) writeContentType(value string) { func (c *context) writeContentType(value string) {
@ -256,14 +257,13 @@ func (c *context) Scheme() string {
} }
func (c *context) RealIP() string { func (c *context) RealIP() string {
ra := c.request.RemoteAddr
if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" { if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" {
ra = strings.Split(ip, ", ")[0] return strings.Split(ip, ", ")[0]
} else if ip := c.request.Header.Get(HeaderXRealIP); ip != "" {
ra = ip
} else {
ra, _, _ = net.SplitHostPort(ra)
} }
if ip := c.request.Header.Get(HeaderXRealIP); ip != "" {
return ip
}
ra, _, _ := net.SplitHostPort(c.request.RemoteAddr)
return ra return ra
} }
@ -404,24 +404,46 @@ func (c *context) String(code int, s string) (err error) {
return c.Blob(code, MIMETextPlainCharsetUTF8, []byte(s)) return c.Blob(code, MIMETextPlainCharsetUTF8, []byte(s))
} }
func (c *context) JSON(code int, i interface{}) (err error) { func (c *context) jsonPBlob(code int, callback string, i interface{}) (err error) {
enc := json.NewEncoder(c.response)
_, pretty := c.QueryParams()["pretty"] _, pretty := c.QueryParams()["pretty"]
if c.echo.Debug || pretty { if c.echo.Debug || pretty {
return c.JSONPretty(code, i, " ") enc.SetIndent("", " ")
} }
b, err := json.Marshal(i) c.writeContentType(MIMEApplicationJavaScriptCharsetUTF8)
if err != nil { c.response.WriteHeader(code)
if _, err = c.response.Write([]byte(callback + "(")); err != nil {
return return
} }
return c.JSONBlob(code, b) if err = enc.Encode(i); err != nil {
return
}
if _, err = c.response.Write([]byte(");")); err != nil {
return
}
return
}
func (c *context) json(code int, i interface{}, indent string) error {
enc := json.NewEncoder(c.response)
if indent != "" {
enc.SetIndent("", indent)
}
c.writeContentType(MIMEApplicationJSONCharsetUTF8)
c.response.WriteHeader(code)
return enc.Encode(i)
}
func (c *context) JSON(code int, i interface{}) (err error) {
indent := ""
if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty {
indent = defaultIndent
}
return c.json(code, i, indent)
} }
func (c *context) JSONPretty(code int, i interface{}, indent string) (err error) { func (c *context) JSONPretty(code int, i interface{}, indent string) (err error) {
b, err := json.MarshalIndent(i, "", indent) return c.json(code, i, indent)
if err != nil {
return
}
return c.JSONBlob(code, b)
} }
func (c *context) JSONBlob(code int, b []byte) (err error) { func (c *context) JSONBlob(code int, b []byte) (err error) {
@ -429,11 +451,7 @@ func (c *context) JSONBlob(code int, b []byte) (err error) {
} }
func (c *context) JSONP(code int, callback string, i interface{}) (err error) { func (c *context) JSONP(code int, callback string, i interface{}) (err error) {
b, err := json.Marshal(i) return c.jsonPBlob(code, callback, i)
if err != nil {
return
}
return c.JSONPBlob(code, callback, b)
} }
func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) { func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) {
@ -449,24 +467,29 @@ func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) {
return return
} }
func (c *context) XML(code int, i interface{}) (err error) { func (c *context) xml(code int, i interface{}, indent string) (err error) {
_, pretty := c.QueryParams()["pretty"] c.writeContentType(MIMEApplicationXMLCharsetUTF8)
if c.echo.Debug || pretty { c.response.WriteHeader(code)
return c.XMLPretty(code, i, " ") enc := xml.NewEncoder(c.response)
if indent != "" {
enc.Indent("", indent)
} }
b, err := xml.Marshal(i) if _, err = c.response.Write([]byte(xml.Header)); err != nil {
if err != nil {
return return
} }
return c.XMLBlob(code, b) return enc.Encode(i)
}
func (c *context) XML(code int, i interface{}) (err error) {
indent := ""
if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty {
indent = defaultIndent
}
return c.xml(code, i, indent)
} }
func (c *context) XMLPretty(code int, i interface{}, indent string) (err error) { func (c *context) XMLPretty(code int, i interface{}, indent string) (err error) {
b, err := xml.MarshalIndent(i, "", indent) return c.xml(code, i, indent)
if err != nil {
return
}
return c.XMLBlob(code, b)
} }
func (c *context) XMLBlob(code int, b []byte) (err error) { func (c *context) XMLBlob(code int, b []byte) (err error) {
@ -574,3 +597,4 @@ func (c *context) Reset(r *http.Request, w http.ResponseWriter) {
// NOTE: Don't reset because it has to have length c.echo.maxParam at all times // NOTE: Don't reset because it has to have length c.echo.maxParam at all times
// c.pvalues = nil // c.pvalues = nil
} }

View File

@ -8,8 +8,8 @@ Example:
import ( import (
"net/http" "net/http"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/echo/middleware" "github.com/labstack/echo/v4/middleware"
) )
// Handler // Handler
@ -62,7 +62,7 @@ import (
type ( type (
// Echo is the top-level framework instance. // Echo is the top-level framework instance.
Echo struct { Echo struct {
stdLogger *stdLog.Logger StdLogger *stdLog.Logger
colorer *color.Color colorer *color.Color
premiddleware []MiddlewareFunc premiddleware []MiddlewareFunc
middleware []MiddlewareFunc middleware []MiddlewareFunc
@ -103,7 +103,7 @@ type (
// MiddlewareFunc defines a function to process middleware. // MiddlewareFunc defines a function to process middleware.
MiddlewareFunc func(HandlerFunc) HandlerFunc MiddlewareFunc func(HandlerFunc) HandlerFunc
// HandlerFunc defines a function to server HTTP requests. // HandlerFunc defines a function to serve HTTP requests.
HandlerFunc func(Context) error HandlerFunc func(Context) error
// HTTPErrorHandler is a centralized HTTP error handler. // HTTPErrorHandler is a centralized HTTP error handler.
@ -129,17 +129,18 @@ type (
) )
// HTTP methods // HTTP methods
// NOTE: Deprecated, please use the stdlib constants directly instead.
const ( const (
CONNECT = "CONNECT" CONNECT = http.MethodConnect
DELETE = "DELETE" DELETE = http.MethodDelete
GET = "GET" GET = http.MethodGet
HEAD = "HEAD" HEAD = http.MethodHead
OPTIONS = "OPTIONS" OPTIONS = http.MethodOptions
PATCH = "PATCH" PATCH = http.MethodPatch
POST = "POST" POST = http.MethodPost
PROPFIND = "PROPFIND" // PROPFIND = "PROPFIND"
PUT = "PUT" PUT = http.MethodPut
TRACE = "TRACE" TRACE = http.MethodTrace
) )
// MIME types // MIME types
@ -165,6 +166,8 @@ const (
const ( const (
charsetUTF8 = "charset=UTF-8" charsetUTF8 = "charset=UTF-8"
// PROPFIND Method can be used on collection and property resources.
PROPFIND = "PROPFIND"
) )
// Headers // Headers
@ -217,7 +220,8 @@ const (
) )
const ( const (
Version = "3.3.5" // Version of Echo
Version = "4.0.0"
website = "https://echo.labstack.com" website = "https://echo.labstack.com"
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
banner = ` banner = `
@ -234,16 +238,16 @@ ____________________________________O/_______
var ( var (
methods = [...]string{ methods = [...]string{
CONNECT, http.MethodConnect,
DELETE, http.MethodDelete,
GET, http.MethodGet,
HEAD, http.MethodHead,
OPTIONS, http.MethodOptions,
PATCH, http.MethodPatch,
POST, http.MethodPost,
PROPFIND, PROPFIND,
PUT, http.MethodPut,
TRACE, http.MethodTrace,
} }
) )
@ -255,6 +259,12 @@ var (
ErrForbidden = NewHTTPError(http.StatusForbidden) ErrForbidden = NewHTTPError(http.StatusForbidden)
ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed) ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed)
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge)
ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests)
ErrBadRequest = NewHTTPError(http.StatusBadRequest)
ErrBadGateway = NewHTTPError(http.StatusBadGateway)
ErrInternalServerError = NewHTTPError(http.StatusInternalServerError)
ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout)
ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable)
ErrValidatorNotRegistered = errors.New("validator not registered") ErrValidatorNotRegistered = errors.New("validator not registered")
ErrRendererNotRegistered = errors.New("renderer not registered") ErrRendererNotRegistered = errors.New("renderer not registered")
ErrInvalidRedirectCode = errors.New("invalid redirect status code") ErrInvalidRedirectCode = errors.New("invalid redirect status code")
@ -289,7 +299,7 @@ func New() (e *Echo) {
e.HTTPErrorHandler = e.DefaultHTTPErrorHandler e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
e.Binder = &DefaultBinder{} e.Binder = &DefaultBinder{}
e.Logger.SetLevel(log.ERROR) e.Logger.SetLevel(log.ERROR)
e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0) e.StdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
e.pool.New = func() interface{} { e.pool.New = func() interface{} {
return e.NewContext(nil, nil) return e.NewContext(nil, nil)
} }
@ -326,7 +336,7 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
code = he.Code code = he.Code
msg = he.Message msg = he.Message
if he.Internal != nil { if he.Internal != nil {
msg = fmt.Sprintf("%v, %v", err, he.Internal) err = fmt.Errorf("%v, %v", err, he.Internal)
} }
} else if e.Debug { } else if e.Debug {
msg = err.Error() msg = err.Error()
@ -337,11 +347,9 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
msg = Map{"message": msg} msg = Map{"message": msg}
} }
e.Logger.Error(err)
// Send response // Send response
if !c.Response().Committed { if !c.Response().Committed {
if c.Request().Method == HEAD { // Issue #608 if c.Request().Method == http.MethodHead { // Issue #608
err = c.NoContent(code) err = c.NoContent(code)
} else { } else {
err = c.JSON(code, msg) err = c.JSON(code, msg)
@ -365,55 +373,55 @@ func (e *Echo) Use(middleware ...MiddlewareFunc) {
// CONNECT registers a new CONNECT route for a path with matching handler in the // CONNECT registers a new CONNECT route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(CONNECT, path, h, m...) return e.Add(http.MethodConnect, path, h, m...)
} }
// DELETE registers a new DELETE route for a path with matching handler in the router // DELETE registers a new DELETE route for a path with matching handler in the router
// with optional route-level middleware. // with optional route-level middleware.
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(DELETE, path, h, m...) return e.Add(http.MethodDelete, path, h, m...)
} }
// GET registers a new GET route for a path with matching handler in the router // GET registers a new GET route for a path with matching handler in the router
// with optional route-level middleware. // with optional route-level middleware.
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(GET, path, h, m...) return e.Add(http.MethodGet, path, h, m...)
} }
// HEAD registers a new HEAD route for a path with matching handler in the // HEAD registers a new HEAD route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(HEAD, path, h, m...) return e.Add(http.MethodHead, path, h, m...)
} }
// OPTIONS registers a new OPTIONS route for a path with matching handler in the // OPTIONS registers a new OPTIONS route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(OPTIONS, path, h, m...) return e.Add(http.MethodOptions, path, h, m...)
} }
// PATCH registers a new PATCH route for a path with matching handler in the // PATCH registers a new PATCH route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(PATCH, path, h, m...) return e.Add(http.MethodPatch, path, h, m...)
} }
// POST registers a new POST route for a path with matching handler in the // POST registers a new POST route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(POST, path, h, m...) return e.Add(http.MethodPost, path, h, m...)
} }
// PUT registers a new PUT route for a path with matching handler in the // PUT registers a new PUT route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(PUT, path, h, m...) return e.Add(http.MethodPut, path, h, m...)
} }
// TRACE registers a new TRACE route for a path with matching handler in the // TRACE registers a new TRACE route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return e.Add(TRACE, path, h, m...) return e.Add(http.MethodTrace, path, h, m...)
} }
// Any registers a new route for all HTTP methods and path with matching handler // Any registers a new route for all HTTP methods and path with matching handler
@ -462,11 +470,11 @@ func static(i i, prefix, root string) *Route {
return i.GET(prefix+"/*", h) return i.GET(prefix+"/*", h)
} }
// File registers a new route with path to serve a static file. // File registers a new route with path to serve a static file with optional route-level middleware.
func (e *Echo) File(path, file string) *Route { func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route {
return e.GET(path, func(c Context) error { return e.GET(path, func(c Context) error {
return c.File(file) return c.File(file)
}) }, m...)
} }
// Add registers a new route for an HTTP method and path with matching handler // Add registers a new route for an HTTP method and path with matching handler
@ -559,26 +567,17 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c := e.pool.Get().(*context) c := e.pool.Get().(*context)
c.Reset(r, w) c.Reset(r, w)
m := r.Method
h := NotFoundHandler h := NotFoundHandler
if e.premiddleware == nil { if e.premiddleware == nil {
path := r.URL.RawPath e.router.Find(r.Method, getPath(r), c)
if path == "" {
path = r.URL.Path
}
e.router.Find(m, getPath(r), c)
h = c.Handler() h = c.Handler()
for i := len(e.middleware) - 1; i >= 0; i-- { for i := len(e.middleware) - 1; i >= 0; i-- {
h = e.middleware[i](h) h = e.middleware[i](h)
} }
} else { } else {
h = func(c Context) error { h = func(c Context) error {
path := r.URL.RawPath e.router.Find(r.Method, getPath(r), c)
if path == "" {
path = r.URL.Path
}
e.router.Find(m, getPath(r), c)
h := c.Handler() h := c.Handler()
for i := len(e.middleware) - 1; i >= 0; i-- { for i := len(e.middleware) - 1; i >= 0; i-- {
h = e.middleware[i](h) h = e.middleware[i](h)
@ -622,10 +621,6 @@ func (e *Echo) StartTLS(address string, certFile, keyFile string) (err error) {
// StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org. // StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org.
func (e *Echo) StartAutoTLS(address string) error { func (e *Echo) StartAutoTLS(address string) error {
if e.Listener == nil {
go http.ListenAndServe(":http", e.AutoTLSManager.HTTPHandler(nil))
}
s := e.TLSServer s := e.TLSServer
s.TLSConfig = new(tls.Config) s.TLSConfig = new(tls.Config)
s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate
@ -645,7 +640,7 @@ func (e *Echo) startTLS(address string) error {
func (e *Echo) StartServer(s *http.Server) (err error) { func (e *Echo) StartServer(s *http.Server) (err error) {
// Setup // Setup
e.colorer.SetOutput(e.Logger.Output()) e.colorer.SetOutput(e.Logger.Output())
s.ErrorLog = e.stdLogger s.ErrorLog = e.StdLogger
s.Handler = e s.Handler = e
if e.Debug { if e.Debug {
e.Logger.SetLevel(log.DEBUG) e.Logger.SetLevel(log.DEBUG)
@ -689,7 +684,7 @@ func (e *Echo) Close() error {
return e.Server.Close() return e.Server.Close()
} }
// Shutdown stops server the gracefully. // Shutdown stops the server gracefully.
// It internally calls `http.Server#Shutdown()`. // It internally calls `http.Server#Shutdown()`.
func (e *Echo) Shutdown(ctx stdContext.Context) error { func (e *Echo) Shutdown(ctx stdContext.Context) error {
if err := e.TLSServer.Shutdown(ctx); err != nil { if err := e.TLSServer.Shutdown(ctx); err != nil {
@ -712,6 +707,12 @@ func (he *HTTPError) Error() string {
return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message) return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message)
} }
// SetInternal sets error to HTTPError.Internal
func (he *HTTPError) SetInternal(err error) *HTTPError {
he.Internal = err
return he
}
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`. // WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
func WrapHandler(h http.Handler) HandlerFunc { func WrapHandler(h http.Handler) HandlerFunc {
return func(c Context) error { return func(c Context) error {

13
vendor/github.com/labstack/echo/v4/go.mod generated vendored Normal file
View File

@ -0,0 +1,13 @@
module github.com/labstack/echo/v4
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/labstack/gommon v0.2.8
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/stretchr/testify v1.3.0
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc // indirect
)

23
vendor/github.com/labstack/echo/v4/go.sum generated vendored Normal file
View File

@ -0,0 +1,23 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@ -1,6 +1,7 @@
package echo package echo
import ( import (
"net/http"
"path" "path"
) )
@ -29,47 +30,47 @@ func (g *Group) Use(middleware ...MiddlewareFunc) {
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group. // CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(CONNECT, path, h, m...) return g.Add(http.MethodConnect, path, h, m...)
} }
// DELETE implements `Echo#DELETE()` for sub-routes within the Group. // DELETE implements `Echo#DELETE()` for sub-routes within the Group.
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(DELETE, path, h, m...) return g.Add(http.MethodDelete, path, h, m...)
} }
// GET implements `Echo#GET()` for sub-routes within the Group. // GET implements `Echo#GET()` for sub-routes within the Group.
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(GET, path, h, m...) return g.Add(http.MethodGet, path, h, m...)
} }
// HEAD implements `Echo#HEAD()` for sub-routes within the Group. // HEAD implements `Echo#HEAD()` for sub-routes within the Group.
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(HEAD, path, h, m...) return g.Add(http.MethodHead, path, h, m...)
} }
// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group. // OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group.
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(OPTIONS, path, h, m...) return g.Add(http.MethodOptions, path, h, m...)
} }
// PATCH implements `Echo#PATCH()` for sub-routes within the Group. // PATCH implements `Echo#PATCH()` for sub-routes within the Group.
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(PATCH, path, h, m...) return g.Add(http.MethodPatch, path, h, m...)
} }
// POST implements `Echo#POST()` for sub-routes within the Group. // POST implements `Echo#POST()` for sub-routes within the Group.
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(POST, path, h, m...) return g.Add(http.MethodPost, path, h, m...)
} }
// PUT implements `Echo#PUT()` for sub-routes within the Group. // PUT implements `Echo#PUT()` for sub-routes within the Group.
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(PUT, path, h, m...) return g.Add(http.MethodPut, path, h, m...)
} }
// TRACE implements `Echo#TRACE()` for sub-routes within the Group. // TRACE implements `Echo#TRACE()` for sub-routes within the Group.
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
return g.Add(TRACE, path, h, m...) return g.Add(http.MethodTrace, path, h, m...)
} }
// Any implements `Echo#Any()` for sub-routes within the Group. // Any implements `Echo#Any()` for sub-routes within the Group.

View File

@ -15,6 +15,7 @@ type (
SetPrefix(p string) SetPrefix(p string)
Level() log.Lvl Level() log.Lvl
SetLevel(v log.Lvl) SetLevel(v log.Lvl)
SetHeader(h string)
Print(i ...interface{}) Print(i ...interface{})
Printf(format string, args ...interface{}) Printf(format string, args ...interface{})
Printj(j log.JSON) Printj(j log.JSON)

View File

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (

View File

@ -8,7 +8,7 @@ import (
"net" "net"
"net/http" "net/http"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (
@ -105,7 +105,3 @@ func (w *bodyDumpResponseWriter) Flush() {
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.ResponseWriter.(http.Hijacker).Hijack() return w.ResponseWriter.(http.Hijacker).Hijack()
} }
func (w *bodyDumpResponseWriter) CloseNotify() <-chan bool {
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

View File

@ -5,7 +5,7 @@ import (
"io" "io"
"sync" "sync"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/gommon/bytes" "github.com/labstack/gommon/bytes"
) )

View File

@ -9,7 +9,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (
@ -116,7 +116,3 @@ func (w *gzipResponseWriter) Flush() {
func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.ResponseWriter.(http.Hijacker).Hijack() return w.ResponseWriter.(http.Hijacker).Hijack()
} }
func (w *gzipResponseWriter) CloseNotify() <-chan bool {
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

View File

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (
@ -24,7 +24,7 @@ type (
AllowMethods []string `yaml:"allow_methods"` AllowMethods []string `yaml:"allow_methods"`
// AllowHeaders defines a list of request headers that can be used when // AllowHeaders defines a list of request headers that can be used when
// making the actual request. This in response to a preflight request. // making the actual request. This is in response to a preflight request.
// Optional. Default value []string{}. // Optional. Default value []string{}.
AllowHeaders []string `yaml:"allow_headers"` AllowHeaders []string `yaml:"allow_headers"`
@ -52,7 +52,7 @@ var (
DefaultCORSConfig = CORSConfig{ DefaultCORSConfig = CORSConfig{
Skipper: DefaultSkipper, Skipper: DefaultSkipper,
AllowOrigins: []string{"*"}, AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE}, AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
} }
) )
@ -94,6 +94,10 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
// Check allowed origins // Check allowed origins
for _, o := range config.AllowOrigins { for _, o := range config.AllowOrigins {
if o == "*" && config.AllowCredentials {
allowOrigin = origin
break
}
if o == "*" || o == origin { if o == "*" || o == origin {
allowOrigin = o allowOrigin = o
break break
@ -101,7 +105,7 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
} }
// Simple request // Simple request
if req.Method != echo.OPTIONS { if req.Method != http.MethodOptions {
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin) res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin) res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
if config.AllowCredentials { if config.AllowCredentials {

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/gommon/random" "github.com/labstack/gommon/random"
) )
@ -135,7 +135,7 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
} }
switch req.Method { switch req.Method {
case echo.GET, echo.HEAD, echo.OPTIONS, echo.TRACE: case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
default: default:
// Validate token only for requests which are not defined as 'safe' by RFC7231 // Validate token only for requests which are not defined as 'safe' by RFC7231
clientToken, err := extractor(c) clientToken, err := extractor(c)

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (
@ -16,6 +16,16 @@ type (
// Skipper defines a function to skip middleware. // Skipper defines a function to skip middleware.
Skipper Skipper Skipper Skipper
// BeforeFunc defines a function which is executed just before the middleware.
BeforeFunc BeforeFunc
// SuccessHandler defines a function which is executed for a valid token.
SuccessHandler JWTSuccessHandler
// ErrorHandler defines a function which is executed for an invalid token.
// It may be used to define a custom JWT error.
ErrorHandler JWTErrorHandler
// Signing key to validate token. // Signing key to validate token.
// Required. // Required.
SigningKey interface{} SigningKey interface{}
@ -48,6 +58,12 @@ type (
keyFunc jwt.Keyfunc keyFunc jwt.Keyfunc
} }
// JWTSuccessHandler defines a function which is executed for a valid token.
JWTSuccessHandler func(echo.Context)
// JWTErrorHandler defines a function which is executed for an invalid token.
JWTErrorHandler func(error) error
jwtExtractor func(echo.Context) (string, error) jwtExtractor func(echo.Context) (string, error)
) )
@ -59,7 +75,6 @@ const (
// Errors // Errors
var ( var (
ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt") ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt")
ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "invalid or expired jwt")
) )
var ( var (
@ -137,8 +152,15 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
return next(c) return next(c)
} }
if config.BeforeFunc != nil {
config.BeforeFunc(c)
}
auth, err := extractor(c) auth, err := extractor(c)
if err != nil { if err != nil {
if config.ErrorHandler != nil {
return config.ErrorHandler(err)
}
return err return err
} }
token := new(jwt.Token) token := new(jwt.Token)
@ -153,11 +175,17 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
if err == nil && token.Valid { if err == nil && token.Valid {
// Store user information from token into context. // Store user information from token into context.
c.Set(config.ContextKey, token) c.Set(config.ContextKey, token)
if config.SuccessHandler != nil {
config.SuccessHandler(c)
}
return next(c) return next(c)
} }
if config.ErrorHandler != nil {
return config.ErrorHandler(err)
}
return &echo.HTTPError{ return &echo.HTTPError{
Code: ErrJWTInvalid.Code, Code: http.StatusUnauthorized,
Message: ErrJWTInvalid.Message, Message: "invalid or expired jwt",
Internal: err, Internal: err,
} }
} }

View File

@ -5,7 +5,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (

View File

@ -9,7 +9,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/gommon/color" "github.com/labstack/gommon/color"
"github.com/valyala/fasttemplate" "github.com/valyala/fasttemplate"
) )
@ -33,9 +33,11 @@ type (
// - host // - host
// - method // - method
// - path // - path
// - protocol
// - referer // - referer
// - user_agent // - user_agent
// - status // - status
// - error
// - latency (In nanoseconds) // - latency (In nanoseconds)
// - latency_human (Human readable) // - latency_human (Human readable)
// - bytes_in (Bytes received) // - bytes_in (Bytes received)
@ -66,10 +68,10 @@ var (
// DefaultLoggerConfig is the default Logger middleware config. // DefaultLoggerConfig is the default Logger middleware config.
DefaultLoggerConfig = LoggerConfig{ DefaultLoggerConfig = LoggerConfig{
Skipper: DefaultSkipper, Skipper: DefaultSkipper,
Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` + Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}",` +
`"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` + `"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` +
`"latency_human":"${latency_human}","bytes_in":${bytes_in},` + `"status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}"` +
`"bytes_out":${bytes_out}}` + "\n", `,"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n",
CustomTimeFormat: "2006-01-02 15:04:05.00000", CustomTimeFormat: "2006-01-02 15:04:05.00000",
Output: os.Stdout, Output: os.Stdout,
colorer: color.New(), colorer: color.New(),
@ -153,6 +155,8 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
p = "/" p = "/"
} }
return buf.WriteString(p) return buf.WriteString(p)
case "protocol":
return buf.WriteString(req.Proto)
case "referer": case "referer":
return buf.WriteString(req.Referer()) return buf.WriteString(req.Referer())
case "user_agent": case "user_agent":
@ -169,6 +173,10 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
s = config.colorer.Cyan(n) s = config.colorer.Cyan(n)
} }
return buf.WriteString(s) return buf.WriteString(s)
case "error":
if err != nil {
return buf.WriteString(err.Error())
}
case "latency": case "latency":
l := stop.Sub(start) l := stop.Sub(start)
return buf.WriteString(strconv.FormatInt(int64(l), 10)) return buf.WriteString(strconv.FormatInt(int64(l), 10))

View File

@ -1,6 +1,10 @@
package middleware package middleware
import "github.com/labstack/echo" import (
"net/http"
"github.com/labstack/echo/v4"
)
type ( type (
// MethodOverrideConfig defines the config for MethodOverride middleware. // MethodOverrideConfig defines the config for MethodOverride middleware.
@ -52,7 +56,7 @@ func MethodOverrideWithConfig(config MethodOverrideConfig) echo.MiddlewareFunc {
} }
req := c.Request() req := c.Request()
if req.Method == echo.POST { if req.Method == http.MethodPost {
m := config.Getter(c) m := config.Getter(c)
if m != "" { if m != "" {
req.Method = m req.Method = m

View File

@ -5,13 +5,16 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (
// Skipper defines a function to skip middleware. Returning true skips processing // Skipper defines a function to skip middleware. Returning true skips processing
// the middleware. // the middleware.
Skipper func(c echo.Context) bool Skipper func(echo.Context) bool
// BeforeFunc defines a function which is executed just before the middleware.
BeforeFunc func(echo.Context)
) )
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer { func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {

View File

@ -6,7 +6,6 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"net/http/httputil"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
@ -14,7 +13,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
// TODO: Handle TLS proxy // TODO: Handle TLS proxy
@ -38,6 +37,14 @@ type (
// "/users/*/orders/*": "/user/$1/order/$2", // "/users/*/orders/*": "/user/$1/order/$2",
Rewrite map[string]string Rewrite map[string]string
// Context key to store selected ProxyTarget into context.
// Optional. Default value "target".
ContextKey string
// To customize the transport to remote.
// Examples: If custom TLS certificates are required.
Transport http.RoundTripper
rewriteRegex map[*regexp.Regexp]string rewriteRegex map[*regexp.Regexp]string
} }
@ -45,13 +52,14 @@ type (
ProxyTarget struct { ProxyTarget struct {
Name string Name string
URL *url.URL URL *url.URL
Meta echo.Map
} }
// ProxyBalancer defines an interface to implement a load balancing technique. // ProxyBalancer defines an interface to implement a load balancing technique.
ProxyBalancer interface { ProxyBalancer interface {
AddTarget(*ProxyTarget) bool AddTarget(*ProxyTarget) bool
RemoveTarget(string) bool RemoveTarget(string) bool
Next() *ProxyTarget Next(echo.Context) *ProxyTarget
} }
commonBalancer struct { commonBalancer struct {
@ -76,13 +84,10 @@ var (
// DefaultProxyConfig is the default Proxy middleware config. // DefaultProxyConfig is the default Proxy middleware config.
DefaultProxyConfig = ProxyConfig{ DefaultProxyConfig = ProxyConfig{
Skipper: DefaultSkipper, Skipper: DefaultSkipper,
ContextKey: "target",
} }
) )
func proxyHTTP(t *ProxyTarget) http.Handler {
return httputil.NewSingleHostReverseProxy(t.URL)
}
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
in, _, err := c.Response().Hijack() in, _, err := c.Response().Hijack()
@ -164,7 +169,7 @@ func (b *commonBalancer) RemoveTarget(name string) bool {
} }
// Next randomly returns an upstream target. // Next randomly returns an upstream target.
func (b *randomBalancer) Next() *ProxyTarget { func (b *randomBalancer) Next(c echo.Context) *ProxyTarget {
if b.random == nil { if b.random == nil {
b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
} }
@ -174,7 +179,7 @@ func (b *randomBalancer) Next() *ProxyTarget {
} }
// Next returns an upstream target using round-robin technique. // Next returns an upstream target using round-robin technique.
func (b *roundRobinBalancer) Next() *ProxyTarget { func (b *roundRobinBalancer) Next(c echo.Context) *ProxyTarget {
b.i = b.i % uint32(len(b.targets)) b.i = b.i % uint32(len(b.targets))
t := b.targets[b.i] t := b.targets[b.i]
atomic.AddUint32(&b.i, 1) atomic.AddUint32(&b.i, 1)
@ -216,7 +221,8 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
req := c.Request() req := c.Request()
res := c.Response() res := c.Response()
tgt := config.Balancer.Next() tgt := config.Balancer.Next(c)
c.Set(config.ContextKey, tgt)
// Rewrite // Rewrite
for k, v := range config.rewriteRegex { for k, v := range config.rewriteRegex {
@ -243,7 +249,7 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
proxyRaw(tgt, c).ServeHTTP(res, req) proxyRaw(tgt, c).ServeHTTP(res, req)
case req.Header.Get(echo.HeaderAccept) == "text/event-stream": case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
default: default:
proxyHTTP(tgt).ServeHTTP(res, req) proxyHTTP(tgt, c, config).ServeHTTP(res, req)
} }
return return

View File

@ -0,0 +1,25 @@
// +build go1.11
package middleware
import (
"fmt"
"net/http"
"net/http/httputil"
"github.com/labstack/echo/v4"
)
func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
proxy := httputil.NewSingleHostReverseProxy(tgt.URL)
proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) {
desc := tgt.URL.String()
if tgt.Name != "" {
desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String())
}
c.Logger().Errorf("remote %s unreachable, could not forward: %v", desc, err)
c.Error(echo.NewHTTPError(http.StatusServiceUnavailable))
}
proxy.Transport = config.Transport
return proxy
}

View File

@ -0,0 +1,14 @@
// +build !go1.11
package middleware
import (
"net/http"
"net/http/httputil"
"github.com/labstack/echo/v4"
)
func proxyHTTP(t *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
return httputil.NewSingleHostReverseProxy(t.URL)
}

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"runtime" "runtime"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (

View File

@ -3,7 +3,7 @@ package middleware
import ( import (
"net/http" "net/http"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
// RedirectConfig defines the config for Redirect middleware. // RedirectConfig defines the config for Redirect middleware.

View File

@ -1,7 +1,7 @@
package middleware package middleware
import ( import (
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/gommon/random" "github.com/labstack/gommon/random"
) )

View File

@ -4,7 +4,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (
@ -57,7 +57,8 @@ func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
// Initialize // Initialize
for k, v := range config.Rules { for k, v := range config.Rules {
k = strings.Replace(k, "*", "(\\S*)", -1) k = strings.Replace(k, "*", "(.*)", -1)
k = k + "$"
config.rulesRegex[regexp.MustCompile(k)] = v config.rulesRegex[regexp.MustCompile(k)] = v
} }
@ -74,9 +75,9 @@ func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
replacer := captureTokens(k, req.URL.Path) replacer := captureTokens(k, req.URL.Path)
if replacer != nil { if replacer != nil {
req.URL.Path = replacer.Replace(v) req.URL.Path = replacer.Replace(v)
break
} }
} }
return next(c) return next(c)
} }
} }

View File

@ -3,7 +3,7 @@ package middleware
import ( import (
"fmt" "fmt"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (

View File

@ -1,7 +1,7 @@
package middleware package middleware
import ( import (
"github.com/labstack/echo" "github.com/labstack/echo/v4"
) )
type ( type (

View File

@ -10,7 +10,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/labstack/echo" "github.com/labstack/echo/v4"
"github.com/labstack/gommon/bytes" "github.com/labstack/gommon/bytes"
) )

View File

@ -91,15 +91,6 @@ func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return r.Writer.(http.Hijacker).Hijack() return r.Writer.(http.Hijacker).Hijack()
} }
// CloseNotify implements the http.CloseNotifier interface to allow detecting
// when the underlying connection has gone away.
// This mechanism can be used to cancel long operations on the server if the
// client has disconnected before the response is ready.
// See [http.CloseNotifier](https://golang.org/pkg/net/http/#CloseNotifier)
func (r *Response) CloseNotify() <-chan bool {
return r.Writer.(http.CloseNotifier).CloseNotify()
}
func (r *Response) reset(w http.ResponseWriter) { func (r *Response) reset(w http.ResponseWriter) {
r.beforeFuncs = nil r.beforeFuncs = nil
r.afterFuncs = nil r.afterFuncs = nil

View File

@ -1,5 +1,7 @@
package echo package echo
import "net/http"
type ( type (
// Router is the registry of all registered routes for an `Echo` instance for // Router is the registry of all registered routes for an `Echo` instance for
// request matching and URL path parameter parsing. // request matching and URL path parameter parsing.
@ -79,7 +81,7 @@ func (r *Router) Add(method, path string, h HandlerFunc) {
r.insert(method, path[:i], h, pkind, ppath, pnames) r.insert(method, path[:i], h, pkind, ppath, pnames)
return return
} }
r.insert(method, path[:i], nil, pkind, ppath, pnames) r.insert(method, path[:i], nil, pkind, "", nil)
} else if path[i] == '*' { } else if path[i] == '*' {
r.insert(method, path[:i], nil, skind, "", nil) r.insert(method, path[:i], nil, skind, "", nil)
pnames = append(pnames, "*") pnames = append(pnames, "*")
@ -226,50 +228,50 @@ func (n *node) findChildByKind(t kind) *node {
func (n *node) addHandler(method string, h HandlerFunc) { func (n *node) addHandler(method string, h HandlerFunc) {
switch method { switch method {
case CONNECT: case http.MethodConnect:
n.methodHandler.connect = h n.methodHandler.connect = h
case DELETE: case http.MethodDelete:
n.methodHandler.delete = h n.methodHandler.delete = h
case GET: case http.MethodGet:
n.methodHandler.get = h n.methodHandler.get = h
case HEAD: case http.MethodHead:
n.methodHandler.head = h n.methodHandler.head = h
case OPTIONS: case http.MethodOptions:
n.methodHandler.options = h n.methodHandler.options = h
case PATCH: case http.MethodPatch:
n.methodHandler.patch = h n.methodHandler.patch = h
case POST: case http.MethodPost:
n.methodHandler.post = h n.methodHandler.post = h
case PROPFIND: case PROPFIND:
n.methodHandler.propfind = h n.methodHandler.propfind = h
case PUT: case http.MethodPut:
n.methodHandler.put = h n.methodHandler.put = h
case TRACE: case http.MethodTrace:
n.methodHandler.trace = h n.methodHandler.trace = h
} }
} }
func (n *node) findHandler(method string) HandlerFunc { func (n *node) findHandler(method string) HandlerFunc {
switch method { switch method {
case CONNECT: case http.MethodConnect:
return n.methodHandler.connect return n.methodHandler.connect
case DELETE: case http.MethodDelete:
return n.methodHandler.delete return n.methodHandler.delete
case GET: case http.MethodGet:
return n.methodHandler.get return n.methodHandler.get
case HEAD: case http.MethodHead:
return n.methodHandler.head return n.methodHandler.head
case OPTIONS: case http.MethodOptions:
return n.methodHandler.options return n.methodHandler.options
case PATCH: case http.MethodPatch:
return n.methodHandler.patch return n.methodHandler.patch
case POST: case http.MethodPost:
return n.methodHandler.post return n.methodHandler.post
case PROPFIND: case PROPFIND:
return n.methodHandler.propfind return n.methodHandler.propfind
case PUT: case http.MethodPut:
return n.methodHandler.put return n.methodHandler.put
case TRACE: case http.MethodTrace:
return n.methodHandler.trace return n.methodHandler.trace
default: default:
return nil return nil
@ -311,7 +313,7 @@ func (r *Router) Find(method, path string, c Context) {
// Search order static > param > any // Search order static > param > any
for { for {
if search == "" { if search == "" {
goto End break
} }
pl := 0 // Prefix length pl := 0 // Prefix length
@ -346,7 +348,7 @@ func (r *Router) Find(method, path string, c Context) {
} }
if search == "" { if search == "" {
goto End break
} }
// Static node // Static node
@ -403,10 +405,9 @@ func (r *Router) Find(method, path string, c Context) {
return return
} }
pvalues[len(cn.pnames)-1] = search pvalues[len(cn.pnames)-1] = search
goto End break
} }
End:
ctx.handler = cn.findHandler(method) ctx.handler = cn.findHandler(method)
ctx.path = cn.ppath ctx.path = cn.ppath
ctx.pnames = cn.pnames ctx.pnames = cn.pnames

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 labstack Copyright (c) 2018 labstack
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -7,12 +7,12 @@ import (
) )
type ( type (
Bytes struct { // Bytes struct
} Bytes struct{}
) )
const ( const (
B = 1 << (10 * iota) _ = 1.0 << (10 * iota) // ignore first value by assigning to blank identifier
KB KB
MB MB
GB GB
@ -22,7 +22,7 @@ const (
) )
var ( var (
pattern = regexp.MustCompile(`(?i)^(-?\d+)([KMGTP]B?|B)$`) pattern = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)([KMGTPE]B?|B?)$`)
global = New() global = New()
) )
@ -38,29 +38,31 @@ func (*Bytes) Format(b int64) string {
value := float64(b) value := float64(b)
switch { switch {
case b < KB: case b >= EB:
return strconv.FormatInt(b, 10) + "B" value /= EB
case b < MB: multiple = "EB"
value /= KB case b >= PB:
multiple = "KB"
case b < MB:
value /= KB
multiple = "KB"
case b < GB:
value /= MB
multiple = "MB"
case b < TB:
value /= GB
multiple = "GB"
case b < PB:
value /= TB
multiple = "TB"
case b < EB:
value /= PB value /= PB
multiple = "PB" multiple = "PB"
case b >= TB:
value /= TB
multiple = "TB"
case b >= GB:
value /= GB
multiple = "GB"
case b >= MB:
value /= MB
multiple = "MB"
case b >= KB:
value /= KB
multiple = "KB"
case b == 0:
return "0"
default:
return strconv.FormatInt(b, 10) + "B"
} }
return fmt.Sprintf("%.02f%s", value, multiple) return fmt.Sprintf("%.2f%s", value, multiple)
} }
// Parse parses human readable bytes string to bytes integer. // Parse parses human readable bytes string to bytes integer.
@ -72,27 +74,27 @@ func (*Bytes) Parse(value string) (i int64, err error) {
} }
bytesString := parts[1] bytesString := parts[1]
multiple := parts[2] multiple := parts[2]
bytes, err := strconv.ParseInt(bytesString, 10, 64) bytes, err := strconv.ParseFloat(bytesString, 64)
if err != nil { if err != nil {
return return
} }
switch multiple { switch multiple {
case "B": default:
return bytes * B, nil return int64(bytes), nil
case "K", "KB": case "K", "KB":
return bytes * KB, nil return int64(bytes * KB), nil
case "M", "MB": case "M", "MB":
return bytes * MB, nil return int64(bytes * MB), nil
case "G", "GB": case "G", "GB":
return bytes * GB, nil return int64(bytes * GB), nil
case "T", "TB": case "T", "TB":
return bytes * TB, nil return int64(bytes * TB), nil
case "P", "PB": case "P", "PB":
return bytes * PB, nil return int64(bytes * PB), nil
case "E", "EB":
return int64(bytes * EB), nil
} }
return
} }
// Format wraps global Bytes's Format function. // Format wraps global Bytes's Format function.

View File

@ -8,11 +8,10 @@ import (
"os" "os"
"path" "path"
"runtime" "runtime"
"strconv"
"sync" "sync"
"time" "time"
"strconv"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/valyala/fasttemplate" "github.com/valyala/fasttemplate"
@ -23,6 +22,7 @@ type (
Logger struct { Logger struct {
prefix string prefix string
level Lvl level Lvl
skip int
output io.Writer output io.Writer
template *fasttemplate.Template template *fasttemplate.Template
levels []string levels []string
@ -42,6 +42,8 @@ const (
WARN WARN
ERROR ERROR
OFF OFF
panicLevel
fatalLevel
) )
var ( var (
@ -50,9 +52,14 @@ var (
`"file":"${short_file}","line":"${line}"}` `"file":"${short_file}","line":"${line}"}`
) )
func init() {
global.skip = 3
}
func New(prefix string) (l *Logger) { func New(prefix string) (l *Logger) {
l = &Logger{ l = &Logger{
level: INFO, level: INFO,
skip: 2,
prefix: prefix, prefix: prefix,
template: l.newTemplate(defaultHeader), template: l.newTemplate(defaultHeader),
color: color.New(), color: color.New(),
@ -74,6 +81,9 @@ func (l *Logger) initLevels() {
l.color.Green("INFO"), l.color.Green("INFO"),
l.color.Yellow("WARN"), l.color.Yellow("WARN"),
l.color.Red("ERROR"), l.color.Red("ERROR"),
"",
l.color.Yellow("PANIC", color.U),
l.color.Red("FATAL", color.U),
} }
} }
@ -188,32 +198,32 @@ func (l *Logger) Errorj(j JSON) {
} }
func (l *Logger) Fatal(i ...interface{}) { func (l *Logger) Fatal(i ...interface{}) {
l.Print(i...) l.log(fatalLevel, "", i...)
os.Exit(1) os.Exit(1)
} }
func (l *Logger) Fatalf(format string, args ...interface{}) { func (l *Logger) Fatalf(format string, args ...interface{}) {
l.Printf(format, args...) l.log(fatalLevel, format, args...)
os.Exit(1) os.Exit(1)
} }
func (l *Logger) Fatalj(j JSON) { func (l *Logger) Fatalj(j JSON) {
l.Printj(j) l.log(fatalLevel, "json", j)
os.Exit(1) os.Exit(1)
} }
func (l *Logger) Panic(i ...interface{}) { func (l *Logger) Panic(i ...interface{}) {
l.Print(i...) l.log(panicLevel, "", i...)
panic(fmt.Sprint(i...)) panic(fmt.Sprint(i...))
} }
func (l *Logger) Panicf(format string, args ...interface{}) { func (l *Logger) Panicf(format string, args ...interface{}) {
l.Printf(format, args...) l.log(panicLevel, format, args...)
panic(fmt.Sprintf(format, args)) panic(fmt.Sprintf(format, args...))
} }
func (l *Logger) Panicj(j JSON) { func (l *Logger) Panicj(j JSON) {
l.Printj(j) l.log(panicLevel, "json", j)
panic(j) panic(j)
} }
@ -343,7 +353,7 @@ func (l *Logger) log(v Lvl, format string, args ...interface{}) {
buf := l.bufferPool.Get().(*bytes.Buffer) buf := l.bufferPool.Get().(*bytes.Buffer)
buf.Reset() buf.Reset()
defer l.bufferPool.Put(buf) defer l.bufferPool.Put(buf)
_, file, line, _ := runtime.Caller(3) _, file, line, _ := runtime.Caller(l.skip)
if v >= l.level || v == 0 { if v >= l.level || v == 0 {
message := "" message := ""

View File

@ -13,7 +13,7 @@ type (
// Charsets // Charsets
const ( const (
Uppercase string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" Uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Lowercase = "abcdefghijklmnopqrstuvwxyz" Lowercase = "abcdefghijklmnopqrstuvwxyz"
Alphabetic = Uppercase + Lowercase Alphabetic = Uppercase + Lowercase
Numeric = "0123456789" Numeric = "0123456789"

View File

@ -2,7 +2,8 @@ language: go
go: go:
- tip - tip
sudo: false before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script: script:
- go test -v - $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw

View File

@ -1,5 +1,10 @@
# go-colorable # go-colorable
[![Godoc Reference](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable)
[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable)
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-colorable/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-colorable?branch=master)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable)
Colorable writer for windows. Colorable writer for windows.
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)

View File

@ -0,0 +1,29 @@
// +build appengine
package colorable
import (
"io"
"os"
_ "github.com/mattn/go-isatty"
)
// NewColorable return new instance of Writer which handle escape sequence.
func NewColorable(file *os.File) io.Writer {
if file == nil {
panic("nil passed instead of *os.File to NewColorable()")
}
return file
}
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
func NewColorableStdout() io.Writer {
return os.Stdout
}
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
func NewColorableStderr() io.Writer {
return os.Stderr
}

View File

@ -1,10 +1,13 @@
// +build !windows // +build !windows
// +build !appengine
package colorable package colorable
import ( import (
"io" "io"
"os" "os"
_ "github.com/mattn/go-isatty"
) )
// NewColorable return new instance of Writer which handle escape sequence. // NewColorable return new instance of Writer which handle escape sequence.

View File

@ -1,3 +1,6 @@
// +build windows
// +build !appengine
package colorable package colorable
import ( import (
@ -65,12 +68,13 @@ var (
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
) )
// Writer provide colorable Writer to the console
type Writer struct { type Writer struct {
out io.Writer out io.Writer
handle syscall.Handle handle syscall.Handle
lastbuf bytes.Buffer
oldattr word oldattr word
oldpos coord oldpos coord
} }
@ -86,9 +90,8 @@ func NewColorable(file *os.File) io.Writer {
handle := syscall.Handle(file.Fd()) handle := syscall.Handle(file.Fd())
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
} else {
return file
} }
return file
} }
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. // NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
@ -360,6 +363,45 @@ var color256 = map[int]int{
255: 0xeeeeee, 255: 0xeeeeee,
} }
// `\033]0;TITLESTR\007`
func doTitleSequence(er *bytes.Reader) error {
var c byte
var err error
c, err = er.ReadByte()
if err != nil {
return err
}
if c != '0' && c != '2' {
return nil
}
c, err = er.ReadByte()
if err != nil {
return err
}
if c != ';' {
return nil
}
title := make([]byte, 0, 80)
for {
c, err = er.ReadByte()
if err != nil {
return err
}
if c == 0x07 || c == '\n' {
break
}
title = append(title, c)
}
if len(title) > 0 {
title8, err := syscall.UTF16PtrFromString(string(title))
if err == nil {
procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8)))
}
}
return nil
}
// Write write data on console // Write write data on console
func (w *Writer) Write(data []byte) (n int, err error) { func (w *Writer) Write(data []byte) (n int, err error) {
var csbi consoleScreenBufferInfo var csbi consoleScreenBufferInfo
@ -369,11 +411,6 @@ func (w *Writer) Write(data []byte) (n int, err error) {
var bw [1]byte var bw [1]byte
loop: loop:
for { for {
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
if r1 == 0 {
break loop
}
c1, err := er.ReadByte() c1, err := er.ReadByte()
if err != nil { if err != nil {
break loop break loop
@ -385,12 +422,16 @@ loop:
} }
c2, err := er.ReadByte() c2, err := er.ReadByte()
if err != nil { if err != nil {
w.lastbuf.WriteByte(c1)
break loop break loop
} }
if c2 == ']' {
if err := doTitleSequence(er); err != nil {
break loop
}
continue
}
if c2 != 0x5b { if c2 != 0x5b {
w.lastbuf.WriteByte(c1)
w.lastbuf.WriteByte(c2)
continue continue
} }
@ -399,9 +440,6 @@ loop:
for { for {
c, err := er.ReadByte() c, err := er.ReadByte()
if err != nil { if err != nil {
w.lastbuf.WriteByte(c1)
w.lastbuf.WriteByte(c2)
w.lastbuf.Write(buf.Bytes())
break loop break loop
} }
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
@ -411,7 +449,6 @@ loop:
buf.Write([]byte(string(c))) buf.Write([]byte(string(c)))
} }
var csbi consoleScreenBufferInfo
switch m { switch m {
case 'A': case 'A':
n, err = strconv.Atoi(buf.String()) n, err = strconv.Atoi(buf.String())
@ -435,19 +472,16 @@ loop:
continue continue
} }
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x -= short(n) csbi.cursorPosition.x += short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'D': case 'D':
n, err = strconv.Atoi(buf.String()) n, err = strconv.Atoi(buf.String())
if err != nil { if err != nil {
continue continue
} }
if n, err = strconv.Atoi(buf.String()); err == nil {
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x += short(n) csbi.cursorPosition.x -= short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
}
case 'E': case 'E':
n, err = strconv.Atoi(buf.String()) n, err = strconv.Atoi(buf.String())
if err != nil { if err != nil {
@ -474,11 +508,18 @@ loop:
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x = short(n - 1) csbi.cursorPosition.x = short(n - 1)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'H': case 'H', 'f':
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
if buf.Len() > 0 {
token := strings.Split(buf.String(), ";") token := strings.Split(buf.String(), ";")
if len(token) != 2 { switch len(token) {
case 1:
n1, err := strconv.Atoi(token[0])
if err != nil {
continue continue
} }
csbi.cursorPosition.y = short(n1 - 1)
case 2:
n1, err := strconv.Atoi(token[0]) n1, err := strconv.Atoi(token[0])
if err != nil { if err != nil {
continue continue
@ -489,43 +530,57 @@ loop:
} }
csbi.cursorPosition.x = short(n2 - 1) csbi.cursorPosition.x = short(n2 - 1)
csbi.cursorPosition.y = short(n1 - 1) csbi.cursorPosition.y = short(n1 - 1)
}
} else {
csbi.cursorPosition.y = 0
}
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'J': case 'J':
n, err := strconv.Atoi(buf.String()) n := 0
if buf.Len() > 0 {
n, err = strconv.Atoi(buf.String())
if err != nil { if err != nil {
continue continue
} }
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) }
var count, written dword
var cursor coord var cursor coord
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
switch n { switch n {
case 0: case 0:
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
case 1: case 1:
cursor = coord{x: csbi.window.left, y: csbi.window.top} cursor = coord{x: csbi.window.left, y: csbi.window.top}
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.window.top-csbi.cursorPosition.y)*csbi.size.x)
case 2: case 2:
cursor = coord{x: csbi.window.left, y: csbi.window.top} cursor = coord{x: csbi.window.left, y: csbi.window.top}
}
var count, written dword
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
}
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
case 'K': case 'K':
n, err := strconv.Atoi(buf.String()) n := 0
if buf.Len() > 0 {
n, err = strconv.Atoi(buf.String())
if err != nil { if err != nil {
continue continue
} }
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
var cursor coord var cursor coord
var count, written dword
switch n { switch n {
case 0: case 0:
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} cursor = coord{x: csbi.cursorPosition.x + 1, y: csbi.cursorPosition.y}
count = dword(csbi.size.x - csbi.cursorPosition.x - 1)
case 1: case 1:
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
count = dword(csbi.size.x - csbi.cursorPosition.x)
case 2: case 2:
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
count = dword(csbi.size.x)
} }
var count, written dword
count = dword(csbi.size.x - csbi.cursorPosition.x)
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
case 'm': case 'm':
@ -547,7 +602,7 @@ loop:
attr |= foregroundIntensity attr |= foregroundIntensity
case n == 7: case n == 7:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
case 22 == n || n == 25 || n == 25: case n == 22 || n == 25:
attr |= foregroundIntensity attr |= foregroundIntensity
case n == 27: case n == 27:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
@ -634,17 +689,25 @@ loop:
} }
} }
case 'h': case 'h':
cs := buf.String()
if cs == "?25" {
var ci consoleCursorInfo var ci consoleCursorInfo
cs := buf.String()
if cs == "5>" {
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
ci.visible = 0
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
} else if cs == "?25" {
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
ci.visible = 1 ci.visible = 1
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
} }
case 'l': case 'l':
cs := buf.String()
if cs == "?25" {
var ci consoleCursorInfo var ci consoleCursorInfo
cs := buf.String()
if cs == "5>" {
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
ci.visible = 1
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
} else if cs == "?25" {
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
ci.visible = 0 ci.visible = 0
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
@ -656,7 +719,8 @@ loop:
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
} }
} }
return len(data) - w.lastbuf.Len(), nil
return len(data), nil
} }
type consoleColor struct { type consoleColor struct {
@ -700,22 +764,22 @@ func (c consoleColor) backgroundAttr() (attr word) {
} }
var color16 = []consoleColor{ var color16 = []consoleColor{
consoleColor{0x000000, false, false, false, false}, {0x000000, false, false, false, false},
consoleColor{0x000080, false, false, true, false}, {0x000080, false, false, true, false},
consoleColor{0x008000, false, true, false, false}, {0x008000, false, true, false, false},
consoleColor{0x008080, false, true, true, false}, {0x008080, false, true, true, false},
consoleColor{0x800000, true, false, false, false}, {0x800000, true, false, false, false},
consoleColor{0x800080, true, false, true, false}, {0x800080, true, false, true, false},
consoleColor{0x808000, true, true, false, false}, {0x808000, true, true, false, false},
consoleColor{0xc0c0c0, true, true, true, false}, {0xc0c0c0, true, true, true, false},
consoleColor{0x808080, false, false, false, true}, {0x808080, false, false, false, true},
consoleColor{0x0000ff, false, false, true, true}, {0x0000ff, false, false, true, true},
consoleColor{0x00ff00, false, true, false, true}, {0x00ff00, false, true, false, true},
consoleColor{0x00ffff, false, true, true, true}, {0x00ffff, false, true, true, true},
consoleColor{0xff0000, true, false, false, true}, {0xff0000, true, false, false, true},
consoleColor{0xff00ff, true, false, true, true}, {0xff00ff, true, false, true, true},
consoleColor{0xffff00, true, true, false, true}, {0xffff00, true, true, false, true},
consoleColor{0xffffff, true, true, true, true}, {0xffffff, true, true, true, true},
} }
type hsv struct { type hsv struct {

View File

@ -8,7 +8,6 @@ import (
// NonColorable hold writer but remove escape sequence. // NonColorable hold writer but remove escape sequence.
type NonColorable struct { type NonColorable struct {
out io.Writer out io.Writer
lastbuf bytes.Buffer
} }
// NewNonColorable return new instance of Writer which remove escape sequence from Writer. // NewNonColorable return new instance of Writer which remove escape sequence from Writer.
@ -33,12 +32,9 @@ loop:
} }
c2, err := er.ReadByte() c2, err := er.ReadByte()
if err != nil { if err != nil {
w.lastbuf.WriteByte(c1)
break loop break loop
} }
if c2 != 0x5b { if c2 != 0x5b {
w.lastbuf.WriteByte(c1)
w.lastbuf.WriteByte(c2)
continue continue
} }
@ -46,9 +42,6 @@ loop:
for { for {
c, err := er.ReadByte() c, err := er.ReadByte()
if err != nil { if err != nil {
w.lastbuf.WriteByte(c1)
w.lastbuf.WriteByte(c2)
w.lastbuf.Write(buf.Bytes())
break loop break loop
} }
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
@ -57,5 +50,6 @@ loop:
buf.Write([]byte(string(c))) buf.Write([]byte(string(c)))
} }
} }
return len(data) - w.lastbuf.Len(), nil
return len(data), nil
} }

View File

@ -1,6 +1,11 @@
language: go language: go
go: go:
- tip - tip
os:
- linux
- osx
before_install: before_install:
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover - go get golang.org/x/tools/cmd/cover

View File

@ -1,6 +1,9 @@
# go-isatty # go-isatty
[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty) [![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) [![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty)
[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty)
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty)
isatty for golang isatty for golang

View File

@ -7,3 +7,9 @@ package isatty
func IsTerminal(fd uintptr) bool { func IsTerminal(fd uintptr) bool {
return false return false
} }
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View File

@ -1,5 +1,5 @@
// +build linux // +build linux
// +build !appengine // +build !appengine,!ppc64,!ppc64le
package isatty package isatty

View File

@ -0,0 +1,19 @@
// +build linux
// +build ppc64 ppc64le
package isatty
import (
"unsafe"
syscall "golang.org/x/sys/unix"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View File

@ -1,8 +1,9 @@
// +build !windows appengine // +build !windows
// +build !appengine
package isatty package isatty
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 // IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment. // terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool { func IsCygwinTerminal(fd uintptr) bool {
return false return false

14
vendor/github.com/nlopes/slack/.gometalinter.json generated vendored Normal file
View File

@ -0,0 +1,14 @@
{
"DisableAll": true,
"Enable": [
"structcheck",
"vet",
"misspell",
"unconvert",
"interfacer",
"goimports"
],
"Vendor": true,
"Exclude": ["vendor"],
"Deadline": "300s"
}

View File

@ -10,10 +10,12 @@ go:
before_install: before_install:
- export PATH=$HOME/gopath/bin:$PATH - export PATH=$HOME/gopath/bin:$PATH
# install gometalinter
- curl -L https://git.io/vp6lP | sh
script: script:
- go test -race ./... - PATH=$PWD/bin:$PATH gometalinter ./...
- go test -cover ./... - go test -race -cover ./...
matrix: matrix:
allow_failures: allow_failures:

View File

@ -1,3 +1,8 @@
### v0.5.0 - January 20, 2019
full differences can be viewed using `git log --oneline --decorate --color v0.4.0..v0.5.0`
- Breaking changes: various old struct fields have been removed or updated to match slack's api.
- deadlock fix in RTM disconnect.
### v0.4.0 - October 06, 2018 ### v0.4.0 - October 06, 2018
full differences can be viewed using `git log --oneline --decorate --color v0.3.0..v0.4.0` full differences can be viewed using `git log --oneline --decorate --color v0.3.0..v0.4.0`
- Breaking Change: renamed ApplyMessageOption, to mark it as unsafe, - Breaking Change: renamed ApplyMessageOption, to mark it as unsafe,

View File

@ -9,18 +9,10 @@ a fully managed way.
## Change log
Support for the EventsAPI has recently been added. It is still in its early stages but nearly all events have been added and tested (except for those events in [Developer Preview](https://api.slack.com/slack-apps-preview) mode). API stability for events is not promised at this time.
### v0.2.0 - Feb 10, 2018 ## Changelog
Release adds a bunch of functionality and improvements, mainly to give people a recent version to vendor against. [CHANGELOG.md](https://github.com/nlopes/slack/blob/master/CHANGELOG.md) is available. Please visit it for updates.
Please check [0.2.0](https://github.com/nlopes/slack/releases/tag/v0.2.0)
### CHANGELOG.md
[CHANGELOG.md](https://github.com/nlopes/slack/blob/master/CHANGELOG.md) is available. Please visit it for updates.
## Installing ## Installing

View File

@ -38,6 +38,29 @@ func channelRequest(ctx context.Context, client httpClient, path string, values
return response, nil return response, nil
} }
type channelsConfig struct {
values url.Values
}
// GetChannelsOption option provided when getting channels.
type GetChannelsOption func(*channelsConfig) error
// GetChannelsOptionExcludeMembers excludes the members collection from each channel.
func GetChannelsOptionExcludeMembers() GetChannelsOption {
return func(config *channelsConfig) error {
config.values.Add("exclude_members", "true")
return nil
}
}
// GetChannelsOptionExcludeArchived excludes archived channels from results.
func GetChannelsOptionExcludeArchived() GetChannelsOption {
return func(config *channelsConfig) error {
config.values.Add("exclude_archived", "true")
return nil
}
}
// ArchiveChannel archives the given channel // ArchiveChannel archives the given channel
// see https://api.slack.com/methods/channels.archive // see https://api.slack.com/methods/channels.archive
func (api *Client) ArchiveChannel(channelID string) error { func (api *Client) ArchiveChannel(channelID string) error {
@ -152,6 +175,7 @@ func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string)
values := url.Values{ values := url.Values{
"token": {api.token}, "token": {api.token},
"channel": {channelID}, "channel": {channelID},
"include_locale": {strconv.FormatBool(true)},
} }
response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api) response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api)
@ -247,21 +271,29 @@ func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, us
// GetChannels retrieves all the channels // GetChannels retrieves all the channels
// see https://api.slack.com/methods/channels.list // see https://api.slack.com/methods/channels.list
func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) { func (api *Client) GetChannels(excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) {
return api.GetChannelsContext(context.Background(), excludeArchived) return api.GetChannelsContext(context.Background(), excludeArchived, options...)
} }
// GetChannelsContext retrieves all the channels with a custom context // GetChannelsContext retrieves all the channels with a custom context
// see https://api.slack.com/methods/channels.list // see https://api.slack.com/methods/channels.list
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool) ([]Channel, error) { func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) {
values := url.Values{ config := channelsConfig{
values: url.Values{
"token": {api.token}, "token": {api.token},
},
} }
if excludeArchived { if excludeArchived {
values.Add("exclude_archived", "1") options = append(options, GetChannelsOptionExcludeArchived())
} }
response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api) for _, opt := range options {
if err := opt(&config); err != nil {
return nil, err
}
}
response, err := channelRequest(ctx, api.httpclient, "channels.list", config.values, api)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -49,7 +49,6 @@ type PostMessageParameters struct {
ThreadTimestamp string `json:"thread_ts"` ThreadTimestamp string `json:"thread_ts"`
ReplyBroadcast bool `json:"reply_broadcast"` ReplyBroadcast bool `json:"reply_broadcast"`
LinkNames int `json:"link_names"` LinkNames int `json:"link_names"`
Attachments []Attachment `json:"attachments"`
UnfurlLinks bool `json:"unfurl_links"` UnfurlLinks bool `json:"unfurl_links"`
UnfurlMedia bool `json:"unfurl_media"` UnfurlMedia bool `json:"unfurl_media"`
IconURL string `json:"icon_url"` IconURL string `json:"icon_url"`
@ -71,7 +70,6 @@ func NewPostMessageParameters() PostMessageParameters {
Parse: DEFAULT_MESSAGE_PARSE, Parse: DEFAULT_MESSAGE_PARSE,
ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP, ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP,
LinkNames: DEFAULT_MESSAGE_LINK_NAMES, LinkNames: DEFAULT_MESSAGE_LINK_NAMES,
Attachments: nil,
UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS, UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA, UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
IconURL: DEFAULT_MESSAGE_ICON_URL, IconURL: DEFAULT_MESSAGE_ICON_URL,

View File

@ -14,7 +14,7 @@ const (
InputTypeText InputType = "text" InputTypeText InputType = "text"
// InputTypeTextArea textarea input // InputTypeTextArea textarea input
InputTypeTextArea InputType = "textarea" InputTypeTextArea InputType = "textarea"
// InputTypeSelect textfield input // InputTypeSelect select menus input
InputTypeSelect InputType = "select" InputTypeSelect InputType = "select"
) )
@ -68,6 +68,17 @@ type DialogResponseMetadata struct {
Messages []string `json:"messages"` Messages []string `json:"messages"`
} }
// DialogInputValidationError is an error when user inputs incorrect value to form from within a dialog
type DialogInputValidationError struct {
Name string `json:"name"`
Error string `json:"error"`
}
// DialogInputValidationErrors lists the name of field and that error messages
type DialogInputValidationErrors struct {
Errors []DialogInputValidationError `json:"errors"`
}
// OpenDialog opens a dialog window where the triggerID originated from. // OpenDialog opens a dialog window where the triggerID originated from.
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable. // EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) { func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) {

View File

@ -3,6 +3,7 @@ package slack
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"net/url" "net/url"
"strconv" "strconv"
@ -86,12 +87,30 @@ type File struct {
CommentsCount int `json:"comments_count"` CommentsCount int `json:"comments_count"`
NumStars int `json:"num_stars"` NumStars int `json:"num_stars"`
IsStarred bool `json:"is_starred"` IsStarred bool `json:"is_starred"`
Shares Share `json:"shares"`
}
type Share struct {
Public map[string][]ShareFileInfo `json:"public"`
}
type ShareFileInfo struct {
ReplyUsers []string `json:"reply_users"`
ReplyUsersCount int `json:"reply_users_count"`
ReplyCount int `json:"reply_count"`
Ts string `json:"ts"`
ThreadTs string `json:"thread_ts"`
LatestReply string `json:"latest_reply"`
ChannelName string `json:"channel_name"`
TeamID string `json:"team_id"`
} }
// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request. // FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request.
// //
// There are three ways to upload a file. You can either set Content if file is small, set Reader if file is large, // There are three ways to upload a file. You can either set Content if file is small, set Reader if file is large,
// or provide a local file path in File to upload it from your filesystem. // or provide a local file path in File to upload it from your filesystem.
//
// Note that when using the Reader option, you *must* specify the Filename, otherwise the Slack API isn't happy.
type FileUploadParameters struct { type FileUploadParameters struct {
File string File string
Content string Content string
@ -220,6 +239,9 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
if err != nil { if err != nil {
return nil, err return nil, err
} }
if params.Filename == "" {
return nil, fmt.Errorf("files.upload: FileUploadParameters.Filename is mandatory")
}
response := &fileResponseFull{} response := &fileResponseFull{}
values := url.Values{ values := url.Values{
"token": {api.token}, "token": {api.token},

View File

@ -113,25 +113,6 @@ func (api *Client) CreateChildGroupContext(ctx context.Context, group string) (*
return &response.Group, nil return &response.Group, nil
} }
// CloseGroup closes a private group
func (api *Client) CloseGroup(group string) (bool, bool, error) {
return api.CloseGroupContext(context.Background(), group)
}
// CloseGroupContext closes a private group with a custom context
func (api *Client) CloseGroupContext(ctx context.Context, group string) (bool, bool, error) {
values := url.Values{
"token": {api.token},
"channel": {group},
}
response, err := imRequest(ctx, api.httpclient, "groups.close", values, api)
if err != nil {
return false, false, err
}
return response.NoOp, response.AlreadyClosed, nil
}
// GetGroupHistory fetches all the history for a private group // GetGroupHistory fetches all the history for a private group
func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*History, error) { func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*History, error) {
return api.GetGroupHistoryContext(context.Background(), group, params) return api.GetGroupHistoryContext(context.Background(), group, params)
@ -258,6 +239,7 @@ func (api *Client) GetGroupInfoContext(ctx context.Context, group string) (*Grou
values := url.Values{ values := url.Values{
"token": {api.token}, "token": {api.token},
"channel": {group}, "channel": {group},
"include_locale": {strconv.FormatBool(true)},
} }
response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api) response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api)

View File

@ -5,6 +5,7 @@ type InteractionType string
// Types of interactions that can be received. // Types of interactions that can be received.
const ( const (
InteractionTypeDialogCancellation = InteractionType("dialog_cancellation")
InteractionTypeDialogSubmission = InteractionType("dialog_submission") InteractionTypeDialogSubmission = InteractionType("dialog_submission")
InteractionTypeDialogSuggestion = InteractionType("dialog_suggestion") InteractionTypeDialogSuggestion = InteractionType("dialog_suggestion")
InteractionTypeInteractionMessage = InteractionType("interactive_message") InteractionTypeInteractionMessage = InteractionType("interactive_message")

75
vendor/github.com/nlopes/slack/reminders.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
package slack
import (
"context"
"net/url"
"time"
)
type Reminder struct {
ID string `json:"id"`
Creator string `json:"creator"`
User string `json:"user"`
Text string `json:"text"`
Recurring bool `json:"recurring"`
Time time.Time `json:"time"`
CompleteTS int `json:"complete_ts"`
}
type reminderResp struct {
SlackResponse
Reminder Reminder `json:"reminder"`
}
func (api *Client) doReminder(ctx context.Context, path string, values url.Values) (*Reminder, error) {
response := &reminderResp{}
if err := postSlackMethod(ctx, api.httpclient, path, values, response, api); err != nil {
return nil, err
}
return &response.Reminder, response.Err()
}
// AddChannelReminder adds a reminder for a channel.
//
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
// reminders on a channel is currently undocumented but has been tested to
// work)
func (api *Client) AddChannelReminder(channelID, text, time string) (*Reminder, error) {
values := url.Values{
"token": {api.token},
"text": {text},
"time": {time},
"channel": {channelID},
}
return api.doReminder(context.Background(), "reminders.add", values)
}
// AddUserReminder adds a reminder for a user.
//
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
// reminders on a channel is currently undocumented but has been tested to
// work)
func (api *Client) AddUserReminder(userID, text, time string) (*Reminder, error) {
values := url.Values{
"token": {api.token},
"text": {text},
"time": {time},
"user": {userID},
}
return api.doReminder(context.Background(), "reminders.add", values)
}
// DeleteReminder deletes an existing reminder.
//
// See https://api.slack.com/methods/reminders.delete
func (api *Client) DeleteReminder(id string) error {
values := url.Values{
"token": {api.token},
"reminder": {id},
}
response := &SlackResponse{}
if err := postSlackMethod(context.Background(), api.httpclient, "reminders.delete", values, response, api); err != nil {
return err
}
return response.Err()
}

View File

@ -42,7 +42,9 @@ func unsafeSignatureVerifier(header http.Header, secret string) (_ SecretsVerifi
} }
hash := hmac.New(sha256.New, []byte(secret)) hash := hmac.New(sha256.New, []byte(secret))
hash.Write([]byte(fmt.Sprintf("v0:%s:", stimestamp))) if _, err = hash.Write([]byte(fmt.Sprintf("v0:%s:", stimestamp))); err != nil {
return SecretsVerifier{}, err
}
return SecretsVerifier{ return SecretsVerifier{
signature: bsignature, signature: bsignature,
@ -66,7 +68,7 @@ func NewSecretsVerifier(header http.Header, secret string) (sv SecretsVerifier,
return SecretsVerifier{}, err return SecretsVerifier{}, err
} }
diff := absDuration(time.Now().Sub(time.Unix(timestamp, 0))) diff := absDuration(time.Since(time.Unix(timestamp, 0)))
if diff > 5*time.Minute { if diff > 5*time.Minute {
return SecretsVerifier{}, fmt.Errorf("timestamp is too old") return SecretsVerifier{}, fmt.Errorf("timestamp is too old")
} }

View File

@ -9,7 +9,7 @@ import (
"os" "os"
) )
// APIURL a dded as a var so that we can change this for testing purposes // APIURL added as a var so that we can change this for testing purposes
var APIURL = "https://slack.com/api/" var APIURL = "https://slack.com/api/"
// WEBAPIURLFormat ... // WEBAPIURLFormat ...
@ -50,7 +50,6 @@ type authTestResponseFull struct {
// Client for the slack api. // Client for the slack api.
type Client struct { type Client struct {
token string token string
info Info
debug bool debug bool
log ilogger log ilogger
httpclient httpClient httpclient httpClient

View File

@ -228,6 +228,7 @@ func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User,
values := url.Values{ values := url.Values{
"token": {api.token}, "token": {api.token},
"user": {user}, "user": {user},
"include_locale": {strconv.FormatBool(true)},
} }
response, err := userRequest(ctx, api.httpclient, "users.info", values, api) response, err := userRequest(ctx, api.httpclient, "users.info", values, api)
@ -306,6 +307,7 @@ func (t UserPagination) Next(ctx context.Context) (_ UserPagination, err error)
"presence": {strconv.FormatBool(t.presence)}, "presence": {strconv.FormatBool(t.presence)},
"token": {t.c.token}, "token": {t.c.token},
"cursor": {t.previousResp.Cursor}, "cursor": {t.previousResp.Cursor},
"include_locale": {strconv.FormatBool(true)},
} }
if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c); err != nil { if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c); err != nil {

View File

@ -39,8 +39,6 @@ type RTM struct {
wasIntentional bool wasIntentional bool
isConnected bool isConnected bool
websocketURL string
// UserDetails upon connection // UserDetails upon connection
info *Info info *Info
@ -103,7 +101,7 @@ func (rtm *RTM) SendMessage(msg *OutgoingMessage) {
} }
func (rtm *RTM) resetDeadman() { func (rtm *RTM) resetDeadman() {
timerReset(rtm.pingDeadman, deadmanDuration(rtm.pingInterval)) rtm.pingDeadman.Reset(deadmanDuration(rtm.pingInterval))
} }
func deadmanDuration(d time.Duration) time.Duration { func deadmanDuration(d time.Duration) time.Duration {

View File

@ -1,11 +1,10 @@
language: go language: go
go: dist: xenial
- "1.10.3"
- "1.9.7"
sudo: false go:
- "1.11.5"
- "1.10.8"
script: script:
- go vet ./...
- go test ./... - go test ./...

View File

@ -1,4 +1,4 @@
Copyright (c) 2015-2018 Peter Hellberg https://c7.se/ Copyright (c) 2015-2019 Peter Hellberg https://c7.se
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"), a copy of this software and associated documentation files (the "Software"),

View File

@ -39,7 +39,7 @@ _Youll need to have the [golang.org/x/tools/imports](https://golang.org/x/too
## License (MIT) ## License (MIT)
Copyright (c) 2015-2018 [Peter Hellberg](http://c7.se/) Copyright (c) 2015-2019 [Peter Hellberg](https://c7.se)
> Permission is hereby granted, free of charge, to any person obtaining > Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the > a copy of this software and associated documentation files (the

View File

@ -0,0 +1,28 @@
package sanitize
import "regexp"
var reStripName = regexp.MustCompile("[^\\w.-]")
const maxLength = 16
// Name returns a name with only allowed characters and a reasonable length
func Name(s string) string {
s = reStripName.ReplaceAllString(s, "")
nameLength := maxLength
if len(s) <= maxLength {
nameLength = len(s)
}
s = s[:nameLength]
return s
}
var reStripData = regexp.MustCompile("[^[:ascii:]]|[[:cntrl:]]")
// Data returns a string with only allowed characters for client-provided metadata inputs.
func Data(s string, maxlen int) string {
if len(s) > maxlen {
s = s[:maxlen]
}
return reStripData.ReplaceAllString(s, "")
}

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"net" "net"
"github.com/shazow/ssh-chat/internal/sanitize"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -13,8 +14,8 @@ import (
type Auth interface { type Auth interface {
// Whether to allow connections without a public key. // Whether to allow connections without a public key.
AllowAnonymous() bool AllowAnonymous() bool
// Given address and public key, return if the connection should be permitted. // Given address and public key and client agent string, returns nil if the connection should be allowed.
Check(net.Addr, ssh.PublicKey) (bool, error) Check(net.Addr, ssh.PublicKey, string) error
} }
// MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation. // MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation.
@ -23,8 +24,8 @@ func MakeAuth(auth Auth) *ssh.ServerConfig {
NoClientAuth: false, NoClientAuth: false,
// Auth-related things should be constant-time to avoid timing attacks. // Auth-related things should be constant-time to avoid timing attacks.
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
ok, err := auth.Check(conn.RemoteAddr(), key) err := auth.Check(conn.RemoteAddr(), key, sanitize.Data(string(conn.ClientVersion()), 64))
if !ok { if err != nil {
return nil, err return nil, err
} }
perm := &ssh.Permissions{Extensions: map[string]string{ perm := &ssh.Permissions{Extensions: map[string]string{
@ -36,7 +37,7 @@ func MakeAuth(auth Auth) *ssh.ServerConfig {
if !auth.AllowAnonymous() { if !auth.AllowAnonymous() {
return nil, errors.New("public key authentication required") return nil, errors.New("public key authentication required")
} }
_, err := auth.Check(conn.RemoteAddr(), nil) err := auth.Check(conn.RemoteAddr(), nil, sanitize.Data(string(conn.ClientVersion()), 64))
return nil, err return nil, err
}, },
} }

View File

@ -1,4 +1,5 @@
language: go language: go
go_import_path: github.com/sirupsen/logrus
env: env:
- GOMAXPROCS=4 GORACE=halt_on_error=1 - GOMAXPROCS=4 GORACE=halt_on_error=1
matrix: matrix:

View File

@ -361,6 +361,7 @@ The built-in logging formatters are:
Third party logging formatters: Third party logging formatters:
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine. * [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. * [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. * [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. * [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.

View File

@ -108,23 +108,34 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
for k, v := range entry.Data { for k, v := range entry.Data {
data[k] = v data[k] = v
} }
var field_err string fieldErr := entry.err
for k, v := range fields { for k, v := range fields {
if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func { isErrField := false
field_err = fmt.Sprintf("can not add field %q", k) if t := reflect.TypeOf(v); t != nil {
if entry.err != "" { switch t.Kind() {
field_err = entry.err + ", " + field_err case reflect.Func:
isErrField = true
case reflect.Ptr:
isErrField = t.Elem().Kind() == reflect.Func
}
}
if isErrField {
tmp := fmt.Sprintf("can not add field %q", k)
if fieldErr != "" {
fieldErr = entry.err + ", " + tmp
} else {
fieldErr = tmp
} }
} else { } else {
data[k] = v data[k] = v
} }
} }
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err} return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr}
} }
// Overrides the time of the Entry. // Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry { func (entry *Entry) WithTime(t time.Time) *Entry {
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t} return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err}
} }
// getPackageName reduces a fully qualified function name to the package name // getPackageName reduces a fully qualified function name to the package name
@ -240,16 +251,18 @@ func (entry *Entry) write() {
} }
} }
func (entry *Entry) Trace(args ...interface{}) { func (entry *Entry) Log(level Level, args ...interface{}) {
if entry.Logger.IsLevelEnabled(TraceLevel) { if entry.Logger.IsLevelEnabled(level) {
entry.log(TraceLevel, fmt.Sprint(args...)) entry.log(level, fmt.Sprint(args...))
} }
} }
func (entry *Entry) Trace(args ...interface{}) {
entry.Log(TraceLevel, args...)
}
func (entry *Entry) Debug(args ...interface{}) { func (entry *Entry) Debug(args ...interface{}) {
if entry.Logger.IsLevelEnabled(DebugLevel) { entry.Log(DebugLevel, args...)
entry.log(DebugLevel, fmt.Sprint(args...))
}
} }
func (entry *Entry) Print(args ...interface{}) { func (entry *Entry) Print(args ...interface{}) {
@ -257,15 +270,11 @@ func (entry *Entry) Print(args ...interface{}) {
} }
func (entry *Entry) Info(args ...interface{}) { func (entry *Entry) Info(args ...interface{}) {
if entry.Logger.IsLevelEnabled(InfoLevel) { entry.Log(InfoLevel, args...)
entry.log(InfoLevel, fmt.Sprint(args...))
}
} }
func (entry *Entry) Warn(args ...interface{}) { func (entry *Entry) Warn(args ...interface{}) {
if entry.Logger.IsLevelEnabled(WarnLevel) { entry.Log(WarnLevel, args...)
entry.log(WarnLevel, fmt.Sprint(args...))
}
} }
func (entry *Entry) Warning(args ...interface{}) { func (entry *Entry) Warning(args ...interface{}) {
@ -273,43 +282,35 @@ func (entry *Entry) Warning(args ...interface{}) {
} }
func (entry *Entry) Error(args ...interface{}) { func (entry *Entry) Error(args ...interface{}) {
if entry.Logger.IsLevelEnabled(ErrorLevel) { entry.Log(ErrorLevel, args...)
entry.log(ErrorLevel, fmt.Sprint(args...))
}
} }
func (entry *Entry) Fatal(args ...interface{}) { func (entry *Entry) Fatal(args ...interface{}) {
if entry.Logger.IsLevelEnabled(FatalLevel) { entry.Log(FatalLevel, args...)
entry.log(FatalLevel, fmt.Sprint(args...))
}
entry.Logger.Exit(1) entry.Logger.Exit(1)
} }
func (entry *Entry) Panic(args ...interface{}) { func (entry *Entry) Panic(args ...interface{}) {
if entry.Logger.IsLevelEnabled(PanicLevel) { entry.Log(PanicLevel, args...)
entry.log(PanicLevel, fmt.Sprint(args...))
}
panic(fmt.Sprint(args...)) panic(fmt.Sprint(args...))
} }
// Entry Printf family functions // Entry Printf family functions
func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
entry.Log(level, fmt.Sprintf(format, args...))
}
func (entry *Entry) Tracef(format string, args ...interface{}) { func (entry *Entry) Tracef(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(TraceLevel) { entry.Logf(TraceLevel, format, args...)
entry.Trace(fmt.Sprintf(format, args...))
}
} }
func (entry *Entry) Debugf(format string, args ...interface{}) { func (entry *Entry) Debugf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(DebugLevel) { entry.Logf(DebugLevel, format, args...)
entry.Debug(fmt.Sprintf(format, args...))
}
} }
func (entry *Entry) Infof(format string, args ...interface{}) { func (entry *Entry) Infof(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(InfoLevel) { entry.Logf(InfoLevel, format, args...)
entry.Info(fmt.Sprintf(format, args...))
}
} }
func (entry *Entry) Printf(format string, args ...interface{}) { func (entry *Entry) Printf(format string, args ...interface{}) {
@ -317,9 +318,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) {
} }
func (entry *Entry) Warnf(format string, args ...interface{}) { func (entry *Entry) Warnf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(WarnLevel) { entry.Logf(WarnLevel, format, args...)
entry.Warn(fmt.Sprintf(format, args...))
}
} }
func (entry *Entry) Warningf(format string, args ...interface{}) { func (entry *Entry) Warningf(format string, args ...interface{}) {
@ -327,42 +326,36 @@ func (entry *Entry) Warningf(format string, args ...interface{}) {
} }
func (entry *Entry) Errorf(format string, args ...interface{}) { func (entry *Entry) Errorf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(ErrorLevel) { entry.Logf(ErrorLevel, format, args...)
entry.Error(fmt.Sprintf(format, args...))
}
} }
func (entry *Entry) Fatalf(format string, args ...interface{}) { func (entry *Entry) Fatalf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(FatalLevel) { entry.Logf(FatalLevel, format, args...)
entry.Fatal(fmt.Sprintf(format, args...))
}
entry.Logger.Exit(1) entry.Logger.Exit(1)
} }
func (entry *Entry) Panicf(format string, args ...interface{}) { func (entry *Entry) Panicf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(PanicLevel) { entry.Logf(PanicLevel, format, args...)
entry.Panic(fmt.Sprintf(format, args...))
}
} }
// Entry Println family functions // Entry Println family functions
func (entry *Entry) Traceln(args ...interface{}) { func (entry *Entry) Logln(level Level, args ...interface{}) {
if entry.Logger.IsLevelEnabled(TraceLevel) { if entry.Logger.IsLevelEnabled(level) {
entry.Trace(entry.sprintlnn(args...)) entry.Log(level, entry.sprintlnn(args...))
} }
} }
func (entry *Entry) Traceln(args ...interface{}) {
entry.Logln(TraceLevel, args...)
}
func (entry *Entry) Debugln(args ...interface{}) { func (entry *Entry) Debugln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(DebugLevel) { entry.Logln(DebugLevel, args...)
entry.Debug(entry.sprintlnn(args...))
}
} }
func (entry *Entry) Infoln(args ...interface{}) { func (entry *Entry) Infoln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(InfoLevel) { entry.Logln(InfoLevel, args...)
entry.Info(entry.sprintlnn(args...))
}
} }
func (entry *Entry) Println(args ...interface{}) { func (entry *Entry) Println(args ...interface{}) {
@ -370,9 +363,7 @@ func (entry *Entry) Println(args ...interface{}) {
} }
func (entry *Entry) Warnln(args ...interface{}) { func (entry *Entry) Warnln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(WarnLevel) { entry.Logln(WarnLevel, args...)
entry.Warn(entry.sprintlnn(args...))
}
} }
func (entry *Entry) Warningln(args ...interface{}) { func (entry *Entry) Warningln(args ...interface{}) {
@ -380,22 +371,16 @@ func (entry *Entry) Warningln(args ...interface{}) {
} }
func (entry *Entry) Errorln(args ...interface{}) { func (entry *Entry) Errorln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(ErrorLevel) { entry.Logln(ErrorLevel, args...)
entry.Error(entry.sprintlnn(args...))
}
} }
func (entry *Entry) Fatalln(args ...interface{}) { func (entry *Entry) Fatalln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(FatalLevel) { entry.Logln(FatalLevel, args...)
entry.Fatal(entry.sprintlnn(args...))
}
entry.Logger.Exit(1) entry.Logger.Exit(1)
} }
func (entry *Entry) Panicln(args ...interface{}) { func (entry *Entry) Panicln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(PanicLevel) { entry.Logln(PanicLevel, args...)
entry.Panic(entry.sprintlnn(args...))
}
} }
// Sprintlnn => Sprint no newline. This is to get the behavior of how // Sprintlnn => Sprint no newline. This is to get the behavior of how

View File

@ -131,28 +131,24 @@ func (logger *Logger) WithTime(t time.Time) *Entry {
return entry.WithTime(t) return entry.WithTime(t)
} }
func (logger *Logger) Tracef(format string, args ...interface{}) { func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
if logger.IsLevelEnabled(TraceLevel) { if logger.IsLevelEnabled(level) {
entry := logger.newEntry() entry := logger.newEntry()
entry.Tracef(format, args...) entry.Logf(level, format, args...)
logger.releaseEntry(entry) logger.releaseEntry(entry)
} }
} }
func (logger *Logger) Tracef(format string, args ...interface{}) {
logger.Logf(TraceLevel, format, args...)
}
func (logger *Logger) Debugf(format string, args ...interface{}) { func (logger *Logger) Debugf(format string, args ...interface{}) {
if logger.IsLevelEnabled(DebugLevel) { logger.Logf(DebugLevel, format, args...)
entry := logger.newEntry()
entry.Debugf(format, args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Infof(format string, args ...interface{}) { func (logger *Logger) Infof(format string, args ...interface{}) {
if logger.IsLevelEnabled(InfoLevel) { logger.Logf(InfoLevel, format, args...)
entry := logger.newEntry()
entry.Infof(format, args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Printf(format string, args ...interface{}) { func (logger *Logger) Printf(format string, args ...interface{}) {
@ -162,68 +158,44 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
} }
func (logger *Logger) Warnf(format string, args ...interface{}) { func (logger *Logger) Warnf(format string, args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) { logger.Logf(WarnLevel, format, args...)
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Warningf(format string, args ...interface{}) { func (logger *Logger) Warningf(format string, args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) { logger.Warnf(format, args...)
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Errorf(format string, args ...interface{}) { func (logger *Logger) Errorf(format string, args ...interface{}) {
if logger.IsLevelEnabled(ErrorLevel) { logger.Logf(ErrorLevel, format, args...)
entry := logger.newEntry()
entry.Errorf(format, args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Fatalf(format string, args ...interface{}) { func (logger *Logger) Fatalf(format string, args ...interface{}) {
if logger.IsLevelEnabled(FatalLevel) { logger.Logf(FatalLevel, format, args...)
entry := logger.newEntry()
entry.Fatalf(format, args...)
logger.releaseEntry(entry)
}
logger.Exit(1) logger.Exit(1)
} }
func (logger *Logger) Panicf(format string, args ...interface{}) { func (logger *Logger) Panicf(format string, args ...interface{}) {
if logger.IsLevelEnabled(PanicLevel) { logger.Logf(PanicLevel, format, args...)
}
func (logger *Logger) Log(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry() entry := logger.newEntry()
entry.Panicf(format, args...) entry.Log(level, args...)
logger.releaseEntry(entry) logger.releaseEntry(entry)
} }
} }
func (logger *Logger) Trace(args ...interface{}) { func (logger *Logger) Trace(args ...interface{}) {
if logger.IsLevelEnabled(TraceLevel) { logger.Log(TraceLevel, args...)
entry := logger.newEntry()
entry.Trace(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Debug(args ...interface{}) { func (logger *Logger) Debug(args ...interface{}) {
if logger.IsLevelEnabled(DebugLevel) { logger.Log(DebugLevel, args...)
entry := logger.newEntry()
entry.Debug(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Info(args ...interface{}) { func (logger *Logger) Info(args ...interface{}) {
if logger.IsLevelEnabled(InfoLevel) { logger.Log(InfoLevel, args...)
entry := logger.newEntry()
entry.Info(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Print(args ...interface{}) { func (logger *Logger) Print(args ...interface{}) {
@ -233,68 +205,44 @@ func (logger *Logger) Print(args ...interface{}) {
} }
func (logger *Logger) Warn(args ...interface{}) { func (logger *Logger) Warn(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) { logger.Log(WarnLevel, args...)
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Warning(args ...interface{}) { func (logger *Logger) Warning(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) { logger.Warn(args...)
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Error(args ...interface{}) { func (logger *Logger) Error(args ...interface{}) {
if logger.IsLevelEnabled(ErrorLevel) { logger.Log(ErrorLevel, args...)
entry := logger.newEntry()
entry.Error(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Fatal(args ...interface{}) { func (logger *Logger) Fatal(args ...interface{}) {
if logger.IsLevelEnabled(FatalLevel) { logger.Log(FatalLevel, args...)
entry := logger.newEntry()
entry.Fatal(args...)
logger.releaseEntry(entry)
}
logger.Exit(1) logger.Exit(1)
} }
func (logger *Logger) Panic(args ...interface{}) { func (logger *Logger) Panic(args ...interface{}) {
if logger.IsLevelEnabled(PanicLevel) { logger.Log(PanicLevel, args...)
}
func (logger *Logger) Logln(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry() entry := logger.newEntry()
entry.Panic(args...) entry.Logln(level, args...)
logger.releaseEntry(entry) logger.releaseEntry(entry)
} }
} }
func (logger *Logger) Traceln(args ...interface{}) { func (logger *Logger) Traceln(args ...interface{}) {
if logger.IsLevelEnabled(TraceLevel) { logger.Logln(TraceLevel, args...)
entry := logger.newEntry()
entry.Traceln(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Debugln(args ...interface{}) { func (logger *Logger) Debugln(args ...interface{}) {
if logger.IsLevelEnabled(DebugLevel) { logger.Logln(DebugLevel, args...)
entry := logger.newEntry()
entry.Debugln(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Infoln(args ...interface{}) { func (logger *Logger) Infoln(args ...interface{}) {
if logger.IsLevelEnabled(InfoLevel) { logger.Logln(InfoLevel, args...)
entry := logger.newEntry()
entry.Infoln(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Println(args ...interface{}) { func (logger *Logger) Println(args ...interface{}) {
@ -304,44 +252,24 @@ func (logger *Logger) Println(args ...interface{}) {
} }
func (logger *Logger) Warnln(args ...interface{}) { func (logger *Logger) Warnln(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) { logger.Logln(WarnLevel, args...)
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Warningln(args ...interface{}) { func (logger *Logger) Warningln(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) { logger.Warn(args...)
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Errorln(args ...interface{}) { func (logger *Logger) Errorln(args ...interface{}) {
if logger.IsLevelEnabled(ErrorLevel) { logger.Logln(ErrorLevel, args...)
entry := logger.newEntry()
entry.Errorln(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Fatalln(args ...interface{}) { func (logger *Logger) Fatalln(args ...interface{}) {
if logger.IsLevelEnabled(FatalLevel) { logger.Logln(FatalLevel, args...)
entry := logger.newEntry()
entry.Fatalln(args...)
logger.releaseEntry(entry)
}
logger.Exit(1) logger.Exit(1)
} }
func (logger *Logger) Panicln(args ...interface{}) { func (logger *Logger) Panicln(args ...interface{}) {
if logger.IsLevelEnabled(PanicLevel) { logger.Logln(PanicLevel, args...)
entry := logger.newEntry()
entry.Panicln(args...)
logger.releaseEntry(entry)
}
} }
func (logger *Logger) Exit(code int) { func (logger *Logger) Exit(code int) {

View File

@ -14,24 +14,11 @@ type Level uint32
// Convert the Level to a string. E.g. PanicLevel becomes "panic". // Convert the Level to a string. E.g. PanicLevel becomes "panic".
func (level Level) String() string { func (level Level) String() string {
switch level { if b, err := level.MarshalText(); err == nil {
case TraceLevel: return string(b)
return "trace" } else {
case DebugLevel:
return "debug"
case InfoLevel:
return "info"
case WarnLevel:
return "warning"
case ErrorLevel:
return "error"
case FatalLevel:
return "fatal"
case PanicLevel:
return "panic"
}
return "unknown" return "unknown"
}
} }
// ParseLevel takes a string level and returns the Logrus log level constant. // ParseLevel takes a string level and returns the Logrus log level constant.
@ -69,6 +56,27 @@ func (level *Level) UnmarshalText(text []byte) error {
return nil return nil
} }
func (level Level) MarshalText() ([]byte, error) {
switch level {
case TraceLevel:
return []byte("trace"), nil
case DebugLevel:
return []byte("debug"), nil
case InfoLevel:
return []byte("info"), nil
case WarnLevel:
return []byte("warning"), nil
case ErrorLevel:
return []byte("error"), nil
case FatalLevel:
return []byte("fatal"), nil
case PanicLevel:
return []byte("panic"), nil
}
return nil, fmt.Errorf("not a valid lorus level %q", level)
}
// A constant exposing all logging levels // A constant exposing all logging levels
var AllLevels = []Level{ var AllLevels = []Level{
PanicLevel, PanicLevel,

View File

@ -0,0 +1,9 @@
// +build !appengine,!js,!windows,aix
package logrus
import "io"
func checkIfTerminal(w io.Writer) bool {
return false
}

View File

@ -1,4 +1,4 @@
// +build !appengine,!js,!windows // +build !appengine,!js,!windows,!aix
package logrus package logrus

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"os" "os"
"runtime"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -90,7 +91,7 @@ func (f *TextFormatter) init(entry *Entry) {
} }
func (f *TextFormatter) isColored() bool { func (f *TextFormatter) isColored() bool {
isColored := f.ForceColors || f.isTerminal isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
if f.EnvironmentOverrideColors { if f.EnvironmentOverrideColors {
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
@ -107,14 +108,17 @@ func (f *TextFormatter) isColored() bool {
// Format renders a single log entry // Format renders a single log entry
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
prefixFieldClashes(entry.Data, f.FieldMap, entry.HasCaller()) data := make(Fields)
for k, v := range entry.Data {
keys := make([]string, 0, len(entry.Data)) data[k] = v
for k := range entry.Data { }
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
keys := make([]string, 0, len(data))
for k := range data {
keys = append(keys, k) keys = append(keys, k)
} }
fixedKeys := make([]string, 0, 4+len(entry.Data)) fixedKeys := make([]string, 0, 4+len(data))
if !f.DisableTimestamp { if !f.DisableTimestamp {
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
} }
@ -160,7 +164,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
timestampFormat = defaultTimestampFormat timestampFormat = defaultTimestampFormat
} }
if f.isColored() { if f.isColored() {
f.printColored(b, entry, keys, timestampFormat) f.printColored(b, entry, keys, data, timestampFormat)
} else { } else {
for _, key := range fixedKeys { for _, key := range fixedKeys {
var value interface{} var value interface{}
@ -178,7 +182,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
default: default:
value = entry.Data[key] value = data[key]
} }
f.appendKeyValue(b, key, value) f.appendKeyValue(b, key, value)
} }
@ -188,7 +192,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
return b.Bytes(), nil return b.Bytes(), nil
} }
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
var levelColor int var levelColor int
switch entry.Level { switch entry.Level {
case DebugLevel, TraceLevel: case DebugLevel, TraceLevel:
@ -225,7 +229,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
} }
for _, k := range keys { for _, k := range keys {
v := entry.Data[k] v := data[k]
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
f.appendValue(b, v) f.appendValue(b, v)
} }

View File

@ -22,3 +22,8 @@ _testmain.go
*.exe *.exe
*.test *.test
*.bench *.bench
.vscode
# exclude dependencies in the `/vendor` folder
vendor

View File

@ -1,8 +1,12 @@
go_import_path: github.com/spf13/viper go_import_path: github.com/spf13/viper
language: go language: go
env:
global:
- GO111MODULE="on"
go: go:
- 1.10.x
- 1.11.x - 1.11.x
- tip - tip

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