Fix recovery middleware to render gitea style page. (#13857)
* Some changes to fix recovery * Move Recovery to middlewares * Remove trace code * Fix lint * add session middleware and remove dependent on macaron for sso * Fix panic 500 page rendering * Fix bugs * Fix fmt * Fix vendor * recover unnecessary change * Fix lint and addd some comments about the copied codes. * Use util.StatDir instead of com.StatDir Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
		
							parent
							
								
									126c9331d6
								
							
						
					
					
						commit
						15a475b7db
					
				
					 75 changed files with 5233 additions and 307 deletions
				
			
		
							
								
								
									
										6
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.mod
									
									
									
									
									
								
							|  | @ -5,6 +5,7 @@ go 1.14 | ||||||
| require ( | require ( | ||||||
| 	code.gitea.io/gitea-vet v0.2.1 | 	code.gitea.io/gitea-vet v0.2.1 | ||||||
| 	code.gitea.io/sdk/gitea v0.13.1 | 	code.gitea.io/sdk/gitea v0.13.1 | ||||||
|  | 	gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27 | ||||||
| 	gitea.com/lunny/levelqueue v0.3.0 | 	gitea.com/lunny/levelqueue v0.3.0 | ||||||
| 	gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b | 	gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b | ||||||
| 	gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b | 	gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b | ||||||
|  | @ -33,7 +34,7 @@ require ( | ||||||
| 	github.com/ethantkoenig/rupture v0.0.0-20181029165146-c3b3b810dc77 | 	github.com/ethantkoenig/rupture v0.0.0-20181029165146-c3b3b810dc77 | ||||||
| 	github.com/gliderlabs/ssh v0.3.1 | 	github.com/gliderlabs/ssh v0.3.1 | ||||||
| 	github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect | 	github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect | ||||||
| 	github.com/go-chi/chi v1.5.0 | 	github.com/go-chi/chi v1.5.1 | ||||||
| 	github.com/go-enry/go-enry/v2 v2.6.0 | 	github.com/go-enry/go-enry/v2 v2.6.0 | ||||||
| 	github.com/go-git/go-billy/v5 v5.0.0 | 	github.com/go-git/go-billy/v5 v5.0.0 | ||||||
| 	github.com/go-git/go-git/v5 v5.2.0 | 	github.com/go-git/go-git/v5 v5.2.0 | ||||||
|  | @ -78,7 +79,6 @@ require ( | ||||||
| 	github.com/niklasfasching/go-org v1.3.2 | 	github.com/niklasfasching/go-org v1.3.2 | ||||||
| 	github.com/oliamb/cutter v0.2.2 | 	github.com/oliamb/cutter v0.2.2 | ||||||
| 	github.com/olivere/elastic/v7 v7.0.21 | 	github.com/olivere/elastic/v7 v7.0.21 | ||||||
| 	github.com/onsi/ginkgo v1.13.0 // indirect |  | ||||||
| 	github.com/pelletier/go-toml v1.8.1 | 	github.com/pelletier/go-toml v1.8.1 | ||||||
| 	github.com/pierrec/lz4/v4 v4.1.1 // indirect | 	github.com/pierrec/lz4/v4 v4.1.1 // indirect | ||||||
| 	github.com/pkg/errors v0.9.1 | 	github.com/pkg/errors v0.9.1 | ||||||
|  | @ -98,6 +98,7 @@ require ( | ||||||
| 	github.com/unknwon/com v1.0.1 | 	github.com/unknwon/com v1.0.1 | ||||||
| 	github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c | 	github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c | ||||||
| 	github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae | 	github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae | ||||||
|  | 	github.com/unrolled/render v1.0.3 | ||||||
| 	github.com/urfave/cli v1.22.5 | 	github.com/urfave/cli v1.22.5 | ||||||
| 	github.com/willf/bitset v1.1.11 // indirect | 	github.com/willf/bitset v1.1.11 // indirect | ||||||
| 	github.com/xanzy/go-gitlab v0.39.0 | 	github.com/xanzy/go-gitlab v0.39.0 | ||||||
|  | @ -114,7 +115,6 @@ require ( | ||||||
| 	golang.org/x/text v0.3.4 | 	golang.org/x/text v0.3.4 | ||||||
| 	golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect | 	golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect | ||||||
| 	golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9 | 	golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9 | ||||||
| 	google.golang.org/appengine v1.6.7 // indirect |  | ||||||
| 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | ||||||
| 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df | 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df | ||||||
| 	gopkg.in/ini.v1 v1.62.0 | 	gopkg.in/ini.v1 v1.62.0 | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								go.sum
									
									
									
									
									
								
							|  | @ -40,6 +40,8 @@ code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFj | ||||||
| code.gitea.io/sdk/gitea v0.13.1 h1:Y7bpH2iO6Q0KhhMJfjP/LZ0AmiYITeRQlCD8b0oYqhk= | code.gitea.io/sdk/gitea v0.13.1 h1:Y7bpH2iO6Q0KhhMJfjP/LZ0AmiYITeRQlCD8b0oYqhk= | ||||||
| code.gitea.io/sdk/gitea v0.13.1/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= | code.gitea.io/sdk/gitea v0.13.1/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= | ||||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||||
|  | gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27 h1:cdb1OTNXGLwQ55gg+9tIPWufdsnrHWcIq8Qs+j/E8JU= | ||||||
|  | gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0= | ||||||
| gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I= | gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I= | ||||||
| gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= | gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= | ||||||
| gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk= | gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk= | ||||||
|  | @ -227,6 +229,8 @@ github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+ | ||||||
| github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | ||||||
| github.com/couchbase/gomemcached v0.1.0 h1:whUde87n8CScx8ckMp2En5liqAlcuG3aKy/BQeBPu84= | github.com/couchbase/gomemcached v0.1.0 h1:whUde87n8CScx8ckMp2En5liqAlcuG3aKy/BQeBPu84= | ||||||
| github.com/couchbase/gomemcached v0.1.0/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | github.com/couchbase/gomemcached v0.1.0/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | ||||||
|  | github.com/couchbase/gomemcached v0.1.1 h1:xCS8ZglJDhrlQg3jmK7Rn1V8f7bPjXABLC05CgLQauc= | ||||||
|  | github.com/couchbase/gomemcached v0.1.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo= | ||||||
| github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | ||||||
| github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 h1:NCqJ6fwen6YP0WlV/IyibaT0kPt3JEI1rA62V/UPKT4= | github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 h1:NCqJ6fwen6YP0WlV/IyibaT0kPt3JEI1rA62V/UPKT4= | ||||||
| github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | ||||||
|  | @ -264,6 +268,7 @@ github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D | ||||||
| github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | ||||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||||||
|  | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= | ||||||
| github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= | ||||||
| github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= | github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= | ||||||
| github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= | github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= | ||||||
|  | @ -283,6 +288,8 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP | ||||||
| github.com/editorconfig/editorconfig-core-go/v2 v2.3.9 h1:4vZN3UCLAUbT408wDutTKGZwOlgGMpV3vhahYufNbV8= | github.com/editorconfig/editorconfig-core-go/v2 v2.3.9 h1:4vZN3UCLAUbT408wDutTKGZwOlgGMpV3vhahYufNbV8= | ||||||
| github.com/editorconfig/editorconfig-core-go/v2 v2.3.9/go.mod h1:yoHDFR3nO8O5ssvhITSRsf0owQqIs0c9+nBTtarunPo= | github.com/editorconfig/editorconfig-core-go/v2 v2.3.9/go.mod h1:yoHDFR3nO8O5ssvhITSRsf0owQqIs0c9+nBTtarunPo= | ||||||
| github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | ||||||
|  | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= | ||||||
|  | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= | ||||||
| github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= | ||||||
| github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | ||||||
| github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= | ||||||
|  | @ -326,8 +333,8 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy | ||||||
| github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= | github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= | ||||||
| github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= | github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= | ||||||
| github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= | github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= | ||||||
| github.com/go-chi/chi v1.5.0 h1:2ZcJZozJ+rj6BA0c19ffBUGXEKAT/aOLOtQjD46vBRA= | github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w= | ||||||
| github.com/go-chi/chi v1.5.0/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= | github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= | ||||||
| github.com/go-enry/go-enry/v2 v2.6.0 h1:nbGWQBpO+D+cJuRxNgSDFnFY9QWz3QM/CeZxU7VAH20= | github.com/go-enry/go-enry/v2 v2.6.0 h1:nbGWQBpO+D+cJuRxNgSDFnFY9QWz3QM/CeZxU7VAH20= | ||||||
| github.com/go-enry/go-enry/v2 v2.6.0/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= | github.com/go-enry/go-enry/v2 v2.6.0/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= | ||||||
| github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= | github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= | ||||||
|  | @ -422,6 +429,7 @@ github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDA | ||||||
| github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= | github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= | ||||||
| github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= | github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= | ||||||
| github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= | github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= | ||||||
|  | github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= | ||||||
| github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | ||||||
| github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | ||||||
| github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= | ||||||
|  | @ -519,6 +527,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | ||||||
| github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
| github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= | ||||||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
|  | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
| github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= | github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= | ||||||
| github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
| github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= | github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= | ||||||
|  | @ -871,8 +880,8 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W | ||||||
| github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= | github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= | ||||||
| github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | ||||||
| github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= | ||||||
| github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y= | github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= | ||||||
| github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= | github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= | ||||||
| github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | ||||||
| github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | ||||||
| github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= | github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= | ||||||
|  | @ -880,6 +889,8 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa | ||||||
| github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= | ||||||
| github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= | github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= | ||||||
| github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | ||||||
|  | github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= | ||||||
|  | github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= | ||||||
| github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= | ||||||
| github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= | ||||||
| github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= | github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= | ||||||
|  | @ -977,7 +988,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD | ||||||
| github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= | ||||||
| github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= | github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= | ||||||
| github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= | ||||||
| github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= |  | ||||||
| github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= | ||||||
| github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= | ||||||
| github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= | ||||||
|  | @ -1086,6 +1096,8 @@ github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c h1:679/gJXwrsHC3RATr0 | ||||||
| github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ= | github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ= | ||||||
| github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs= | github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs= | ||||||
| github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae/go.mod h1:1fdkY6xxl6ExVs2QFv7R0F5IRZHKA8RahhB9fMC9RvM= | github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae/go.mod h1:1fdkY6xxl6ExVs2QFv7R0F5IRZHKA8RahhB9fMC9RvM= | ||||||
|  | github.com/unrolled/render v1.0.3 h1:baO+NG1bZSF2WR4zwh+0bMWauWky7DVrTOfvE2w+aFo= | ||||||
|  | github.com/unrolled/render v1.0.3/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= | ||||||
| github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= | ||||||
| github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | ||||||
| github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= | github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= | ||||||
|  | @ -1142,6 +1154,7 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= | ||||||
| go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||||
| go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||||
| go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||||
|  | go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw= | ||||||
| go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | ||||||
| go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | ||||||
| go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= | ||||||
|  | @ -1258,6 +1271,7 @@ golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81R | ||||||
| golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= | golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= | ||||||
| golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||||
| golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||||
|  | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||||
| golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||||
| golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= | ||||||
| golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||||
|  |  | ||||||
|  | @ -9,13 +9,10 @@ import ( | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/modules/auth/sso" |  | ||||||
| 	"code.gitea.io/gitea/modules/validation" | 	"code.gitea.io/gitea/modules/validation" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/macaron/binding" | 	"gitea.com/macaron/binding" | ||||||
| 	"gitea.com/macaron/macaron" | 	"gitea.com/macaron/macaron" | ||||||
| 	"gitea.com/macaron/session" |  | ||||||
| 	"github.com/unknwon/com" | 	"github.com/unknwon/com" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -24,28 +21,6 @@ func IsAPIPath(url string) bool { | ||||||
| 	return strings.HasPrefix(url, "/api/") | 	return strings.HasPrefix(url, "/api/") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SignedInUser returns the user object of signed user.
 |  | ||||||
| // It returns a bool value to indicate whether user uses basic auth or not.
 |  | ||||||
| func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) { |  | ||||||
| 	if !models.HasEngine { |  | ||||||
| 		return nil, false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Try to sign in with each of the enabled plugins
 |  | ||||||
| 	for _, ssoMethod := range sso.Methods() { |  | ||||||
| 		if !ssoMethod.IsEnabled() { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		user := ssoMethod.VerifyAuthData(ctx, sess) |  | ||||||
| 		if user != nil { |  | ||||||
| 			_, isBasic := ssoMethod.(*sso.Basic) |  | ||||||
| 			return user, isBasic |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil, false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Form form binding interface
 | // Form form binding interface
 | ||||||
| type Form interface { | type Form interface { | ||||||
| 	binding.Validator | 	binding.Validator | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| package sso | package sso | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | @ -13,9 +14,6 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| 
 |  | ||||||
| 	"gitea.com/macaron/macaron" |  | ||||||
| 	"gitea.com/macaron/session" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Ensure the struct implements the interface.
 | // Ensure the struct implements the interface.
 | ||||||
|  | @ -49,8 +47,8 @@ func (b *Basic) IsEnabled() bool { | ||||||
| // "Authorization" header of the request and returns the corresponding user object for that
 | // "Authorization" header of the request and returns the corresponding user object for that
 | ||||||
| // name/token on successful validation.
 | // name/token on successful validation.
 | ||||||
| // Returns nil if header is empty or validation fails.
 | // Returns nil if header is empty or validation fails.
 | ||||||
| func (b *Basic) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { | func (b *Basic) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { | ||||||
| 	baHead := ctx.Req.Header.Get("Authorization") | 	baHead := req.Header.Get("Authorization") | ||||||
| 	if len(baHead) == 0 { | 	if len(baHead) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  | @ -75,7 +73,7 @@ func (b *Basic) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models | ||||||
| 	uid := CheckOAuthAccessToken(authToken) | 	uid := CheckOAuthAccessToken(authToken) | ||||||
| 	if uid != 0 { | 	if uid != 0 { | ||||||
| 		var err error | 		var err error | ||||||
| 		ctx.Data["IsApiToken"] = true | 		store.GetData()["IsApiToken"] = true | ||||||
| 
 | 
 | ||||||
| 		u, err = models.GetUserByID(uid) | 		u, err = models.GetUserByID(uid) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -108,7 +106,7 @@ func (b *Basic) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		ctx.Data["IsApiToken"] = true | 		store.GetData()["IsApiToken"] = true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return u | 	return u | ||||||
|  |  | ||||||
|  | @ -5,12 +5,23 @@ | ||||||
| package sso | package sso | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	"net/http" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/macaron/macaron" | 	"code.gitea.io/gitea/models" | ||||||
| 	"gitea.com/macaron/session" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // DataStore represents a data store
 | ||||||
|  | type DataStore interface { | ||||||
|  | 	GetData() map[string]interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SessionStore represents a session store
 | ||||||
|  | type SessionStore interface { | ||||||
|  | 	Get(interface{}) interface{} | ||||||
|  | 	Set(interface{}, interface{}) error | ||||||
|  | 	Delete(interface{}) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // SingleSignOn represents a SSO authentication method (plugin) for HTTP requests.
 | // SingleSignOn represents a SSO authentication method (plugin) for HTTP requests.
 | ||||||
| type SingleSignOn interface { | type SingleSignOn interface { | ||||||
| 	// Init should be called exactly once before using any of the other methods,
 | 	// Init should be called exactly once before using any of the other methods,
 | ||||||
|  | @ -29,5 +40,5 @@ type SingleSignOn interface { | ||||||
| 	// or a new user object (with id = 0) populated with the information that was found
 | 	// or a new user object (with id = 0) populated with the information that was found
 | ||||||
| 	// in the authentication data (username or email).
 | 	// in the authentication data (username or email).
 | ||||||
| 	// Returns nil if verification fails.
 | 	// Returns nil if verification fails.
 | ||||||
| 	VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User | 	VerifyAuthData(http *http.Request, store DataStore, sess SessionStore) *models.User | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -6,15 +6,13 @@ | ||||||
| package sso | package sso | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| 
 |  | ||||||
| 	"gitea.com/macaron/macaron" |  | ||||||
| 	"gitea.com/macaron/session" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Ensure the struct implements the interface.
 | // Ensure the struct implements the interface.
 | ||||||
|  | @ -63,15 +61,15 @@ func (o *OAuth2) Free() error { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // userIDFromToken returns the user id corresponding to the OAuth token.
 | // userIDFromToken returns the user id corresponding to the OAuth token.
 | ||||||
| func (o *OAuth2) userIDFromToken(ctx *macaron.Context) int64 { | func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { | ||||||
| 	// Check access token.
 | 	// Check access token.
 | ||||||
| 	tokenSHA := ctx.Query("token") | 	tokenSHA := req.Form.Get("token") | ||||||
| 	if len(tokenSHA) == 0 { | 	if len(tokenSHA) == 0 { | ||||||
| 		tokenSHA = ctx.Query("access_token") | 		tokenSHA = req.Form.Get("access_token") | ||||||
| 	} | 	} | ||||||
| 	if len(tokenSHA) == 0 { | 	if len(tokenSHA) == 0 { | ||||||
| 		// Well, check with header again.
 | 		// Well, check with header again.
 | ||||||
| 		auHead := ctx.Req.Header.Get("Authorization") | 		auHead := req.Header.Get("Authorization") | ||||||
| 		if len(auHead) > 0 { | 		if len(auHead) > 0 { | ||||||
| 			auths := strings.Fields(auHead) | 			auths := strings.Fields(auHead) | ||||||
| 			if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { | 			if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { | ||||||
|  | @ -87,7 +85,7 @@ func (o *OAuth2) userIDFromToken(ctx *macaron.Context) int64 { | ||||||
| 	if strings.Contains(tokenSHA, ".") { | 	if strings.Contains(tokenSHA, ".") { | ||||||
| 		uid := CheckOAuthAccessToken(tokenSHA) | 		uid := CheckOAuthAccessToken(tokenSHA) | ||||||
| 		if uid != 0 { | 		if uid != 0 { | ||||||
| 			ctx.Data["IsApiToken"] = true | 			store.GetData()["IsApiToken"] = true | ||||||
| 		} | 		} | ||||||
| 		return uid | 		return uid | ||||||
| 	} | 	} | ||||||
|  | @ -102,7 +100,7 @@ func (o *OAuth2) userIDFromToken(ctx *macaron.Context) int64 { | ||||||
| 	if err = models.UpdateAccessToken(t); err != nil { | 	if err = models.UpdateAccessToken(t); err != nil { | ||||||
| 		log.Error("UpdateAccessToken: %v", err) | 		log.Error("UpdateAccessToken: %v", err) | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["IsApiToken"] = true | 	store.GetData()["IsApiToken"] = true | ||||||
| 	return t.UID | 	return t.UID | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -116,16 +114,16 @@ func (o *OAuth2) IsEnabled() bool { | ||||||
| // or the "Authorization" header and returns the corresponding user object for that ID.
 | // or the "Authorization" header and returns the corresponding user object for that ID.
 | ||||||
| // If verification is successful returns an existing user object.
 | // If verification is successful returns an existing user object.
 | ||||||
| // Returns nil if verification fails.
 | // Returns nil if verification fails.
 | ||||||
| func (o *OAuth2) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { | func (o *OAuth2) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { | ||||||
| 	if !models.HasEngine { | 	if !models.HasEngine { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if isInternalPath(ctx) || !isAPIPath(ctx) && !isAttachmentDownload(ctx) { | 	if isInternalPath(req) || !isAPIPath(req) && !isAttachmentDownload(req) { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	id := o.userIDFromToken(ctx) | 	id := o.userIDFromToken(req, store) | ||||||
| 	if id <= 0 { | 	if id <= 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -6,14 +6,13 @@ | ||||||
| package sso | package sso | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/macaron/macaron" |  | ||||||
| 	"gitea.com/macaron/session" |  | ||||||
| 	gouuid "github.com/google/uuid" | 	gouuid "github.com/google/uuid" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -31,8 +30,8 @@ type ReverseProxy struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // getUserName extracts the username from the "setting.ReverseProxyAuthUser" header
 | // getUserName extracts the username from the "setting.ReverseProxyAuthUser" header
 | ||||||
| func (r *ReverseProxy) getUserName(ctx *macaron.Context) string { | func (r *ReverseProxy) getUserName(req *http.Request) string { | ||||||
| 	webAuthUser := strings.TrimSpace(ctx.Req.Header.Get(setting.ReverseProxyAuthUser)) | 	webAuthUser := strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser)) | ||||||
| 	if len(webAuthUser) == 0 { | 	if len(webAuthUser) == 0 { | ||||||
| 		return "" | 		return "" | ||||||
| 	} | 	} | ||||||
|  | @ -61,8 +60,8 @@ func (r *ReverseProxy) IsEnabled() bool { | ||||||
| // If a username is available in the "setting.ReverseProxyAuthUser" header an existing
 | // If a username is available in the "setting.ReverseProxyAuthUser" header an existing
 | ||||||
| // user object is returned (populated with username or email found in header).
 | // user object is returned (populated with username or email found in header).
 | ||||||
| // Returns nil if header is empty.
 | // Returns nil if header is empty.
 | ||||||
| func (r *ReverseProxy) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { | func (r *ReverseProxy) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { | ||||||
| 	username := r.getUserName(ctx) | 	username := r.getUserName(req) | ||||||
| 	if len(username) == 0 { | 	if len(username) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  | @ -70,7 +69,7 @@ func (r *ReverseProxy) VerifyAuthData(ctx *macaron.Context, sess session.Store) | ||||||
| 	user, err := models.GetUserByName(username) | 	user, err := models.GetUserByName(username) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if models.IsErrUserNotExist(err) && r.isAutoRegisterAllowed() { | 		if models.IsErrUserNotExist(err) && r.isAutoRegisterAllowed() { | ||||||
| 			return r.newUser(ctx) | 			return r.newUser(req) | ||||||
| 		} | 		} | ||||||
| 		log.Error("GetUserByName: %v", err) | 		log.Error("GetUserByName: %v", err) | ||||||
| 		return nil | 		return nil | ||||||
|  | @ -86,15 +85,15 @@ func (r *ReverseProxy) isAutoRegisterAllowed() bool { | ||||||
| 
 | 
 | ||||||
| // newUser creates a new user object for the purpose of automatic registration
 | // newUser creates a new user object for the purpose of automatic registration
 | ||||||
| // and populates its name and email with the information present in request headers.
 | // and populates its name and email with the information present in request headers.
 | ||||||
| func (r *ReverseProxy) newUser(ctx *macaron.Context) *models.User { | func (r *ReverseProxy) newUser(req *http.Request) *models.User { | ||||||
| 	username := r.getUserName(ctx) | 	username := r.getUserName(req) | ||||||
| 	if len(username) == 0 { | 	if len(username) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	email := gouuid.New().String() + "@localhost" | 	email := gouuid.New().String() + "@localhost" | ||||||
| 	if setting.Service.EnableReverseProxyEmail { | 	if setting.Service.EnableReverseProxyEmail { | ||||||
| 		webAuthEmail := ctx.Req.Header.Get(setting.ReverseProxyAuthEmail) | 		webAuthEmail := req.Header.Get(setting.ReverseProxyAuthEmail) | ||||||
| 		if len(webAuthEmail) > 0 { | 		if len(webAuthEmail) > 0 { | ||||||
| 			email = webAuthEmail | 			email = webAuthEmail | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -5,10 +5,9 @@ | ||||||
| package sso | package sso | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	"net/http" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/macaron/macaron" | 	"code.gitea.io/gitea/models" | ||||||
| 	"gitea.com/macaron/session" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Ensure the struct implements the interface.
 | // Ensure the struct implements the interface.
 | ||||||
|  | @ -40,7 +39,7 @@ func (s *Session) IsEnabled() bool { | ||||||
| // VerifyAuthData checks if there is a user uid stored in the session and returns the user
 | // VerifyAuthData checks if there is a user uid stored in the session and returns the user
 | ||||||
| // object for that uid.
 | // object for that uid.
 | ||||||
| // Returns nil if there is no user uid stored in the session.
 | // Returns nil if there is no user uid stored in the session.
 | ||||||
| func (s *Session) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { | func (s *Session) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { | ||||||
| 	user := SessionUser(sess) | 	user := SessionUser(sess) | ||||||
| 	if user != nil { | 	if user != nil { | ||||||
| 		return user | 		return user | ||||||
|  |  | ||||||
|  | @ -7,15 +7,14 @@ package sso | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/middlewares" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 
 |  | ||||||
| 	"gitea.com/macaron/macaron" |  | ||||||
| 	"gitea.com/macaron/session" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // ssoMethods contains the list of SSO authentication plugins in the order they are expected to be
 | // ssoMethods contains the list of SSO authentication plugins in the order they are expected to be
 | ||||||
|  | @ -73,7 +72,7 @@ func Free() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SessionUser returns the user object corresponding to the "uid" session variable.
 | // SessionUser returns the user object corresponding to the "uid" session variable.
 | ||||||
| func SessionUser(sess session.Store) *models.User { | func SessionUser(sess SessionStore) *models.User { | ||||||
| 	// Get user ID
 | 	// Get user ID
 | ||||||
| 	uid := sess.Get("uid") | 	uid := sess.Get("uid") | ||||||
| 	if uid == nil { | 	if uid == nil { | ||||||
|  | @ -96,22 +95,22 @@ func SessionUser(sess session.Store) *models.User { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // isAPIPath returns true if the specified URL is an API path
 | // isAPIPath returns true if the specified URL is an API path
 | ||||||
| func isAPIPath(ctx *macaron.Context) bool { | func isAPIPath(req *http.Request) bool { | ||||||
| 	return strings.HasPrefix(ctx.Req.URL.Path, "/api/") | 	return strings.HasPrefix(req.URL.Path, "/api/") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // isInternalPath returns true if the specified URL is an internal API path
 | // isInternalPath returns true if the specified URL is an internal API path
 | ||||||
| func isInternalPath(ctx *macaron.Context) bool { | func isInternalPath(req *http.Request) bool { | ||||||
| 	return strings.HasPrefix(ctx.Req.URL.Path, "/api/internal/") | 	return strings.HasPrefix(req.URL.Path, "/api/internal/") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // isAttachmentDownload check if request is a file download (GET) with URL to an attachment
 | // isAttachmentDownload check if request is a file download (GET) with URL to an attachment
 | ||||||
| func isAttachmentDownload(ctx *macaron.Context) bool { | func isAttachmentDownload(req *http.Request) bool { | ||||||
| 	return strings.HasPrefix(ctx.Req.URL.Path, "/attachments/") && ctx.Req.Method == "GET" | 	return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // handleSignIn clears existing session variables and stores new ones for the specified user object
 | // handleSignIn clears existing session variables and stores new ones for the specified user object
 | ||||||
| func handleSignIn(ctx *macaron.Context, sess session.Store, user *models.User) { | func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *models.User) { | ||||||
| 	_ = sess.Delete("openid_verified_uri") | 	_ = sess.Delete("openid_verified_uri") | ||||||
| 	_ = sess.Delete("openid_signin_remember") | 	_ = sess.Delete("openid_signin_remember") | ||||||
| 	_ = sess.Delete("openid_determined_email") | 	_ = sess.Delete("openid_determined_email") | ||||||
|  | @ -132,15 +131,16 @@ func handleSignIn(ctx *macaron.Context, sess session.Store, user *models.User) { | ||||||
| 	// Language setting of the user overwrites the one previously set
 | 	// Language setting of the user overwrites the one previously set
 | ||||||
| 	// If the user does not have a locale set, we save the current one.
 | 	// If the user does not have a locale set, we save the current one.
 | ||||||
| 	if len(user.Language) == 0 { | 	if len(user.Language) == 0 { | ||||||
| 		user.Language = ctx.Locale.Language() | 		lc := middlewares.Locale(resp, req) | ||||||
|  | 		user.Language = lc.Language() | ||||||
| 		if err := models.UpdateUserCols(user, "language"); err != nil { | 		if err := models.UpdateUserCols(user, "language"); err != nil { | ||||||
| 			log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", user.ID, user.Language)) | 			log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", user.ID, user.Language)) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx.SetCookie("lang", user.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) | 	middlewares.SetCookie(resp, "lang", user.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) | ||||||
| 
 | 
 | ||||||
| 	// Clear whatever CSRF has right now, force to generate a new one
 | 	// Clear whatever CSRF has right now, force to generate a new one
 | ||||||
| 	ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) | 	middlewares.SetCookie(resp, setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ package sso | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"net/http" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | @ -64,7 +65,7 @@ func (s *SSPI) IsEnabled() bool { | ||||||
| // If authentication is successful, returs the corresponding user object.
 | // If authentication is successful, returs the corresponding user object.
 | ||||||
| // If negotiation should continue or authentication fails, immediately returns a 401 HTTP
 | // If negotiation should continue or authentication fails, immediately returns a 401 HTTP
 | ||||||
| // response code, as required by the SPNEGO protocol.
 | // response code, as required by the SPNEGO protocol.
 | ||||||
| func (s *SSPI) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { | func (s *SSPI) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { | ||||||
| 	if !s.shouldAuthenticate(ctx) { | 	if !s.shouldAuthenticate(ctx) { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  | @ -75,7 +76,7 @@ func (s *SSPI) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models. | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	userInfo, outToken, err := sspiAuth.Authenticate(ctx.Req.Request, ctx.Resp) | 	userInfo, outToken, err := sspiAuth.Authenticate(req, ctx.Resp) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Warn("Authentication failed with error: %v\n", err) | 		log.Warn("Authentication failed with error: %v\n", err) | ||||||
| 		sspiAuth.AppendAuthenticateHeader(ctx.Resp, outToken) | 		sspiAuth.AppendAuthenticateHeader(ctx.Resp, outToken) | ||||||
|  | @ -139,18 +140,18 @@ func (s *SSPI) getConfig() (*models.SSPIConfig, error) { | ||||||
| 	return sources[0].SSPI(), nil | 	return sources[0].SSPI(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *SSPI) shouldAuthenticate(ctx *macaron.Context) (shouldAuth bool) { | func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) { | ||||||
| 	shouldAuth = false | 	shouldAuth = false | ||||||
| 	path := strings.TrimSuffix(ctx.Req.URL.Path, "/") | 	path := strings.TrimSuffix(req.URL.Path, "/") | ||||||
| 	if path == "/user/login" { | 	if path == "/user/login" { | ||||||
| 		if ctx.Req.FormValue("user_name") != "" && ctx.Req.FormValue("password") != "" { | 		if req.FormValue("user_name") != "" && req.FormValue("password") != "" { | ||||||
| 			shouldAuth = false | 			shouldAuth = false | ||||||
| 		} else if ctx.Req.FormValue("auth_with_sspi") == "1" { | 		} else if ctx.Req.FormValue("auth_with_sspi") == "1" { | ||||||
| 			shouldAuth = true | 			shouldAuth = true | ||||||
| 		} | 		} | ||||||
| 	} else if isInternalPath(ctx) { | 	} else if isInternalPath(req) { | ||||||
| 		shouldAuth = false | 		shouldAuth = false | ||||||
| 	} else if isAPIPath(ctx) || isAttachmentDownload(ctx) { | 	} else if isAPIPath(req) || isAttachmentDownload(req) { | ||||||
| 		shouldAuth = true | 		shouldAuth = true | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
|  | @ -158,7 +159,7 @@ func (s *SSPI) shouldAuthenticate(ctx *macaron.Context) (shouldAuth bool) { | ||||||
| 
 | 
 | ||||||
| // newUser creates a new user object for the purpose of automatic registration
 | // newUser creates a new user object for the purpose of automatic registration
 | ||||||
| // and populates its name and email with the information present in request headers.
 | // and populates its name and email with the information present in request headers.
 | ||||||
| func (s *SSPI) newUser(ctx *macaron.Context, username string, cfg *models.SSPIConfig) (*models.User, error) { | func (s *SSPI) newUser(username string, cfg *models.SSPIConfig) (*models.User, error) { | ||||||
| 	email := gouuid.New().String() + "@localhost.localdomain" | 	email := gouuid.New().String() + "@localhost.localdomain" | ||||||
| 	user := &models.User{ | 	user := &models.User{ | ||||||
| 		Name:                         username, | 		Name:                         username, | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								modules/auth/sso/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								modules/auth/sso/user.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package sso | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // SignedInUser returns the user object of signed user.
 | ||||||
|  | // It returns a bool value to indicate whether user uses basic auth or not.
 | ||||||
|  | func SignedInUser(req *http.Request, ds DataStore, sess SessionStore) (*models.User, bool) { | ||||||
|  | 	if !models.HasEngine { | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Try to sign in with each of the enabled plugins
 | ||||||
|  | 	for _, ssoMethod := range Methods() { | ||||||
|  | 		if !ssoMethod.IsEnabled() { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		user := ssoMethod.VerifyAuthData(req, ds, sess) | ||||||
|  | 		if user != nil { | ||||||
|  | 			_, isBasic := ssoMethod.(*Basic) | ||||||
|  | 			return user, isBasic | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil, false | ||||||
|  | } | ||||||
|  | @ -17,6 +17,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/modules/auth" | 	"code.gitea.io/gitea/modules/auth" | ||||||
|  | 	"code.gitea.io/gitea/modules/auth/sso" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | @ -48,6 +49,11 @@ type Context struct { | ||||||
| 	Org  *Organization | 	Org  *Organization | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // GetData returns the data
 | ||||||
|  | func (ctx *Context) GetData() map[string]interface{} { | ||||||
|  | 	return ctx.Data | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // IsUserSiteAdmin returns true if current user is a site admin
 | // IsUserSiteAdmin returns true if current user is a site admin
 | ||||||
| func (ctx *Context) IsUserSiteAdmin() bool { | func (ctx *Context) IsUserSiteAdmin() bool { | ||||||
| 	return ctx.IsSigned && ctx.User.IsAdmin | 	return ctx.IsSigned && ctx.User.IsAdmin | ||||||
|  | @ -303,7 +309,7 @@ func Contexter() macaron.Handler { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Get user from session if logged in.
 | 		// Get user from session if logged in.
 | ||||||
| 		ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Context, ctx.Session) | 		ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req.Request, ctx, ctx.Session) | ||||||
| 
 | 
 | ||||||
| 		if ctx.User != nil { | 		if ctx.User != nil { | ||||||
| 			ctx.IsSigned = true | 			ctx.IsSigned = true | ||||||
|  |  | ||||||
|  | @ -1,41 +0,0 @@ | ||||||
| // Copyright 2013 Martini Authors
 |  | ||||||
| // Copyright 2014 The Macaron Authors
 |  | ||||||
| // Copyright 2019 The Gitea Authors. All rights reserved.
 |  | ||||||
| //
 |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License"): you may
 |  | ||||||
| // not use this file except in compliance with the License. You may obtain
 |  | ||||||
| // a copy of the License at
 |  | ||||||
| //
 |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
| //
 |  | ||||||
| // Unless required by applicable law or agreed to in writing, software
 |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 |  | ||||||
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 |  | ||||||
| // License for the specific language governing permissions and limitations
 |  | ||||||
| // under the License.
 |  | ||||||
| 
 |  | ||||||
| package context |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 
 |  | ||||||
| 	"code.gitea.io/gitea/modules/log" |  | ||||||
| 
 |  | ||||||
| 	"gitea.com/macaron/macaron" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
 |  | ||||||
| // Although similar to macaron.Recovery() the main difference is that this error will be created
 |  | ||||||
| // with the gitea 500 page.
 |  | ||||||
| func Recovery() macaron.Handler { |  | ||||||
| 	return func(ctx *Context) { |  | ||||||
| 		defer func() { |  | ||||||
| 			if err := recover(); err != nil { |  | ||||||
| 				combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2)) |  | ||||||
| 				ctx.ServerError("PANIC:", combinedErr) |  | ||||||
| 			} |  | ||||||
| 		}() |  | ||||||
| 
 |  | ||||||
| 		ctx.Next() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										104
									
								
								modules/middlewares/cookie.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								modules/middlewares/cookie.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,104 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package middlewares | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // NewCookie creates a cookie
 | ||||||
|  | func NewCookie(name, value string, maxAge int) *http.Cookie { | ||||||
|  | 	return &http.Cookie{ | ||||||
|  | 		Name:     name, | ||||||
|  | 		Value:    value, | ||||||
|  | 		HttpOnly: true, | ||||||
|  | 		Path:     setting.SessionConfig.CookiePath, | ||||||
|  | 		Domain:   setting.SessionConfig.Domain, | ||||||
|  | 		MaxAge:   maxAge, | ||||||
|  | 		Secure:   setting.SessionConfig.Secure, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetCookie set the cookies
 | ||||||
|  | // TODO: Copied from gitea.com/macaron/macaron and should be improved after macaron removed.
 | ||||||
|  | func SetCookie(resp http.ResponseWriter, name string, value string, others ...interface{}) { | ||||||
|  | 	cookie := http.Cookie{} | ||||||
|  | 	cookie.Name = name | ||||||
|  | 	cookie.Value = url.QueryEscape(value) | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 0 { | ||||||
|  | 		switch v := others[0].(type) { | ||||||
|  | 		case int: | ||||||
|  | 			cookie.MaxAge = v | ||||||
|  | 		case int64: | ||||||
|  | 			cookie.MaxAge = int(v) | ||||||
|  | 		case int32: | ||||||
|  | 			cookie.MaxAge = int(v) | ||||||
|  | 		case func(*http.Cookie): | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cookie.Path = "/" | ||||||
|  | 	if len(others) > 1 { | ||||||
|  | 		if v, ok := others[1].(string); ok && len(v) > 0 { | ||||||
|  | 			cookie.Path = v | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 2 { | ||||||
|  | 		if v, ok := others[2].(string); ok && len(v) > 0 { | ||||||
|  | 			cookie.Domain = v | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 3 { | ||||||
|  | 		switch v := others[3].(type) { | ||||||
|  | 		case bool: | ||||||
|  | 			cookie.Secure = v | ||||||
|  | 		case func(*http.Cookie): | ||||||
|  | 			v(&cookie) | ||||||
|  | 		default: | ||||||
|  | 			if others[3] != nil { | ||||||
|  | 				cookie.Secure = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 4 { | ||||||
|  | 		if v, ok := others[4].(bool); ok && v { | ||||||
|  | 			cookie.HttpOnly = true | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 5 { | ||||||
|  | 		if v, ok := others[5].(time.Time); ok { | ||||||
|  | 			cookie.Expires = v | ||||||
|  | 			cookie.RawExpires = v.Format(time.UnixDate) | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 6 { | ||||||
|  | 		for _, other := range others[6:] { | ||||||
|  | 			if v, ok := other.(func(*http.Cookie)); ok { | ||||||
|  | 				v(&cookie) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	resp.Header().Add("Set-Cookie", cookie.String()) | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								modules/middlewares/locale.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								modules/middlewares/locale.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package middlewares | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/modules/translation" | ||||||
|  | 
 | ||||||
|  | 	"github.com/unknwon/i18n" | ||||||
|  | 	"golang.org/x/text/language" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Locale handle locale
 | ||||||
|  | func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale { | ||||||
|  | 	hasCookie := false | ||||||
|  | 
 | ||||||
|  | 	// 1. Check URL arguments.
 | ||||||
|  | 	lang := req.URL.Query().Get("lang") | ||||||
|  | 
 | ||||||
|  | 	// 2. Get language information from cookies.
 | ||||||
|  | 	if len(lang) == 0 { | ||||||
|  | 		ck, _ := req.Cookie("lang") | ||||||
|  | 		lang = ck.Value | ||||||
|  | 		hasCookie = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Check again in case someone modify by purpose.
 | ||||||
|  | 	if !i18n.IsExist(lang) { | ||||||
|  | 		lang = "" | ||||||
|  | 		hasCookie = false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// 3. Get language information from 'Accept-Language'.
 | ||||||
|  | 	// The first element in the list is chosen to be the default language automatically.
 | ||||||
|  | 	if len(lang) == 0 { | ||||||
|  | 		tags, _, _ := language.ParseAcceptLanguage(req.Header.Get("Accept-Language")) | ||||||
|  | 		tag, _, _ := translation.Match(tags...) | ||||||
|  | 		lang = tag.String() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !hasCookie { | ||||||
|  | 		req.AddCookie(NewCookie("lang", lang, 1<<31-1)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return translation.NewLocale(lang) | ||||||
|  | } | ||||||
							
								
								
									
										217
									
								
								modules/middlewares/redis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								modules/middlewares/redis.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,217 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package middlewares | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/modules/nosql" | ||||||
|  | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
|  | 	"github.com/go-redis/redis/v7" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // RedisStore represents a redis session store implementation.
 | ||||||
|  | // TODO: copied from modules/session/redis.go and should remove that one until macaron removed.
 | ||||||
|  | type RedisStore struct { | ||||||
|  | 	c           redis.UniversalClient | ||||||
|  | 	prefix, sid string | ||||||
|  | 	duration    time.Duration | ||||||
|  | 	lock        sync.RWMutex | ||||||
|  | 	data        map[interface{}]interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewRedisStore creates and returns a redis session store.
 | ||||||
|  | func NewRedisStore(c redis.UniversalClient, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore { | ||||||
|  | 	return &RedisStore{ | ||||||
|  | 		c:        c, | ||||||
|  | 		prefix:   prefix, | ||||||
|  | 		sid:      sid, | ||||||
|  | 		duration: dur, | ||||||
|  | 		data:     kv, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *RedisStore) Set(key, val interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = val | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *RedisStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete delete a key from session.
 | ||||||
|  | func (s *RedisStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *RedisStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases resource and save data to provider.
 | ||||||
|  | func (s *RedisStore) Release() error { | ||||||
|  | 	// Skip encoding if the data is empty
 | ||||||
|  | 	if len(s.data) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, err := session.EncodeGob(s.data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return s.c.Set(s.prefix+s.sid, string(data), s.duration).Err() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *RedisStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // RedisProvider represents a redis session provider implementation.
 | ||||||
|  | type RedisProvider struct { | ||||||
|  | 	c        redis.UniversalClient | ||||||
|  | 	duration time.Duration | ||||||
|  | 	prefix   string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes redis session provider.
 | ||||||
|  | // configs: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,prefix=session;
 | ||||||
|  | func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) { | ||||||
|  | 	p.duration, err = time.ParseDuration(fmt.Sprintf("%ds", maxlifetime)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	uri := nosql.ToRedisURI(configs) | ||||||
|  | 
 | ||||||
|  | 	for k, v := range uri.Query() { | ||||||
|  | 		switch k { | ||||||
|  | 		case "prefix": | ||||||
|  | 			p.prefix = v[0] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.c = nosql.GetManager().GetRedisClient(uri.String()) | ||||||
|  | 	return p.c.Ping().Err() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (p *RedisProvider) Read(sid string) (session.RawStore, error) { | ||||||
|  | 	psid := p.prefix + sid | ||||||
|  | 	if !p.Exist(sid) { | ||||||
|  | 		if err := p.c.Set(psid, "", p.duration).Err(); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	kvs, err := p.c.Get(psid).Result() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(kvs) == 0 { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob([]byte(kvs)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (p *RedisProvider) Exist(sid string) bool { | ||||||
|  | 	v, err := p.c.Exists(p.prefix + sid).Result() | ||||||
|  | 	return err == nil && v == 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (p *RedisProvider) Destroy(sid string) error { | ||||||
|  | 	return p.c.Del(p.prefix + sid).Err() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { | ||||||
|  | 	poldsid := p.prefix + oldsid | ||||||
|  | 	psid := p.prefix + sid | ||||||
|  | 
 | ||||||
|  | 	if p.Exist(sid) { | ||||||
|  | 		return nil, fmt.Errorf("new sid '%s' already exists", sid) | ||||||
|  | 	} else if !p.Exist(oldsid) { | ||||||
|  | 		// Make a fake old session.
 | ||||||
|  | 		if err = p.c.Set(poldsid, "", p.duration).Err(); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = p.c.Rename(poldsid, psid).Err(); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	kvs, err := p.c.Get(psid).Result() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(kvs) == 0 { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob([]byte(kvs)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (p *RedisProvider) Count() int { | ||||||
|  | 	return int(p.c.DBSize().Val()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (*RedisProvider) GC() {} | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	session.Register("redis", &RedisProvider{}) | ||||||
|  | } | ||||||
							
								
								
									
										196
									
								
								modules/middlewares/virtual.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								modules/middlewares/virtual.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,196 @@ | ||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package middlewares | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"sync" | ||||||
|  | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
|  | 	couchbase "gitea.com/go-chi/session/couchbase" | ||||||
|  | 	memcache "gitea.com/go-chi/session/memcache" | ||||||
|  | 	mysql "gitea.com/go-chi/session/mysql" | ||||||
|  | 	postgres "gitea.com/go-chi/session/postgres" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // VirtualSessionProvider represents a shadowed session provider implementation.
 | ||||||
|  | // TODO: copied from modules/session/redis.go and should remove that one until macaron removed.
 | ||||||
|  | type VirtualSessionProvider struct { | ||||||
|  | 	lock     sync.RWMutex | ||||||
|  | 	provider session.Provider | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes the cookie session provider with given root path.
 | ||||||
|  | func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error { | ||||||
|  | 	var opts session.Options | ||||||
|  | 	if err := json.Unmarshal([]byte(config), &opts); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// Note that these options are unprepared so we can't just use NewManager here.
 | ||||||
|  | 	// Nor can we access the provider map in session.
 | ||||||
|  | 	// So we will just have to do this by hand.
 | ||||||
|  | 	// This is only slightly more wrong than modules/setting/session.go:23
 | ||||||
|  | 	switch opts.Provider { | ||||||
|  | 	case "memory": | ||||||
|  | 		o.provider = &session.MemProvider{} | ||||||
|  | 	case "file": | ||||||
|  | 		o.provider = &session.FileProvider{} | ||||||
|  | 	case "redis": | ||||||
|  | 		o.provider = &RedisProvider{} | ||||||
|  | 	case "mysql": | ||||||
|  | 		o.provider = &mysql.MysqlProvider{} | ||||||
|  | 	case "postgres": | ||||||
|  | 		o.provider = &postgres.PostgresProvider{} | ||||||
|  | 	case "couchbase": | ||||||
|  | 		o.provider = &couchbase.CouchbaseProvider{} | ||||||
|  | 	case "memcache": | ||||||
|  | 		o.provider = &memcache.MemcacheProvider{} | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider) | ||||||
|  | 	} | ||||||
|  | 	return o.provider.Init(gclifetime, opts.ProviderConfig) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) { | ||||||
|  | 	o.lock.RLock() | ||||||
|  | 	defer o.lock.RUnlock() | ||||||
|  | 	if o.provider.Exist(sid) { | ||||||
|  | 		return o.provider.Read(sid) | ||||||
|  | 	} | ||||||
|  | 	kv := make(map[interface{}]interface{}) | ||||||
|  | 	kv["_old_uid"] = "0" | ||||||
|  | 	return NewVirtualStore(o, sid, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (o *VirtualSessionProvider) Exist(sid string) bool { | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (o *VirtualSessionProvider) Destroy(sid string) error { | ||||||
|  | 	o.lock.Lock() | ||||||
|  | 	defer o.lock.Unlock() | ||||||
|  | 	return o.provider.Destroy(sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { | ||||||
|  | 	o.lock.Lock() | ||||||
|  | 	defer o.lock.Unlock() | ||||||
|  | 	return o.provider.Regenerate(oldsid, sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (o *VirtualSessionProvider) Count() int { | ||||||
|  | 	o.lock.RLock() | ||||||
|  | 	defer o.lock.RUnlock() | ||||||
|  | 	return o.provider.Count() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (o *VirtualSessionProvider) GC() { | ||||||
|  | 	o.provider.GC() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	session.Register("VirtualSession", &VirtualSessionProvider{}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // VirtualStore represents a virtual session store implementation.
 | ||||||
|  | type VirtualStore struct { | ||||||
|  | 	p        *VirtualSessionProvider | ||||||
|  | 	sid      string | ||||||
|  | 	lock     sync.RWMutex | ||||||
|  | 	data     map[interface{}]interface{} | ||||||
|  | 	released bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewVirtualStore creates and returns a virtual session store.
 | ||||||
|  | func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore { | ||||||
|  | 	return &VirtualStore{ | ||||||
|  | 		p:    p, | ||||||
|  | 		sid:  sid, | ||||||
|  | 		data: kv, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *VirtualStore) Set(key, val interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = val | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *VirtualStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete delete a key from session.
 | ||||||
|  | func (s *VirtualStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *VirtualStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases resource and save data to provider.
 | ||||||
|  | func (s *VirtualStore) Release() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 	// Now need to lock the provider
 | ||||||
|  | 	s.p.lock.Lock() | ||||||
|  | 	defer s.p.lock.Unlock() | ||||||
|  | 	if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) { | ||||||
|  | 		// Now ensure that we don't exist!
 | ||||||
|  | 		realProvider := s.p.provider | ||||||
|  | 
 | ||||||
|  | 		if !s.released && realProvider.Exist(s.sid) { | ||||||
|  | 			// This is an error!
 | ||||||
|  | 			return fmt.Errorf("new sid '%s' already exists", s.sid) | ||||||
|  | 		} | ||||||
|  | 		realStore, err := realProvider.Read(s.sid) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if err := realStore.Flush(); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		for key, value := range s.data { | ||||||
|  | 			if err := realStore.Set(key, value); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		err = realStore.Release() | ||||||
|  | 		if err == nil { | ||||||
|  | 			s.released = true | ||||||
|  | 		} | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *VirtualStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								modules/templates/base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								modules/templates/base.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package templates | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Vars represents variables to be render in golang templates
 | ||||||
|  | type Vars map[string]interface{} | ||||||
|  | 
 | ||||||
|  | // Merge merges another vars to the current, another Vars will override the current
 | ||||||
|  | func (vars Vars) Merge(another map[string]interface{}) Vars { | ||||||
|  | 	for k, v := range another { | ||||||
|  | 		vars[k] = v | ||||||
|  | 	} | ||||||
|  | 	return vars | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // BaseVars returns all basic vars
 | ||||||
|  | func BaseVars() Vars { | ||||||
|  | 	var startTime = time.Now() | ||||||
|  | 	return map[string]interface{}{ | ||||||
|  | 		"IsLandingPageHome":          setting.LandingPageURL == setting.LandingPageHome, | ||||||
|  | 		"IsLandingPageExplore":       setting.LandingPageURL == setting.LandingPageExplore, | ||||||
|  | 		"IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations, | ||||||
|  | 
 | ||||||
|  | 		"ShowRegistrationButton":      setting.Service.ShowRegistrationButton, | ||||||
|  | 		"ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage, | ||||||
|  | 		"ShowFooterBranding":          setting.ShowFooterBranding, | ||||||
|  | 		"ShowFooterVersion":           setting.ShowFooterVersion, | ||||||
|  | 
 | ||||||
|  | 		"EnableSwagger":      setting.API.EnableSwagger, | ||||||
|  | 		"EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn, | ||||||
|  | 		"PageStartTime":      startTime, | ||||||
|  | 		"TmplLoadTimes": func() string { | ||||||
|  | 			return time.Since(startTime).String() | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getDirAssetNames(dir string) []string { | ||||||
|  | 	var tmpls []string | ||||||
|  | 	f, err := os.Stat(dir) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if os.IsNotExist(err) { | ||||||
|  | 			return tmpls | ||||||
|  | 		} | ||||||
|  | 		log.Warn("Unable to check if templates dir %s is a directory. Error: %v", dir, err) | ||||||
|  | 		return tmpls | ||||||
|  | 	} | ||||||
|  | 	if !f.IsDir() { | ||||||
|  | 		log.Warn("Templates dir %s is a not directory.", dir) | ||||||
|  | 		return tmpls | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	files, err := util.StatDir(dir) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warn("Failed to read %s templates dir. %v", dir, err) | ||||||
|  | 		return tmpls | ||||||
|  | 	} | ||||||
|  | 	for _, filePath := range files { | ||||||
|  | 		if strings.HasPrefix(filePath, "mail/") { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if !strings.HasSuffix(filePath, ".tmpl") { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		tmpls = append(tmpls, "templates/"+filePath) | ||||||
|  | 	} | ||||||
|  | 	return tmpls | ||||||
|  | } | ||||||
|  | @ -9,7 +9,9 @@ package templates | ||||||
| import ( | import ( | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	texttmpl "text/template" | 	texttmpl "text/template" | ||||||
| 
 | 
 | ||||||
|  | @ -25,6 +27,25 @@ var ( | ||||||
| 	bodyTemplates    = template.New("") | 	bodyTemplates    = template.New("") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // GetAsset returns asset content via name
 | ||||||
|  | func GetAsset(name string) ([]byte, error) { | ||||||
|  | 	bs, err := ioutil.ReadFile(filepath.Join(setting.CustomPath, name)) | ||||||
|  | 	if err != nil && !os.IsNotExist(err) { | ||||||
|  | 		return nil, err | ||||||
|  | 	} else if err == nil { | ||||||
|  | 		return bs, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ioutil.ReadFile(filepath.Join(setting.StaticRootPath, name)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetAssetNames returns assets list
 | ||||||
|  | func GetAssetNames() []string { | ||||||
|  | 	tmpls := getDirAssetNames(filepath.Join(setting.CustomPath, "templates")) | ||||||
|  | 	tmpls2 := getDirAssetNames(filepath.Join(setting.StaticRootPath, "templates")) | ||||||
|  | 	return append(tmpls, tmpls2...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // HTMLRenderer implements the macaron handler for serving HTML templates.
 | // HTMLRenderer implements the macaron handler for serving HTML templates.
 | ||||||
| func HTMLRenderer() macaron.Handler { | func HTMLRenderer() macaron.Handler { | ||||||
| 	return macaron.Renderer(macaron.RenderOptions{ | 	return macaron.Renderer(macaron.RenderOptions{ | ||||||
|  |  | ||||||
|  | @ -12,7 +12,9 @@ import ( | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	texttmpl "text/template" | 	texttmpl "text/template" | ||||||
| 
 | 
 | ||||||
|  | @ -46,6 +48,30 @@ func (templates templateFileSystem) Get(name string) (io.Reader, error) { | ||||||
| 	return nil, fmt.Errorf("file '%s' not found", name) | 	return nil, fmt.Errorf("file '%s' not found", name) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // GetAsset get a special asset, only for chi
 | ||||||
|  | func GetAsset(name string) ([]byte, error) { | ||||||
|  | 	bs, err := ioutil.ReadFile(filepath.Join(setting.CustomPath, name)) | ||||||
|  | 	if err != nil && !os.IsNotExist(err) { | ||||||
|  | 		return nil, err | ||||||
|  | 	} else if err == nil { | ||||||
|  | 		return bs, nil | ||||||
|  | 	} | ||||||
|  | 	return Asset(strings.TrimPrefix(name, "templates/")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetAssetNames only for chi
 | ||||||
|  | func GetAssetNames() []string { | ||||||
|  | 	realFS := Assets.(vfsgen۰FS) | ||||||
|  | 	var tmpls = make([]string, 0, len(realFS)) | ||||||
|  | 	for k := range realFS { | ||||||
|  | 		tmpls = append(tmpls, "templates/"+k[1:]) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	customDir := path.Join(setting.CustomPath, "templates") | ||||||
|  | 	customTmpls := getDirAssetNames(customDir) | ||||||
|  | 	return append(tmpls, customTmpls...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func NewTemplateFileSystem() templateFileSystem { | func NewTemplateFileSystem() templateFileSystem { | ||||||
| 	fs := templateFileSystem{} | 	fs := templateFileSystem{} | ||||||
| 	fs.files = make([]macaron.TemplateFile, 0, 10) | 	fs.files = make([]macaron.TemplateFile, 0, 10) | ||||||
|  |  | ||||||
							
								
								
									
										92
									
								
								modules/translation/translation.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								modules/translation/translation.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package translation | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/options" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 
 | ||||||
|  | 	macaron_i18n "gitea.com/macaron/i18n" | ||||||
|  | 	"github.com/unknwon/i18n" | ||||||
|  | 	"golang.org/x/text/language" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Locale represents an interface to translation
 | ||||||
|  | type Locale interface { | ||||||
|  | 	Language() string | ||||||
|  | 	Tr(string, ...interface{}) string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	matcher language.Matcher | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // InitLocales loads the locales
 | ||||||
|  | func InitLocales() { | ||||||
|  | 	localeNames, err := options.Dir("locale") | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatal("Failed to list locale files: %v", err) | ||||||
|  | 	} | ||||||
|  | 	localFiles := make(map[string][]byte) | ||||||
|  | 
 | ||||||
|  | 	for _, name := range localeNames { | ||||||
|  | 		localFiles[name], err = options.Locale(name) | ||||||
|  | 
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatal("Failed to load %s locale file. %v", name, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// These codes will be used once macaron removed
 | ||||||
|  | 	/*tags := make([]language.Tag, len(setting.Langs)) | ||||||
|  | 	for i, lang := range setting.Langs { | ||||||
|  | 		tags[i] = language.Raw.Make(lang) | ||||||
|  | 	} | ||||||
|  | 	matcher = language.NewMatcher(tags) | ||||||
|  | 	for i, name := range setting.Names { | ||||||
|  | 		i18n.SetMessage(setting.Langs[i], localFiles[name]) | ||||||
|  | 	} | ||||||
|  | 	i18n.SetDefaultLang("en-US")*/ | ||||||
|  | 
 | ||||||
|  | 	// To be compatible with macaron, we now have to use macaron i18n, once macaron
 | ||||||
|  | 	// removed, we can use i18n directly
 | ||||||
|  | 	macaron_i18n.I18n(macaron_i18n.Options{ | ||||||
|  | 		SubURL:       setting.AppSubURL, | ||||||
|  | 		Files:        localFiles, | ||||||
|  | 		Langs:        setting.Langs, | ||||||
|  | 		Names:        setting.Names, | ||||||
|  | 		DefaultLang:  "en-US", | ||||||
|  | 		Redirect:     false, | ||||||
|  | 		CookieDomain: setting.SessionConfig.Domain, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Match matches accept languages
 | ||||||
|  | func Match(tags ...language.Tag) (tag language.Tag, index int, c language.Confidence) { | ||||||
|  | 	return matcher.Match(tags...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // locale represents the information of localization.
 | ||||||
|  | type locale struct { | ||||||
|  | 	Lang string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewLocale return a locale
 | ||||||
|  | func NewLocale(lang string) Locale { | ||||||
|  | 	return &locale{ | ||||||
|  | 		Lang: lang, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *locale) Language() string { | ||||||
|  | 	return l.Lang | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Tr translates content to target language.
 | ||||||
|  | func (l *locale) Tr(format string, args ...interface{}) string { | ||||||
|  | 	return i18n.Tr(l.Lang, format, args...) | ||||||
|  | } | ||||||
|  | @ -26,19 +26,18 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/markup/external" | 	"code.gitea.io/gitea/modules/markup/external" | ||||||
| 	repo_migrations "code.gitea.io/gitea/modules/migrations" | 	repo_migrations "code.gitea.io/gitea/modules/migrations" | ||||||
| 	"code.gitea.io/gitea/modules/notification" | 	"code.gitea.io/gitea/modules/notification" | ||||||
| 	"code.gitea.io/gitea/modules/options" |  | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/ssh" | 	"code.gitea.io/gitea/modules/ssh" | ||||||
| 	"code.gitea.io/gitea/modules/storage" | 	"code.gitea.io/gitea/modules/storage" | ||||||
| 	"code.gitea.io/gitea/modules/svg" | 	"code.gitea.io/gitea/modules/svg" | ||||||
| 	"code.gitea.io/gitea/modules/task" | 	"code.gitea.io/gitea/modules/task" | ||||||
|  | 	"code.gitea.io/gitea/modules/translation" | ||||||
| 	"code.gitea.io/gitea/services/mailer" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| 	mirror_service "code.gitea.io/gitea/services/mirror" | 	mirror_service "code.gitea.io/gitea/services/mirror" | ||||||
| 	pull_service "code.gitea.io/gitea/services/pull" | 	pull_service "code.gitea.io/gitea/services/pull" | ||||||
| 	"code.gitea.io/gitea/services/repository" | 	"code.gitea.io/gitea/services/repository" | ||||||
| 	"code.gitea.io/gitea/services/webhook" | 	"code.gitea.io/gitea/services/webhook" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/macaron/i18n" |  | ||||||
| 	"gitea.com/macaron/macaron" | 	"gitea.com/macaron/macaron" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -93,33 +92,6 @@ func initDBEngine(ctx context.Context) (err error) { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // InitLocales loads the locales
 |  | ||||||
| func InitLocales() { |  | ||||||
| 	localeNames, err := options.Dir("locale") |  | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal("Failed to list locale files: %v", err) |  | ||||||
| 	} |  | ||||||
| 	localFiles := make(map[string][]byte) |  | ||||||
| 
 |  | ||||||
| 	for _, name := range localeNames { |  | ||||||
| 		localFiles[name], err = options.Locale(name) |  | ||||||
| 
 |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Failed to load %s locale file. %v", name, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	i18n.I18n(i18n.Options{ |  | ||||||
| 		SubURL:       setting.AppSubURL, |  | ||||||
| 		Files:        localFiles, |  | ||||||
| 		Langs:        setting.Langs, |  | ||||||
| 		Names:        setting.Names, |  | ||||||
| 		DefaultLang:  "en-US", |  | ||||||
| 		Redirect:     false, |  | ||||||
| 		CookieDomain: setting.SessionConfig.Domain, |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // PreInstallInit preloads the configuration to check if we need to run install
 | // PreInstallInit preloads the configuration to check if we need to run install
 | ||||||
| func PreInstallInit(ctx context.Context) bool { | func PreInstallInit(ctx context.Context) bool { | ||||||
| 	setting.NewContext() | 	setting.NewContext() | ||||||
|  | @ -129,7 +101,7 @@ func PreInstallInit(ctx context.Context) bool { | ||||||
| 		log.Trace("Custom path: %s", setting.CustomPath) | 		log.Trace("Custom path: %s", setting.CustomPath) | ||||||
| 		log.Trace("Log path: %s", setting.LogRootPath) | 		log.Trace("Log path: %s", setting.LogRootPath) | ||||||
| 		log.Trace("Preparing to run install page") | 		log.Trace("Preparing to run install page") | ||||||
| 		InitLocales() | 		translation.InitLocales() | ||||||
| 		if setting.EnableSQLite3 { | 		if setting.EnableSQLite3 { | ||||||
| 			log.Info("SQLite3 Supported") | 			log.Info("SQLite3 Supported") | ||||||
| 		} | 		} | ||||||
|  | @ -170,7 +142,7 @@ func GlobalInit(ctx context.Context) { | ||||||
| 	log.Trace("Log path: %s", setting.LogRootPath) | 	log.Trace("Log path: %s", setting.LogRootPath) | ||||||
| 
 | 
 | ||||||
| 	// Setup i18n
 | 	// Setup i18n
 | ||||||
| 	InitLocales() | 	translation.InitLocales() | ||||||
| 
 | 
 | ||||||
| 	NewServices() | 	NewServices() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/storage" | 	"code.gitea.io/gitea/modules/storage" | ||||||
| 	"code.gitea.io/gitea/routers" | 	"code.gitea.io/gitea/routers" | ||||||
| 
 | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
| 	"github.com/go-chi/chi" | 	"github.com/go-chi/chi" | ||||||
| 	"github.com/go-chi/chi/middleware" | 	"github.com/go-chi/chi/middleware" | ||||||
| 	"github.com/prometheus/client_golang/prometheus" | 	"github.com/prometheus/client_golang/prometheus" | ||||||
|  | @ -37,7 +38,7 @@ type routerLoggerOptions struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SignedUserName returns signed user's name via context
 | // SignedUserName returns signed user's name via context
 | ||||||
| // FIXME currently no any data stored on gin.Context but macaron.Context, so this will
 | // FIXME currently no any data stored on chi.Context but macaron.Context, so this will
 | ||||||
| // return "" before we remove macaron totally
 | // return "" before we remove macaron totally
 | ||||||
| func SignedUserName(req *http.Request) string { | func SignedUserName(req *http.Request) string { | ||||||
| 	if v, ok := req.Context().Value("SignedUserName").(string); ok { | 	if v, ok := req.Context().Value("SignedUserName").(string); ok { | ||||||
|  | @ -97,24 +98,6 @@ func LoggerHandler(level log.Level) func(next http.Handler) http.Handler { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
 |  | ||||||
| // Although similar to macaron.Recovery() the main difference is that this error will be created
 |  | ||||||
| // with the gitea 500 page.
 |  | ||||||
| func Recovery() func(next http.Handler) http.Handler { |  | ||||||
| 	return func(next http.Handler) http.Handler { |  | ||||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |  | ||||||
| 			defer func() { |  | ||||||
| 				if err := recover(); err != nil { |  | ||||||
| 					combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) |  | ||||||
| 					http.Error(w, combinedErr, 500) |  | ||||||
| 				} |  | ||||||
| 			}() |  | ||||||
| 
 |  | ||||||
| 			next.ServeHTTP(w, req) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { | func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { | ||||||
| 	return func(next http.Handler) http.Handler { | 	return func(next http.Handler) http.Handler { | ||||||
| 		if storageSetting.ServeDirect { | 		if storageSetting.ServeDirect { | ||||||
|  | @ -202,6 +185,17 @@ func NewChi() chi.Router { | ||||||
| 			c.Use(LoggerHandler(setting.RouterLogLevel)) | 			c.Use(LoggerHandler(setting.RouterLogLevel)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	c.Use(session.Sessioner(session.Options{ | ||||||
|  | 		Provider:       setting.SessionConfig.Provider, | ||||||
|  | 		ProviderConfig: setting.SessionConfig.ProviderConfig, | ||||||
|  | 		CookieName:     setting.SessionConfig.CookieName, | ||||||
|  | 		CookiePath:     setting.SessionConfig.CookiePath, | ||||||
|  | 		Gclifetime:     setting.SessionConfig.Gclifetime, | ||||||
|  | 		Maxlifetime:    setting.SessionConfig.Maxlifetime, | ||||||
|  | 		Secure:         setting.SessionConfig.Secure, | ||||||
|  | 		Domain:         setting.SessionConfig.Domain, | ||||||
|  | 	})) | ||||||
|  | 
 | ||||||
| 	c.Use(Recovery()) | 	c.Use(Recovery()) | ||||||
| 	if setting.EnableAccessLog { | 	if setting.EnableAccessLog { | ||||||
| 		setupAccessLogger(c) | 		setupAccessLogger(c) | ||||||
|  |  | ||||||
							
								
								
									
										105
									
								
								routers/routes/recovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								routers/routes/recovery.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,105 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package routes | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/modules/auth/sso" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/middlewares" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/templates" | ||||||
|  | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
|  | 	"github.com/unrolled/render" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type dataStore struct { | ||||||
|  | 	Data map[string]interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (d *dataStore) GetData() map[string]interface{} { | ||||||
|  | 	return d.Data | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
 | ||||||
|  | // Although similar to macaron.Recovery() the main difference is that this error will be created
 | ||||||
|  | // with the gitea 500 page.
 | ||||||
|  | func Recovery() func(next http.Handler) http.Handler { | ||||||
|  | 	var isDevelopment = setting.RunMode != "prod" | ||||||
|  | 	return func(next http.Handler) http.Handler { | ||||||
|  | 		rnd := render.New(render.Options{ | ||||||
|  | 			Extensions:    []string{".tmpl"}, | ||||||
|  | 			Directory:     "templates", | ||||||
|  | 			Funcs:         templates.NewFuncMap(), | ||||||
|  | 			Asset:         templates.GetAsset, | ||||||
|  | 			AssetNames:    templates.GetAssetNames, | ||||||
|  | 			IsDevelopment: isDevelopment, | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
|  | 			defer func() { | ||||||
|  | 				// Why we need this? The first recover will try to render a beautiful
 | ||||||
|  | 				// error page for user, but the process can still panic again, then
 | ||||||
|  | 				// we have to just recover twice and send a simple error page that
 | ||||||
|  | 				// should not panic any more.
 | ||||||
|  | 				defer func() { | ||||||
|  | 					if err := recover(); err != nil { | ||||||
|  | 						combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) | ||||||
|  | 						log.Error(combinedErr) | ||||||
|  | 						if isDevelopment { | ||||||
|  | 							http.Error(w, combinedErr, 500) | ||||||
|  | 						} else { | ||||||
|  | 							http.Error(w, http.StatusText(500), 500) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				if err := recover(); err != nil { | ||||||
|  | 					combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) | ||||||
|  | 					log.Error("%v", combinedErr) | ||||||
|  | 
 | ||||||
|  | 					lc := middlewares.Locale(w, req) | ||||||
|  | 					sess := session.GetSession(req) | ||||||
|  | 
 | ||||||
|  | 					var store = dataStore{ | ||||||
|  | 						Data: templates.Vars{ | ||||||
|  | 							"Language":   lc.Language(), | ||||||
|  | 							"CurrentURL": setting.AppSubURL + req.URL.RequestURI(), | ||||||
|  | 							"i18n":       lc, | ||||||
|  | 						}, | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					// Get user from session if logged in.
 | ||||||
|  | 					user, _ := sso.SignedInUser(req, &store, sess) | ||||||
|  | 					if user != nil { | ||||||
|  | 						store.Data["IsSigned"] = true | ||||||
|  | 						store.Data["SignedUser"] = user | ||||||
|  | 						store.Data["SignedUserID"] = user.ID | ||||||
|  | 						store.Data["SignedUserName"] = user.Name | ||||||
|  | 						store.Data["IsAdmin"] = user.IsAdmin | ||||||
|  | 					} else { | ||||||
|  | 						store.Data["SignedUserID"] = int64(0) | ||||||
|  | 						store.Data["SignedUserName"] = "" | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | ||||||
|  | 
 | ||||||
|  | 					if setting.RunMode != "prod" { | ||||||
|  | 						store.Data["ErrMsg"] = combinedErr | ||||||
|  | 					} | ||||||
|  | 					err := rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) | ||||||
|  | 					if err != nil { | ||||||
|  | 						log.Error("%v", err) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}() | ||||||
|  | 
 | ||||||
|  | 			next.ServeHTTP(w, req) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								vendor/gitea.com/go-chi/session/.drone.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/gitea.com/go-chi/session/.drone.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | --- | ||||||
|  | kind: pipeline | ||||||
|  | name: test | ||||||
|  | 
 | ||||||
|  | platform: | ||||||
|  |   os: linux | ||||||
|  |   arch: amd64 | ||||||
|  | 
 | ||||||
|  | clone: | ||||||
|  |   disable: true | ||||||
|  | 
 | ||||||
|  | workspace: | ||||||
|  |   base: /go | ||||||
|  |   path: src/session113 | ||||||
|  | 
 | ||||||
|  | steps: | ||||||
|  | - name: git | ||||||
|  |   pull: default | ||||||
|  |   image: plugins/git:next | ||||||
|  |   settings: | ||||||
|  |     depth: 50 | ||||||
|  |     tags: true | ||||||
|  | 
 | ||||||
|  | - name: test | ||||||
|  |   pull: default | ||||||
|  |   image: golang:1.13 | ||||||
|  |   environment: | ||||||
|  |     GO111MODULE: on | ||||||
|  |     GOPROXY: https://goproxy.cn | ||||||
|  |   commands: | ||||||
|  |     - go build -v | ||||||
|  |     - go vet ./... | ||||||
|  |     - go test -v -race -coverprofile=coverage.txt -covermode=atomic | ||||||
|  |   when: | ||||||
|  |     event: | ||||||
|  |     - push | ||||||
|  |     - pull_request | ||||||
							
								
								
									
										4
									
								
								vendor/gitea.com/go-chi/session/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/gitea.com/go-chi/session/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | ledis/tmp.db | ||||||
|  | nodb/tmp.db | ||||||
|  | /vendor | ||||||
|  | /.idea | ||||||
							
								
								
									
										191
									
								
								vendor/gitea.com/go-chi/session/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								vendor/gitea.com/go-chi/session/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,191 @@ | ||||||
|  | Apache License | ||||||
|  | Version 2.0, January 2004 | ||||||
|  | http://www.apache.org/licenses/ | ||||||
|  | 
 | ||||||
|  | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||||
|  | 
 | ||||||
|  | 1. Definitions. | ||||||
|  | 
 | ||||||
|  | "License" shall mean the terms and conditions for use, reproduction, and | ||||||
|  | distribution as defined by Sections 1 through 9 of this document. | ||||||
|  | 
 | ||||||
|  | "Licensor" shall mean the copyright owner or entity authorized by the copyright | ||||||
|  | owner that is granting the License. | ||||||
|  | 
 | ||||||
|  | "Legal Entity" shall mean the union of the acting entity and all other entities | ||||||
|  | that control, are controlled by, or are under common control with that entity. | ||||||
|  | For the purposes of this definition, "control" means (i) the power, direct or | ||||||
|  | indirect, to cause the direction or management of such entity, whether by | ||||||
|  | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||||
|  | outstanding shares, or (iii) beneficial ownership of such entity. | ||||||
|  | 
 | ||||||
|  | "You" (or "Your") shall mean an individual or Legal Entity exercising | ||||||
|  | permissions granted by this License. | ||||||
|  | 
 | ||||||
|  | "Source" form shall mean the preferred form for making modifications, including | ||||||
|  | but not limited to software source code, documentation source, and configuration | ||||||
|  | files. | ||||||
|  | 
 | ||||||
|  | "Object" form shall mean any form resulting from mechanical transformation or | ||||||
|  | translation of a Source form, including but not limited to compiled object code, | ||||||
|  | generated documentation, and conversions to other media types. | ||||||
|  | 
 | ||||||
|  | "Work" shall mean the work of authorship, whether in Source or Object form, made | ||||||
|  | available under the License, as indicated by a copyright notice that is included | ||||||
|  | in or attached to the work (an example is provided in the Appendix below). | ||||||
|  | 
 | ||||||
|  | "Derivative Works" shall mean any work, whether in Source or Object form, that | ||||||
|  | is based on (or derived from) the Work and for which the editorial revisions, | ||||||
|  | annotations, elaborations, or other modifications represent, as a whole, an | ||||||
|  | original work of authorship. For the purposes of this License, Derivative Works | ||||||
|  | shall not include works that remain separable from, or merely link (or bind by | ||||||
|  | name) to the interfaces of, the Work and Derivative Works thereof. | ||||||
|  | 
 | ||||||
|  | "Contribution" shall mean any work of authorship, including the original version | ||||||
|  | of the Work and any modifications or additions to that Work or Derivative Works | ||||||
|  | thereof, that is intentionally submitted to Licensor for inclusion in the Work | ||||||
|  | by the copyright owner or by an individual or Legal Entity authorized to submit | ||||||
|  | on behalf of the copyright owner. For the purposes of this definition, | ||||||
|  | "submitted" means any form of electronic, verbal, or written communication sent | ||||||
|  | to the Licensor or its representatives, including but not limited to | ||||||
|  | communication on electronic mailing lists, source code control systems, and | ||||||
|  | issue tracking systems that are managed by, or on behalf of, the Licensor for | ||||||
|  | the purpose of discussing and improving the Work, but excluding communication | ||||||
|  | that is conspicuously marked or otherwise designated in writing by the copyright | ||||||
|  | owner as "Not a Contribution." | ||||||
|  | 
 | ||||||
|  | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf | ||||||
|  | of whom a Contribution has been received by Licensor and subsequently | ||||||
|  | incorporated within the Work. | ||||||
|  | 
 | ||||||
|  | 2. Grant of Copyright License. | ||||||
|  | 
 | ||||||
|  | Subject to the terms and conditions of this License, each Contributor hereby | ||||||
|  | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||||||
|  | irrevocable copyright license to reproduce, prepare Derivative Works of, | ||||||
|  | publicly display, publicly perform, sublicense, and distribute the Work and such | ||||||
|  | Derivative Works in Source or Object form. | ||||||
|  | 
 | ||||||
|  | 3. Grant of Patent License. | ||||||
|  | 
 | ||||||
|  | Subject to the terms and conditions of this License, each Contributor hereby | ||||||
|  | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||||||
|  | irrevocable (except as stated in this section) patent license to make, have | ||||||
|  | made, use, offer to sell, sell, import, and otherwise transfer the Work, where | ||||||
|  | such license applies only to those patent claims licensable by such Contributor | ||||||
|  | that are necessarily infringed by their Contribution(s) alone or by combination | ||||||
|  | of their Contribution(s) with the Work to which such Contribution(s) was | ||||||
|  | submitted. If You institute patent litigation against any entity (including a | ||||||
|  | cross-claim or counterclaim in a lawsuit) alleging that the Work or a | ||||||
|  | Contribution incorporated within the Work constitutes direct or contributory | ||||||
|  | patent infringement, then any patent licenses granted to You under this License | ||||||
|  | for that Work shall terminate as of the date such litigation is filed. | ||||||
|  | 
 | ||||||
|  | 4. Redistribution. | ||||||
|  | 
 | ||||||
|  | You may reproduce and distribute copies of the Work or Derivative Works thereof | ||||||
|  | in any medium, with or without modifications, and in Source or Object form, | ||||||
|  | provided that You meet the following conditions: | ||||||
|  | 
 | ||||||
|  | You must give any other recipients of the Work or Derivative Works a copy of | ||||||
|  | this License; and | ||||||
|  | You must cause any modified files to carry prominent notices stating that You | ||||||
|  | changed the files; and | ||||||
|  | You must retain, in the Source form of any Derivative Works that You distribute, | ||||||
|  | all copyright, patent, trademark, and attribution notices from the Source form | ||||||
|  | of the Work, excluding those notices that do not pertain to any part of the | ||||||
|  | Derivative Works; and | ||||||
|  | If the Work includes a "NOTICE" text file as part of its distribution, then any | ||||||
|  | Derivative Works that You distribute must include a readable copy of the | ||||||
|  | attribution notices contained within such NOTICE file, excluding those notices | ||||||
|  | that do not pertain to any part of the Derivative Works, in at least one of the | ||||||
|  | following places: within a NOTICE text file distributed as part of the | ||||||
|  | Derivative Works; within the Source form or documentation, if provided along | ||||||
|  | with the Derivative Works; or, within a display generated by the Derivative | ||||||
|  | Works, if and wherever such third-party notices normally appear. The contents of | ||||||
|  | the NOTICE file are for informational purposes only and do not modify the | ||||||
|  | License. You may add Your own attribution notices within Derivative Works that | ||||||
|  | You distribute, alongside or as an addendum to the NOTICE text from the Work, | ||||||
|  | provided that such additional attribution notices cannot be construed as | ||||||
|  | modifying the License. | ||||||
|  | You may add Your own copyright statement to Your modifications and may provide | ||||||
|  | additional or different license terms and conditions for use, reproduction, or | ||||||
|  | distribution of Your modifications, or for any such Derivative Works as a whole, | ||||||
|  | provided Your use, reproduction, and distribution of the Work otherwise complies | ||||||
|  | with the conditions stated in this License. | ||||||
|  | 
 | ||||||
|  | 5. Submission of Contributions. | ||||||
|  | 
 | ||||||
|  | Unless You explicitly state otherwise, any Contribution intentionally submitted | ||||||
|  | for inclusion in the Work by You to the Licensor shall be under the terms and | ||||||
|  | conditions of this License, without any additional terms or conditions. | ||||||
|  | Notwithstanding the above, nothing herein shall supersede or modify the terms of | ||||||
|  | any separate license agreement you may have executed with Licensor regarding | ||||||
|  | such Contributions. | ||||||
|  | 
 | ||||||
|  | 6. Trademarks. | ||||||
|  | 
 | ||||||
|  | This License does not grant permission to use the trade names, trademarks, | ||||||
|  | service marks, or product names of the Licensor, except as required for | ||||||
|  | reasonable and customary use in describing the origin of the Work and | ||||||
|  | reproducing the content of the NOTICE file. | ||||||
|  | 
 | ||||||
|  | 7. Disclaimer of Warranty. | ||||||
|  | 
 | ||||||
|  | Unless required by applicable law or agreed to in writing, Licensor provides the | ||||||
|  | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, | ||||||
|  | including, without limitation, any warranties or conditions of TITLE, | ||||||
|  | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are | ||||||
|  | solely responsible for determining the appropriateness of using or | ||||||
|  | redistributing the Work and assume any risks associated with Your exercise of | ||||||
|  | permissions under this License. | ||||||
|  | 
 | ||||||
|  | 8. Limitation of Liability. | ||||||
|  | 
 | ||||||
|  | In no event and under no legal theory, whether in tort (including negligence), | ||||||
|  | contract, or otherwise, unless required by applicable law (such as deliberate | ||||||
|  | and grossly negligent acts) or agreed to in writing, shall any Contributor be | ||||||
|  | liable to You for damages, including any direct, indirect, special, incidental, | ||||||
|  | or consequential damages of any character arising as a result of this License or | ||||||
|  | out of the use or inability to use the Work (including but not limited to | ||||||
|  | damages for loss of goodwill, work stoppage, computer failure or malfunction, or | ||||||
|  | any and all other commercial damages or losses), even if such Contributor has | ||||||
|  | been advised of the possibility of such damages. | ||||||
|  | 
 | ||||||
|  | 9. Accepting Warranty or Additional Liability. | ||||||
|  | 
 | ||||||
|  | While redistributing the Work or Derivative Works thereof, You may choose to | ||||||
|  | offer, and charge a fee for, acceptance of support, warranty, indemnity, or | ||||||
|  | other liability obligations and/or rights consistent with this License. However, | ||||||
|  | in accepting such obligations, You may act only on Your own behalf and on Your | ||||||
|  | sole responsibility, not on behalf of any other Contributor, and only if You | ||||||
|  | agree to indemnify, defend, and hold each Contributor harmless for any liability | ||||||
|  | incurred by, or claims asserted against, such Contributor by reason of your | ||||||
|  | accepting any such warranty or additional liability. | ||||||
|  | 
 | ||||||
|  | END OF TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  | APPENDIX: How to apply the Apache License to your work | ||||||
|  | 
 | ||||||
|  | To apply the Apache License to your work, attach the following boilerplate | ||||||
|  | notice, with the fields enclosed by brackets "[]" replaced with your own | ||||||
|  | identifying information. (Don't include the brackets!) The text should be | ||||||
|  | enclosed in the appropriate comment syntax for the file format. We also | ||||||
|  | recommend that a file or class name and description of purpose be included on | ||||||
|  | the same "printed page" as the copyright notice for easier identification within | ||||||
|  | third-party archives. | ||||||
|  | 
 | ||||||
|  |    Copyright [yyyy] [name of copyright owner] | ||||||
|  | 
 | ||||||
|  |    Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |    you may not use this file except in compliance with the License. | ||||||
|  |    You may obtain a copy of the License at | ||||||
|  | 
 | ||||||
|  |      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | 
 | ||||||
|  |    Unless required by applicable law or agreed to in writing, software | ||||||
|  |    distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |    See the License for the specific language governing permissions and | ||||||
|  |    limitations under the License. | ||||||
							
								
								
									
										17
									
								
								vendor/gitea.com/go-chi/session/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/gitea.com/go-chi/session/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | # Session | ||||||
|  | 
 | ||||||
|  | Middleware session provides session management which copied from [Macaron Session](https://gitea.com/go-chi/session) for [go-chi](https://github.com/go-chi/chi). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase, Ledis and Nodb. | ||||||
|  | 
 | ||||||
|  | ## Installation | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | go get gitea.com/go-chi/session | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Credits | ||||||
|  | 
 | ||||||
|  | This package is a modified version of [go-macaron/session](github.com/go-macaron/session). | ||||||
|  | 
 | ||||||
|  | ## License | ||||||
|  | 
 | ||||||
|  | This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. | ||||||
							
								
								
									
										227
									
								
								vendor/gitea.com/go-chi/session/couchbase/couchbase.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								vendor/gitea.com/go-chi/session/couchbase/couchbase.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,227 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
|  | 	"github.com/couchbase/go-couchbase" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // CouchbaseSessionStore represents a couchbase session store implementation.
 | ||||||
|  | type CouchbaseSessionStore struct { | ||||||
|  | 	b           *couchbase.Bucket | ||||||
|  | 	sid         string | ||||||
|  | 	lock        sync.RWMutex | ||||||
|  | 	data        map[interface{}]interface{} | ||||||
|  | 	maxlifetime int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *CouchbaseSessionStore) Set(key, val interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = val | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *CouchbaseSessionStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete delete a key from session.
 | ||||||
|  | func (s *CouchbaseSessionStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *CouchbaseSessionStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases resource and save data to provider.
 | ||||||
|  | func (s *CouchbaseSessionStore) Release() error { | ||||||
|  | 	defer s.b.Close() | ||||||
|  | 
 | ||||||
|  | 	// Skip encoding if the data is empty
 | ||||||
|  | 	if len(s.data) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, err := session.EncodeGob(s.data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return s.b.Set(s.sid, int(s.maxlifetime), data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *CouchbaseSessionStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CouchbaseProvider represents a couchbase session provider implementation.
 | ||||||
|  | type CouchbaseProvider struct { | ||||||
|  | 	maxlifetime int64 | ||||||
|  | 	connStr     string | ||||||
|  | 	pool        string | ||||||
|  | 	bucket      string | ||||||
|  | 	b           *couchbase.Bucket | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (cp *CouchbaseProvider) getBucket() *couchbase.Bucket { | ||||||
|  | 	c, err := couchbase.Connect(cp.connStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	pool, err := c.GetPool(cp.pool) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bucket, err := pool.GetBucket(cp.bucket) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return bucket | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes memory session provider.
 | ||||||
|  | // connStr is couchbase server REST/JSON URL
 | ||||||
|  | // e.g. http://host:port/, Pool, Bucket
 | ||||||
|  | func (p *CouchbaseProvider) Init(maxlifetime int64, connStr string) error { | ||||||
|  | 	p.maxlifetime = maxlifetime | ||||||
|  | 	configs := strings.Split(connStr, ",") | ||||||
|  | 	if len(configs) > 0 { | ||||||
|  | 		p.connStr = configs[0] | ||||||
|  | 	} | ||||||
|  | 	if len(configs) > 1 { | ||||||
|  | 		p.pool = configs[1] | ||||||
|  | 	} | ||||||
|  | 	if len(configs) > 2 { | ||||||
|  | 		p.bucket = configs[2] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (p *CouchbaseProvider) Read(sid string) (session.RawStore, error) { | ||||||
|  | 	p.b = p.getBucket() | ||||||
|  | 
 | ||||||
|  | 	var doc []byte | ||||||
|  | 
 | ||||||
|  | 	err := p.b.Get(sid, &doc) | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	if doc == nil { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob(doc) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime} | ||||||
|  | 	return cs, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (p *CouchbaseProvider) Exist(sid string) bool { | ||||||
|  | 	p.b = p.getBucket() | ||||||
|  | 	defer p.b.Close() | ||||||
|  | 
 | ||||||
|  | 	var doc []byte | ||||||
|  | 
 | ||||||
|  | 	if err := p.b.Get(sid, &doc); err != nil || doc == nil { | ||||||
|  | 		return false | ||||||
|  | 	} else { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (p *CouchbaseProvider) Destroy(sid string) error { | ||||||
|  | 	p.b = p.getBucket() | ||||||
|  | 	defer p.b.Close() | ||||||
|  | 
 | ||||||
|  | 	p.b.Delete(sid) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (p *CouchbaseProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { | ||||||
|  | 	p.b = p.getBucket() | ||||||
|  | 
 | ||||||
|  | 	var doc []byte | ||||||
|  | 	if err := p.b.Get(oldsid, &doc); err != nil || doc == nil { | ||||||
|  | 		p.b.Set(sid, int(p.maxlifetime), "") | ||||||
|  | 	} else { | ||||||
|  | 		err := p.b.Delete(oldsid) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		_, _ = p.b.Add(sid, int(p.maxlifetime), doc) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err := p.b.Get(sid, &doc) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	if doc == nil { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob(doc) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime} | ||||||
|  | 	return cs, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (p *CouchbaseProvider) Count() int { | ||||||
|  | 	// FIXME
 | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (p *CouchbaseProvider) GC() {} | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	session.Register("couchbase", &CouchbaseProvider{}) | ||||||
|  | } | ||||||
							
								
								
									
										274
									
								
								vendor/gitea.com/go-chi/session/file.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								vendor/gitea.com/go-chi/session/file.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,274 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // FileStore represents a file session store implementation.
 | ||||||
|  | type FileStore struct { | ||||||
|  | 	p    *FileProvider | ||||||
|  | 	sid  string | ||||||
|  | 	lock sync.RWMutex | ||||||
|  | 	data map[interface{}]interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewFileStore creates and returns a file session store.
 | ||||||
|  | func NewFileStore(p *FileProvider, sid string, kv map[interface{}]interface{}) *FileStore { | ||||||
|  | 	return &FileStore{ | ||||||
|  | 		p:    p, | ||||||
|  | 		sid:  sid, | ||||||
|  | 		data: kv, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *FileStore) Set(key, val interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = val | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *FileStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete delete a key from session.
 | ||||||
|  | func (s *FileStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *FileStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases resource and save data to provider.
 | ||||||
|  | func (s *FileStore) Release() error { | ||||||
|  | 	s.p.lock.Lock() | ||||||
|  | 	defer s.p.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	// Skip encoding if the data is empty
 | ||||||
|  | 	if len(s.data) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, err := EncodeGob(s.data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ioutil.WriteFile(s.p.filepath(s.sid), data, 0600) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *FileStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FileProvider represents a file session provider implementation.
 | ||||||
|  | type FileProvider struct { | ||||||
|  | 	lock        sync.RWMutex | ||||||
|  | 	maxlifetime int64 | ||||||
|  | 	rootPath    string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes file session provider with given root path.
 | ||||||
|  | func (p *FileProvider) Init(maxlifetime int64, rootPath string) error { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	p.maxlifetime = maxlifetime | ||||||
|  | 	p.rootPath = rootPath | ||||||
|  | 	p.lock.Unlock() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *FileProvider) filepath(sid string) string { | ||||||
|  | 	return path.Join(p.rootPath, string(sid[0]), string(sid[1]), sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (p *FileProvider) Read(sid string) (_ RawStore, err error) { | ||||||
|  | 	filename := p.filepath(sid) | ||||||
|  | 	if err = os.MkdirAll(path.Dir(filename), 0700); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	p.lock.RLock() | ||||||
|  | 	defer p.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	var f *os.File | ||||||
|  | 	ok := false | ||||||
|  | 	if com.IsFile(filename) { | ||||||
|  | 		modTime, err := com.FileMTime(filename) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		ok = (modTime + p.maxlifetime) >= time.Now().Unix() | ||||||
|  | 	} | ||||||
|  | 	if ok { | ||||||
|  | 		f, err = os.OpenFile(filename, os.O_RDONLY, 0600) | ||||||
|  | 	} else { | ||||||
|  | 		f, err = os.Create(filename) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	defer f.Close() | ||||||
|  | 
 | ||||||
|  | 	if err = os.Chtimes(filename, time.Now(), time.Now()); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	data, err := ioutil.ReadAll(f) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(data) == 0 { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = DecodeGob(data) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return NewFileStore(p, sid, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (p *FileProvider) Exist(sid string) bool { | ||||||
|  | 	p.lock.RLock() | ||||||
|  | 	defer p.lock.RUnlock() | ||||||
|  | 	return com.IsFile(p.filepath(sid)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (p *FileProvider) Destroy(sid string) error { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	defer p.lock.Unlock() | ||||||
|  | 	return os.Remove(p.filepath(sid)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *FileProvider) regenerate(oldsid, sid string) (err error) { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	defer p.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	filename := p.filepath(sid) | ||||||
|  | 	if com.IsExist(filename) { | ||||||
|  | 		return fmt.Errorf("new sid '%s' already exists", sid) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	oldname := p.filepath(oldsid) | ||||||
|  | 	if !com.IsFile(oldname) { | ||||||
|  | 		data, err := EncodeGob(make(map[interface{}]interface{})) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if err = os.MkdirAll(path.Dir(oldname), 0700); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if err = ioutil.WriteFile(oldname, data, 0600); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = os.MkdirAll(path.Dir(filename), 0700); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err = os.Rename(oldname, filename); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (p *FileProvider) Regenerate(oldsid, sid string) (_ RawStore, err error) { | ||||||
|  | 	if err := p.regenerate(oldsid, sid); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return p.Read(sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (p *FileProvider) Count() int { | ||||||
|  | 	count := 0 | ||||||
|  | 	if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if !fi.IsDir() { | ||||||
|  | 			count++ | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil { | ||||||
|  | 		log.Printf("error counting session files: %v", err) | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	return count | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (p *FileProvider) GC() { | ||||||
|  | 	p.lock.RLock() | ||||||
|  | 	defer p.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	if !com.IsExist(p.rootPath) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if !fi.IsDir() && | ||||||
|  | 			(fi.ModTime().Unix()+p.maxlifetime) < time.Now().Unix() { | ||||||
|  | 			return os.Remove(path) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil { | ||||||
|  | 		log.Printf("error garbage collecting session files: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	Register("file", &FileProvider{}) | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								vendor/gitea.com/go-chi/session/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/gitea.com/go-chi/session/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | module gitea.com/go-chi/session | ||||||
|  | 
 | ||||||
|  | go 1.11 | ||||||
|  | 
 | ||||||
|  | require ( | ||||||
|  | 	github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 | ||||||
|  | 	github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 | ||||||
|  | 	github.com/couchbase/gomemcached v0.1.1 // indirect | ||||||
|  | 	github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 // indirect | ||||||
|  | 	github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 // indirect | ||||||
|  | 	github.com/edsrzf/mmap-go v1.0.0 // indirect | ||||||
|  | 	github.com/go-chi/chi v1.5.1 | ||||||
|  | 	github.com/go-redis/redis/v8 v8.4.0 | ||||||
|  | 	github.com/go-sql-driver/mysql v1.4.1 | ||||||
|  | 	github.com/lib/pq v1.2.0 | ||||||
|  | 	github.com/pelletier/go-toml v1.8.1 // indirect | ||||||
|  | 	github.com/pkg/errors v0.9.1 // indirect | ||||||
|  | 	github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect | ||||||
|  | 	github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92 | ||||||
|  | 	github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d // indirect | ||||||
|  | 	github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 | ||||||
|  | 	github.com/syndtr/goleveldb v1.0.0 // indirect | ||||||
|  | 	github.com/unknwon/com v1.0.1 | ||||||
|  | 	google.golang.org/appengine v1.6.7 // indirect | ||||||
|  | 	gopkg.in/ini.v1 v1.62.0 | ||||||
|  | ) | ||||||
							
								
								
									
										151
									
								
								vendor/gitea.com/go-chi/session/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								vendor/gitea.com/go-chi/session/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,151 @@ | ||||||
|  | github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK+hNk4tyD+nuGjpVLPEHuJSFXMw11/HPA= | ||||||
|  | github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= | ||||||
|  | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= | ||||||
|  | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||||
|  | github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 h1:uNLXQ6QO1TocD8BaN/KkRki0Xw0brCM1PKl/ZA5pgfs= | ||||||
|  | github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A= | ||||||
|  | github.com/couchbase/gomemcached v0.1.1 h1:xCS8ZglJDhrlQg3jmK7Rn1V8f7bPjXABLC05CgLQauc= | ||||||
|  | github.com/couchbase/gomemcached v0.1.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo= | ||||||
|  | github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 h1:NCqJ6fwen6YP0WlV/IyibaT0kPt3JEI1rA62V/UPKT4= | ||||||
|  | github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | ||||||
|  | github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 h1:Lgdd/Qp96Qj8jqLpq2cI1I1X7BJnu06efS+XkhRoLUQ= | ||||||
|  | github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= | ||||||
|  | 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/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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= | ||||||
|  | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= | ||||||
|  | github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= | ||||||
|  | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | ||||||
|  | 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.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= | ||||||
|  | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | ||||||
|  | github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w= | ||||||
|  | github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= | ||||||
|  | github.com/go-redis/redis/v8 v8.4.0 h1:J5NCReIgh3QgUJu398hUncxDExN4gMOHI11NVbVicGQ= | ||||||
|  | github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= | ||||||
|  | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= | ||||||
|  | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | ||||||
|  | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
|  | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= | ||||||
|  | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
|  | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||||
|  | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||||
|  | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||||
|  | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||||
|  | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||||
|  | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= | ||||||
|  | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||||
|  | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= | ||||||
|  | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||||
|  | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||||
|  | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||||
|  | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
|  | github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= | ||||||
|  | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
|  | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||||
|  | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= | ||||||
|  | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||||
|  | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= | ||||||
|  | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | ||||||
|  | github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||||
|  | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= | ||||||
|  | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||||
|  | github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= | ||||||
|  | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | ||||||
|  | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= | ||||||
|  | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | ||||||
|  | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | ||||||
|  | github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= | ||||||
|  | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | ||||||
|  | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= | ||||||
|  | github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= | ||||||
|  | github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= | ||||||
|  | github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= | ||||||
|  | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | ||||||
|  | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= | ||||||
|  | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | ||||||
|  | github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= | ||||||
|  | github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= | ||||||
|  | github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= | ||||||
|  | github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= | ||||||
|  | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||||
|  | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||||
|  | 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/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= | ||||||
|  | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= | ||||||
|  | github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92 h1:qvsJwGToa8rxb42cDRhkbKeX2H5N8BH+s2aUikGt8mI= | ||||||
|  | github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= | ||||||
|  | github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs= | ||||||
|  | github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= | ||||||
|  | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||||
|  | github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= | ||||||
|  | github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||||
|  | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= | ||||||
|  | github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= | ||||||
|  | github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= | ||||||
|  | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
|  | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= | ||||||
|  | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
|  | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= | ||||||
|  | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= | ||||||
|  | github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= | ||||||
|  | github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= | ||||||
|  | go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ= | ||||||
|  | go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw= | ||||||
|  | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
|  | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= | ||||||
|  | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
|  | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
|  | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
|  | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
|  | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||||
|  | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
|  | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= | ||||||
|  | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||||
|  | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
|  | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
|  | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= | ||||||
|  | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
|  | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | ||||||
|  | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||||
|  | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= | ||||||
|  | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||||
|  | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||||
|  | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||||
|  | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||||||
|  | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|  | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= | ||||||
|  | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||||
|  | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||||
|  | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||||
|  | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||||
|  | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||||
|  | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||||
|  | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= | ||||||
|  | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
|  | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||||
|  | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|  | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | ||||||
|  | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||||
|  | gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= | ||||||
|  | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||||
|  | 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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= | ||||||
|  | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||||||
|  | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
							
								
								
									
										203
									
								
								vendor/gitea.com/go-chi/session/memcache/memcache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								vendor/gitea.com/go-chi/session/memcache/memcache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,203 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
|  | 	"github.com/bradfitz/gomemcache/memcache" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // MemcacheStore represents a memcache session store implementation.
 | ||||||
|  | type MemcacheStore struct { | ||||||
|  | 	c      *memcache.Client | ||||||
|  | 	sid    string | ||||||
|  | 	expire int32 | ||||||
|  | 	lock   sync.RWMutex | ||||||
|  | 	data   map[interface{}]interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewMemcacheStore creates and returns a memcache session store.
 | ||||||
|  | func NewMemcacheStore(c *memcache.Client, sid string, expire int32, kv map[interface{}]interface{}) *MemcacheStore { | ||||||
|  | 	return &MemcacheStore{ | ||||||
|  | 		c:      c, | ||||||
|  | 		sid:    sid, | ||||||
|  | 		expire: expire, | ||||||
|  | 		data:   kv, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewItem(sid string, data []byte, expire int32) *memcache.Item { | ||||||
|  | 	return &memcache.Item{ | ||||||
|  | 		Key:        sid, | ||||||
|  | 		Value:      data, | ||||||
|  | 		Expiration: expire, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *MemcacheStore) Set(key, val interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = val | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *MemcacheStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete delete a key from session.
 | ||||||
|  | func (s *MemcacheStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *MemcacheStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases resource and save data to provider.
 | ||||||
|  | func (s *MemcacheStore) Release() error { | ||||||
|  | 	// Skip encoding if the data is empty
 | ||||||
|  | 	if len(s.data) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, err := session.EncodeGob(s.data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return s.c.Set(NewItem(s.sid, data, s.expire)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *MemcacheStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MemcacheProvider represents a memcache session provider implementation.
 | ||||||
|  | type MemcacheProvider struct { | ||||||
|  | 	c      *memcache.Client | ||||||
|  | 	expire int32 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes memcache session provider.
 | ||||||
|  | // connStrs: 127.0.0.1:9090;127.0.0.1:9091
 | ||||||
|  | func (p *MemcacheProvider) Init(expire int64, connStrs string) error { | ||||||
|  | 	p.expire = int32(expire) | ||||||
|  | 	p.c = memcache.New(strings.Split(connStrs, ";")...) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (p *MemcacheProvider) Read(sid string) (session.RawStore, error) { | ||||||
|  | 	if !p.Exist(sid) { | ||||||
|  | 		if err := p.c.Set(NewItem(sid, []byte(""), p.expire)); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	item, err := p.c.Get(sid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(item.Value) == 0 { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob(item.Value) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewMemcacheStore(p.c, sid, p.expire, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (p *MemcacheProvider) Exist(sid string) bool { | ||||||
|  | 	_, err := p.c.Get(sid) | ||||||
|  | 	return err == nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (p *MemcacheProvider) Destroy(sid string) error { | ||||||
|  | 	return p.c.Delete(sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (p *MemcacheProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { | ||||||
|  | 	if p.Exist(sid) { | ||||||
|  | 		return nil, fmt.Errorf("new sid '%s' already exists", sid) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	item := NewItem(sid, []byte(""), p.expire) | ||||||
|  | 	if p.Exist(oldsid) { | ||||||
|  | 		item, err = p.c.Get(oldsid) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} else if err = p.c.Delete(oldsid); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		item.Key = sid | ||||||
|  | 	} | ||||||
|  | 	if err = p.c.Set(item); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	if len(item.Value) == 0 { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob(item.Value) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewMemcacheStore(p.c, sid, p.expire, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (p *MemcacheProvider) Count() int { | ||||||
|  | 	// FIXME: how come this library does not have Stats method?
 | ||||||
|  | 	return -1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (p *MemcacheProvider) GC() {} | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	session.Register("memcache", &MemcacheProvider{}) | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								vendor/gitea.com/go-chi/session/memcache/memcache.goconvey
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/gitea.com/go-chi/session/memcache/memcache.goconvey
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | ignore | ||||||
							
								
								
									
										223
									
								
								vendor/gitea.com/go-chi/session/memory.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								vendor/gitea.com/go-chi/session/memory.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,223 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"container/list" | ||||||
|  | 	"fmt" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // MemStore represents a in-memory session store implementation.
 | ||||||
|  | type MemStore struct { | ||||||
|  | 	sid        string | ||||||
|  | 	lock       sync.RWMutex | ||||||
|  | 	data       map[interface{}]interface{} | ||||||
|  | 	lastAccess time.Time | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewMemStore creates and returns a memory session store.
 | ||||||
|  | func NewMemStore(sid string) *MemStore { | ||||||
|  | 	return &MemStore{ | ||||||
|  | 		sid:        sid, | ||||||
|  | 		data:       make(map[interface{}]interface{}), | ||||||
|  | 		lastAccess: time.Now(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *MemStore) Set(key, val interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = val | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *MemStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete deletes a key from session.
 | ||||||
|  | func (s *MemStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *MemStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases resource and save data to provider.
 | ||||||
|  | func (*MemStore) Release() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *MemStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MemProvider represents a in-memory session provider implementation.
 | ||||||
|  | type MemProvider struct { | ||||||
|  | 	lock        sync.RWMutex | ||||||
|  | 	maxLifetime int64 | ||||||
|  | 	data        map[string]*list.Element | ||||||
|  | 	// A priority list whose lastAccess newer gets higer priority.
 | ||||||
|  | 	list *list.List | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes memory session provider.
 | ||||||
|  | func (p *MemProvider) Init(maxLifetime int64, _ string) error { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	p.list = list.New() | ||||||
|  | 	p.data = make(map[string]*list.Element) | ||||||
|  | 	p.maxLifetime = maxLifetime | ||||||
|  | 	p.lock.Unlock() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // update expands time of session store by given ID.
 | ||||||
|  | func (p *MemProvider) update(sid string) error { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	defer p.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	if e, ok := p.data[sid]; ok { | ||||||
|  | 		e.Value.(*MemStore).lastAccess = time.Now() | ||||||
|  | 		p.list.MoveToFront(e) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (p *MemProvider) Read(sid string) (_ RawStore, err error) { | ||||||
|  | 	p.lock.RLock() | ||||||
|  | 	e, ok := p.data[sid] | ||||||
|  | 	p.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	// Only restore if the session is still alive.
 | ||||||
|  | 	if ok && (e.Value.(*MemStore).lastAccess.Unix()+p.maxLifetime) >= time.Now().Unix() { | ||||||
|  | 		if err = p.update(sid); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		return e.Value.(*MemStore), nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Create a new session.
 | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	defer p.lock.Unlock() | ||||||
|  | 	if ok { | ||||||
|  | 		p.list.Remove(e) | ||||||
|  | 		delete(p.data, sid) | ||||||
|  | 	} | ||||||
|  | 	s := NewMemStore(sid) | ||||||
|  | 	p.data[sid] = p.list.PushBack(s) | ||||||
|  | 	return s, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (p *MemProvider) Exist(sid string) bool { | ||||||
|  | 	p.lock.RLock() | ||||||
|  | 	defer p.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	_, ok := p.data[sid] | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (p *MemProvider) Destroy(sid string) error { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	defer p.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	e, ok := p.data[sid] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.list.Remove(e) | ||||||
|  | 	delete(p.data, sid) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) { | ||||||
|  | 	if p.Exist(sid) { | ||||||
|  | 		return nil, fmt.Errorf("new sid '%s' already exists", sid) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	s, err := p.Read(oldsid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = p.Destroy(oldsid); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	s.(*MemStore).sid = sid | ||||||
|  | 
 | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	defer p.lock.Unlock() | ||||||
|  | 	p.data[sid] = p.list.PushBack(s) | ||||||
|  | 	return s, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (p *MemProvider) Count() int { | ||||||
|  | 	return p.list.Len() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (p *MemProvider) GC() { | ||||||
|  | 	p.lock.RLock() | ||||||
|  | 	for { | ||||||
|  | 		// No session in the list.
 | ||||||
|  | 		e := p.list.Back() | ||||||
|  | 		if e == nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { | ||||||
|  | 			p.lock.RUnlock() | ||||||
|  | 			p.lock.Lock() | ||||||
|  | 			p.list.Remove(e) | ||||||
|  | 			delete(p.data, e.Value.(*MemStore).sid) | ||||||
|  | 			p.lock.Unlock() | ||||||
|  | 			p.lock.RLock() | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	p.lock.RUnlock() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	Register("memory", &MemProvider{}) | ||||||
|  | } | ||||||
							
								
								
									
										201
									
								
								vendor/gitea.com/go-chi/session/mysql/mysql.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/gitea.com/go-chi/session/mysql/mysql.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,201 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"database/sql" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
|  | 	_ "github.com/go-sql-driver/mysql" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // MysqlStore represents a mysql session store implementation.
 | ||||||
|  | type MysqlStore struct { | ||||||
|  | 	c    *sql.DB | ||||||
|  | 	sid  string | ||||||
|  | 	lock sync.RWMutex | ||||||
|  | 	data map[interface{}]interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewMysqlStore creates and returns a mysql session store.
 | ||||||
|  | func NewMysqlStore(c *sql.DB, sid string, kv map[interface{}]interface{}) *MysqlStore { | ||||||
|  | 	return &MysqlStore{ | ||||||
|  | 		c:    c, | ||||||
|  | 		sid:  sid, | ||||||
|  | 		data: kv, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *MysqlStore) Set(key, val interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = val | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *MysqlStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete delete a key from session.
 | ||||||
|  | func (s *MysqlStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *MysqlStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases resource and save data to provider.
 | ||||||
|  | func (s *MysqlStore) Release() error { | ||||||
|  | 	// Skip encoding if the data is empty
 | ||||||
|  | 	if len(s.data) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, err := session.EncodeGob(s.data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, err = s.c.Exec("UPDATE session SET data=?, expiry=? WHERE `key`=?", | ||||||
|  | 		data, time.Now().Unix(), s.sid) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *MysqlStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MysqlProvider represents a mysql session provider implementation.
 | ||||||
|  | type MysqlProvider struct { | ||||||
|  | 	c      *sql.DB | ||||||
|  | 	expire int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes mysql session provider.
 | ||||||
|  | // connStr: username:password@protocol(address)/dbname?param=value
 | ||||||
|  | func (p *MysqlProvider) Init(expire int64, connStr string) (err error) { | ||||||
|  | 	p.expire = expire | ||||||
|  | 
 | ||||||
|  | 	p.c, err = sql.Open("mysql", connStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return p.c.Ping() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (p *MysqlProvider) Read(sid string) (session.RawStore, error) { | ||||||
|  | 	now := time.Now().Unix() | ||||||
|  | 	var data []byte | ||||||
|  | 	expiry := now | ||||||
|  | 	err := p.c.QueryRow("SELECT data, expiry FROM session WHERE `key`=?", sid).Scan(&data, &expiry) | ||||||
|  | 	if err == sql.ErrNoRows { | ||||||
|  | 		_, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", | ||||||
|  | 			sid, "", now) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	if len(data) == 0 || expiry+p.expire <= now { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob(data) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewMysqlStore(p.c, sid, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (p *MysqlProvider) Exist(sid string) bool { | ||||||
|  | 	var data []byte | ||||||
|  | 	err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data) | ||||||
|  | 	if err != nil && err != sql.ErrNoRows { | ||||||
|  | 		panic("session/mysql: error checking existence: " + err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return err != sql.ErrNoRows | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (p *MysqlProvider) Destroy(sid string) error { | ||||||
|  | 	_, err := p.c.Exec("DELETE FROM session WHERE `key`=?", sid) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (p *MysqlProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { | ||||||
|  | 	if p.Exist(sid) { | ||||||
|  | 		return nil, fmt.Errorf("new sid '%s' already exists", sid) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !p.Exist(oldsid) { | ||||||
|  | 		if _, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", | ||||||
|  | 			oldsid, "", time.Now().Unix()); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err = p.c.Exec("UPDATE session SET `key`=? WHERE `key`=?", sid, oldsid); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return p.Read(sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (p *MysqlProvider) Count() (total int) { | ||||||
|  | 	if err := p.c.QueryRow("SELECT COUNT(*) AS NUM FROM session").Scan(&total); err != nil { | ||||||
|  | 		panic("session/mysql: error counting records: " + err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return total | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (p *MysqlProvider) GC() { | ||||||
|  | 	if _, err := p.c.Exec("DELETE FROM session WHERE  expiry + ? <= UNIX_TIMESTAMP(NOW())", p.expire); err != nil { | ||||||
|  | 		log.Printf("session/mysql: error garbage collecting: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	session.Register("mysql", &MysqlProvider{}) | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								vendor/gitea.com/go-chi/session/mysql/mysql.goconvey
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/gitea.com/go-chi/session/mysql/mysql.goconvey
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | ignore | ||||||
							
								
								
									
										202
									
								
								vendor/gitea.com/go-chi/session/postgres/postgres.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/gitea.com/go-chi/session/postgres/postgres.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,202 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"database/sql" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
|  | 	_ "github.com/lib/pq" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // PostgresStore represents a postgres session store implementation.
 | ||||||
|  | type PostgresStore struct { | ||||||
|  | 	c    *sql.DB | ||||||
|  | 	sid  string | ||||||
|  | 	lock sync.RWMutex | ||||||
|  | 	data map[interface{}]interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewPostgresStore creates and returns a postgres session store.
 | ||||||
|  | func NewPostgresStore(c *sql.DB, sid string, kv map[interface{}]interface{}) *PostgresStore { | ||||||
|  | 	return &PostgresStore{ | ||||||
|  | 		c:    c, | ||||||
|  | 		sid:  sid, | ||||||
|  | 		data: kv, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets value to given key in session.
 | ||||||
|  | func (s *PostgresStore) Set(key, value interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data[key] = value | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets value by given key in session.
 | ||||||
|  | func (s *PostgresStore) Get(key interface{}) interface{} { | ||||||
|  | 	s.lock.RLock() | ||||||
|  | 	defer s.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	return s.data[key] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Delete delete a key from session.
 | ||||||
|  | func (s *PostgresStore) Delete(key interface{}) error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	delete(s.data, key) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ID returns current session ID.
 | ||||||
|  | func (s *PostgresStore) ID() string { | ||||||
|  | 	return s.sid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // save postgres session values to database.
 | ||||||
|  | // must call this method to save values to database.
 | ||||||
|  | func (s *PostgresStore) Release() error { | ||||||
|  | 	// Skip encoding if the data is empty
 | ||||||
|  | 	if len(s.data) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, err := session.EncodeGob(s.data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, err = s.c.Exec("UPDATE session SET data=$1, expiry=$2 WHERE key=$3", | ||||||
|  | 		data, time.Now().Unix(), s.sid) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush deletes all session data.
 | ||||||
|  | func (s *PostgresStore) Flush() error { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	s.data = make(map[interface{}]interface{}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // PostgresProvider represents a postgres session provider implementation.
 | ||||||
|  | type PostgresProvider struct { | ||||||
|  | 	c           *sql.DB | ||||||
|  | 	maxlifetime int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init initializes postgres session provider.
 | ||||||
|  | // connStr: user=a password=b host=localhost port=5432 dbname=c sslmode=disable
 | ||||||
|  | func (p *PostgresProvider) Init(maxlifetime int64, connStr string) (err error) { | ||||||
|  | 	p.maxlifetime = maxlifetime | ||||||
|  | 
 | ||||||
|  | 	p.c, err = sql.Open("postgres", connStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return p.c.Ping() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (p *PostgresProvider) Read(sid string) (session.RawStore, error) { | ||||||
|  | 	now := time.Now().Unix() | ||||||
|  | 	var data []byte | ||||||
|  | 	expiry := now | ||||||
|  | 	err := p.c.QueryRow("SELECT data, expiry FROM session WHERE key=$1", sid).Scan(&data, &expiry) | ||||||
|  | 	if err == sql.ErrNoRows { | ||||||
|  | 		_, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", | ||||||
|  | 			sid, "", now) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var kv map[interface{}]interface{} | ||||||
|  | 	if len(data) == 0 || expiry+p.maxlifetime <= now { | ||||||
|  | 		kv = make(map[interface{}]interface{}) | ||||||
|  | 	} else { | ||||||
|  | 		kv, err = session.DecodeGob(data) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewPostgresStore(p.c, sid, kv), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exist returns true if session with given ID exists.
 | ||||||
|  | func (p *PostgresProvider) Exist(sid string) bool { | ||||||
|  | 	var data []byte | ||||||
|  | 	err := p.c.QueryRow("SELECT data FROM session WHERE key=$1", sid).Scan(&data) | ||||||
|  | 	if err != nil && err != sql.ErrNoRows { | ||||||
|  | 		panic("session/postgres: error checking existence: " + err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return err != sql.ErrNoRows | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by session ID.
 | ||||||
|  | func (p *PostgresProvider) Destroy(sid string) error { | ||||||
|  | 	_, err := p.c.Exec("DELETE FROM session WHERE key=$1", sid) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | func (p *PostgresProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { | ||||||
|  | 	if p.Exist(sid) { | ||||||
|  | 		return nil, fmt.Errorf("new sid '%s' already exists", sid) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !p.Exist(oldsid) { | ||||||
|  | 		if _, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", | ||||||
|  | 			oldsid, "", time.Now().Unix()); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err = p.c.Exec("UPDATE session SET key=$1 WHERE key=$2", sid, oldsid); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return p.Read(sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (p *PostgresProvider) Count() (total int) { | ||||||
|  | 	if err := p.c.QueryRow("SELECT COUNT(*) AS NUM FROM session").Scan(&total); err != nil { | ||||||
|  | 		panic("session/postgres: error counting records: " + err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return total | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC calls GC to clean expired sessions.
 | ||||||
|  | func (p *PostgresProvider) GC() { | ||||||
|  | 	if _, err := p.c.Exec("DELETE FROM session WHERE EXTRACT(EPOCH FROM NOW()) - expiry > $1", p.maxlifetime); err != nil { | ||||||
|  | 		log.Printf("session/postgres: error garbage collecting: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	session.Register("postgres", &PostgresProvider{}) | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								vendor/gitea.com/go-chi/session/postgres/postgres.goconvey
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/gitea.com/go-chi/session/postgres/postgres.goconvey
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | ignore | ||||||
							
								
								
									
										100
									
								
								vendor/gitea.com/go-chi/session/secret.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								vendor/gitea.com/go-chi/session/secret.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,100 @@ | ||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"crypto/aes" | ||||||
|  | 	"crypto/cipher" | ||||||
|  | 	"crypto/rand" | ||||||
|  | 	"crypto/sha256" | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"errors" | ||||||
|  | 	"io" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // NewSecret creates a new secret
 | ||||||
|  | func NewSecret() (string, error) { | ||||||
|  | 	return NewSecretWithLength(32) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewSecretWithLength creates a new secret for a given length
 | ||||||
|  | func NewSecretWithLength(length int64) (string, error) { | ||||||
|  | 	return randomString(length) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func randomBytes(len int64) ([]byte, error) { | ||||||
|  | 	b := make([]byte, len) | ||||||
|  | 	if _, err := rand.Read(b); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return b, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func randomString(len int64) (string, error) { | ||||||
|  | 	b, err := randomBytes(len) | ||||||
|  | 	return base64.URLEncoding.EncodeToString(b), err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AesEncrypt encrypts text and given key with AES.
 | ||||||
|  | func AesEncrypt(key, text []byte) ([]byte, error) { | ||||||
|  | 	block, err := aes.NewCipher(key) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	b := base64.StdEncoding.EncodeToString(text) | ||||||
|  | 	ciphertext := make([]byte, aes.BlockSize+len(b)) | ||||||
|  | 	iv := ciphertext[:aes.BlockSize] | ||||||
|  | 	if _, err := io.ReadFull(rand.Reader, iv); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	cfb := cipher.NewCFBEncrypter(block, iv) | ||||||
|  | 	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b)) | ||||||
|  | 	return ciphertext, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AesDecrypt decrypts text and given key with AES.
 | ||||||
|  | func AesDecrypt(key, text []byte) ([]byte, error) { | ||||||
|  | 	block, err := aes.NewCipher(key) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(text) < aes.BlockSize { | ||||||
|  | 		return nil, errors.New("ciphertext too short") | ||||||
|  | 	} | ||||||
|  | 	iv := text[:aes.BlockSize] | ||||||
|  | 	text = text[aes.BlockSize:] | ||||||
|  | 	cfb := cipher.NewCFBDecrypter(block, iv) | ||||||
|  | 	cfb.XORKeyStream(text, text) | ||||||
|  | 	data, err := base64.StdEncoding.DecodeString(string(text)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return data, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // EncryptSecret encrypts a string with given key into a hex string
 | ||||||
|  | func EncryptSecret(key string, str string) (string, error) { | ||||||
|  | 	keyHash := sha256.Sum256([]byte(key)) | ||||||
|  | 	plaintext := []byte(str) | ||||||
|  | 	ciphertext, err := AesEncrypt(keyHash[:], plaintext) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return base64.StdEncoding.EncodeToString(ciphertext), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DecryptSecret decrypts a previously encrypted hex string
 | ||||||
|  | func DecryptSecret(key string, cipherhex string) (string, error) { | ||||||
|  | 	keyHash := sha256.Sum256([]byte(key)) | ||||||
|  | 	ciphertext, err := base64.StdEncoding.DecodeString(cipherhex) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	plaintext, err := AesDecrypt(keyHash[:], ciphertext) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return string(plaintext), nil | ||||||
|  | } | ||||||
							
								
								
									
										466
									
								
								vendor/gitea.com/go-chi/session/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										466
									
								
								vendor/gitea.com/go-chi/session/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,466 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | // Package session a middleware that provides the session management of Macaron.
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const version = "0.7.0" | ||||||
|  | 
 | ||||||
|  | // Version returns the version
 | ||||||
|  | func Version() string { | ||||||
|  | 	return version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // RawStore is the interface that operates the session data.
 | ||||||
|  | type RawStore interface { | ||||||
|  | 	// Set sets value to given key in session.
 | ||||||
|  | 	Set(interface{}, interface{}) error | ||||||
|  | 	// Get gets value by given key in session.
 | ||||||
|  | 	Get(interface{}) interface{} | ||||||
|  | 	// Delete deletes a key from session.
 | ||||||
|  | 	Delete(interface{}) error | ||||||
|  | 	// ID returns current session ID.
 | ||||||
|  | 	ID() string | ||||||
|  | 	// Release releases session resource and save data to provider.
 | ||||||
|  | 	Release() error | ||||||
|  | 	// Flush deletes all session data.
 | ||||||
|  | 	Flush() error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Store is the interface that contains all data for one session process with specific ID.
 | ||||||
|  | type Store interface { | ||||||
|  | 	RawStore | ||||||
|  | 	// Read returns raw session store by session ID.
 | ||||||
|  | 	Read(string) (RawStore, error) | ||||||
|  | 	// Destroy deletes a session.
 | ||||||
|  | 	Destroy(http.ResponseWriter, *http.Request) error | ||||||
|  | 	// RegenerateID regenerates a session store from old session ID to new one.
 | ||||||
|  | 	RegenerateID(http.ResponseWriter, *http.Request) (RawStore, error) | ||||||
|  | 	// Count counts and returns number of sessions.
 | ||||||
|  | 	Count() int | ||||||
|  | 	// GC calls GC to clean expired sessions.
 | ||||||
|  | 	GC() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type store struct { | ||||||
|  | 	RawStore | ||||||
|  | 	*Manager | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var _ Store = &store{} | ||||||
|  | 
 | ||||||
|  | // Options represents a struct for specifying configuration options for the session middleware.
 | ||||||
|  | type Options struct { | ||||||
|  | 	// Name of provider. Default is "memory".
 | ||||||
|  | 	Provider string | ||||||
|  | 	// Provider configuration, it's corresponding to provider.
 | ||||||
|  | 	ProviderConfig string | ||||||
|  | 	// Cookie name to save session ID. Default is "MacaronSession".
 | ||||||
|  | 	CookieName string | ||||||
|  | 	// Cookie path to store. Default is "/".
 | ||||||
|  | 	CookiePath string | ||||||
|  | 	// GC interval time in seconds. Default is 3600.
 | ||||||
|  | 	Gclifetime int64 | ||||||
|  | 	// Max life time in seconds. Default is whatever GC interval time is.
 | ||||||
|  | 	Maxlifetime int64 | ||||||
|  | 	// Use HTTPS only. Default is false.
 | ||||||
|  | 	Secure bool | ||||||
|  | 	// Cookie life time. Default is 0.
 | ||||||
|  | 	CookieLifeTime int | ||||||
|  | 	// SameSite set the cookie SameSite
 | ||||||
|  | 	SameSite http.SameSite | ||||||
|  | 	// Cookie domain name. Default is empty.
 | ||||||
|  | 	Domain string | ||||||
|  | 	// Session ID length. Default is 16.
 | ||||||
|  | 	IDLength int | ||||||
|  | 	// Ignore release for websocket. Default is false.
 | ||||||
|  | 	IgnoreReleaseForWebSocket bool | ||||||
|  | 	// FlashEncryptionKey sets the encryption key for flash messages
 | ||||||
|  | 	FlashEncryptionKey string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func prepareOptions(options []Options) Options { | ||||||
|  | 	var opt Options | ||||||
|  | 	if len(options) > 0 { | ||||||
|  | 		opt = options[0] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(opt.Provider) == 0 { | ||||||
|  | 		opt.Provider = "memory" | ||||||
|  | 	} | ||||||
|  | 	if len(opt.ProviderConfig) == 0 { | ||||||
|  | 		opt.ProviderConfig = "data/sessions" | ||||||
|  | 	} | ||||||
|  | 	if len(opt.CookieName) == 0 { | ||||||
|  | 		opt.CookieName = "MacaronSession" | ||||||
|  | 	} | ||||||
|  | 	if len(opt.CookiePath) == 0 { | ||||||
|  | 		opt.CookiePath = "/" | ||||||
|  | 	} | ||||||
|  | 	if opt.Gclifetime == 0 { | ||||||
|  | 		opt.Gclifetime = 3600 | ||||||
|  | 	} | ||||||
|  | 	if opt.Maxlifetime == 0 { | ||||||
|  | 		opt.Maxlifetime = opt.Gclifetime | ||||||
|  | 	} | ||||||
|  | 	if !opt.Secure { | ||||||
|  | 		opt.Secure = false | ||||||
|  | 	} | ||||||
|  | 	if opt.IDLength == 0 { | ||||||
|  | 		opt.IDLength = 16 | ||||||
|  | 	} | ||||||
|  | 	if len(opt.FlashEncryptionKey) == 0 { | ||||||
|  | 		opt.FlashEncryptionKey = "" | ||||||
|  | 	} | ||||||
|  | 	if len(opt.FlashEncryptionKey) == 0 { | ||||||
|  | 		opt.FlashEncryptionKey, _ = NewSecret() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return opt | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetCookie returns given cookie value from request header.
 | ||||||
|  | func GetCookie(req *http.Request, name string) string { | ||||||
|  | 	cookie, err := req.Cookie(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	val, _ := url.QueryUnescape(cookie.Value) | ||||||
|  | 	return val | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewCookie creates cookie via given params and value.
 | ||||||
|  | // FIXME: IE support? http://golanghome.com/post/620#reply2
 | ||||||
|  | func NewCookie(name string, value string, others ...interface{}) *http.Cookie { | ||||||
|  | 	cookie := http.Cookie{} | ||||||
|  | 	cookie.Name = name | ||||||
|  | 	cookie.Value = url.QueryEscape(value) | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 0 { | ||||||
|  | 		switch v := others[0].(type) { | ||||||
|  | 		case int: | ||||||
|  | 			cookie.MaxAge = v | ||||||
|  | 		case int64: | ||||||
|  | 			cookie.MaxAge = int(v) | ||||||
|  | 		case int32: | ||||||
|  | 			cookie.MaxAge = int(v) | ||||||
|  | 		case func(*http.Cookie): | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cookie.Path = "/" | ||||||
|  | 	if len(others) > 1 { | ||||||
|  | 		if v, ok := others[1].(string); ok && len(v) > 0 { | ||||||
|  | 			cookie.Path = v | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 2 { | ||||||
|  | 		if v, ok := others[2].(string); ok && len(v) > 0 { | ||||||
|  | 			cookie.Domain = v | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 3 { | ||||||
|  | 		switch v := others[3].(type) { | ||||||
|  | 		case bool: | ||||||
|  | 			cookie.Secure = v | ||||||
|  | 		case func(*http.Cookie): | ||||||
|  | 			v(&cookie) | ||||||
|  | 		default: | ||||||
|  | 			if others[3] != nil { | ||||||
|  | 				cookie.Secure = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 4 { | ||||||
|  | 		if v, ok := others[4].(bool); ok && v { | ||||||
|  | 			cookie.HttpOnly = true | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 5 { | ||||||
|  | 		if v, ok := others[5].(time.Time); ok { | ||||||
|  | 			cookie.Expires = v | ||||||
|  | 			cookie.RawExpires = v.Format(time.UnixDate) | ||||||
|  | 		} else if v, ok := others[1].(func(*http.Cookie)); ok { | ||||||
|  | 			v(&cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(others) > 6 { | ||||||
|  | 		for _, other := range others[6:] { | ||||||
|  | 			if v, ok := other.(func(*http.Cookie)); ok { | ||||||
|  | 				v(&cookie) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return &cookie | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Sessioner is a middleware that maps a session.SessionStore service into the Macaron handler chain.
 | ||||||
|  | // An single variadic session.Options struct can be optionally provided to configure.
 | ||||||
|  | func Sessioner(options ...Options) func(next http.Handler) http.Handler { | ||||||
|  | 	opt := prepareOptions(options) | ||||||
|  | 	manager, err := NewManager(opt.Provider, opt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	go manager.startGC() | ||||||
|  | 
 | ||||||
|  | 	return func(next http.Handler) http.Handler { | ||||||
|  | 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
|  | 			sess, err := manager.Start(w, req) | ||||||
|  | 			if err != nil { | ||||||
|  | 				panic("session(start): " + err.Error()) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			var s = store{ | ||||||
|  | 				RawStore: sess, | ||||||
|  | 				Manager:  manager, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			req = req.WithContext(context.WithValue(req.Context(), interface{}("Session"), &s)) | ||||||
|  | 
 | ||||||
|  | 			next.ServeHTTP(w, req) | ||||||
|  | 
 | ||||||
|  | 			if manager.opt.IgnoreReleaseForWebSocket && req.Header.Get("Upgrade") == "websocket" { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if err = sess.Release(); err != nil { | ||||||
|  | 				panic("session(release): " + err.Error()) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetSession returns session store
 | ||||||
|  | func GetSession(req *http.Request) Store { | ||||||
|  | 	sessCtx := req.Context().Value("Session") | ||||||
|  | 	sess, _ := sessCtx.(*store) | ||||||
|  | 	return sess | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Provider is the interface that provides session manipulations.
 | ||||||
|  | type Provider interface { | ||||||
|  | 	// Init initializes session provider.
 | ||||||
|  | 	Init(gclifetime int64, config string) error | ||||||
|  | 	// Read returns raw session store by session ID.
 | ||||||
|  | 	Read(sid string) (RawStore, error) | ||||||
|  | 	// Exist returns true if session with given ID exists.
 | ||||||
|  | 	Exist(sid string) bool | ||||||
|  | 	// Destroy deletes a session by session ID.
 | ||||||
|  | 	Destroy(sid string) error | ||||||
|  | 	// Regenerate regenerates a session store from old session ID to new one.
 | ||||||
|  | 	Regenerate(oldsid, sid string) (RawStore, error) | ||||||
|  | 	// Count counts and returns number of sessions.
 | ||||||
|  | 	Count() int | ||||||
|  | 	// GC calls GC to clean expired sessions.
 | ||||||
|  | 	GC() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var providers = make(map[string]Provider) | ||||||
|  | 
 | ||||||
|  | // Register registers a provider.
 | ||||||
|  | func Register(name string, provider Provider) { | ||||||
|  | 	if provider == nil { | ||||||
|  | 		panic("session: cannot register provider with nil value") | ||||||
|  | 	} | ||||||
|  | 	if _, dup := providers[name]; dup { | ||||||
|  | 		panic(fmt.Errorf("session: cannot register provider '%s' twice", name)) | ||||||
|  | 	} | ||||||
|  | 	providers[name] = provider | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //    _____
 | ||||||
|  | //   /     \ _____    ____ _____     ____   ___________
 | ||||||
|  | //  /  \ /  \\__  \  /    \\__  \   / ___\_/ __ \_  __ \
 | ||||||
|  | // /    Y    \/ __ \|   |  \/ __ \_/ /_/  >  ___/|  | \/
 | ||||||
|  | // \____|__  (____  /___|  (____  /\___  / \___  >__|
 | ||||||
|  | //         \/     \/     \/     \//_____/      \/
 | ||||||
|  | 
 | ||||||
|  | // Manager represents a struct that contains session provider and its configuration.
 | ||||||
|  | type Manager struct { | ||||||
|  | 	provider Provider | ||||||
|  | 	opt      Options | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewManager creates and returns a new session manager by given provider name and configuration.
 | ||||||
|  | // It panics when given provider isn't registered.
 | ||||||
|  | func NewManager(name string, opt Options) (*Manager, error) { | ||||||
|  | 	p, ok := providers[name] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, fmt.Errorf("session: unknown provider '%s'(forgotten import?)", name) | ||||||
|  | 	} | ||||||
|  | 	return &Manager{p, opt}, p.Init(opt.Maxlifetime, opt.ProviderConfig) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // sessionID generates a new session ID with rand string, unix nano time, remote addr by hash function.
 | ||||||
|  | func (m *Manager) sessionID() string { | ||||||
|  | 	return hex.EncodeToString(generateRandomKey(m.opt.IDLength / 2)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // validSessionID tests whether a provided session ID is a valid session ID.
 | ||||||
|  | func (m *Manager) validSessionID(sid string) (bool, error) { | ||||||
|  | 	if len(sid) != m.opt.IDLength { | ||||||
|  | 		return false, fmt.Errorf("invalid 'sid': %s %d != %d", sid, len(sid), m.opt.IDLength) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i := range sid { | ||||||
|  | 		switch { | ||||||
|  | 		case '0' <= sid[i] && sid[i] <= '9': | ||||||
|  | 		case 'a' <= sid[i] && sid[i] <= 'f': | ||||||
|  | 		default: | ||||||
|  | 			return false, errors.New("invalid 'sid': " + sid) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Start starts a session by generating new one
 | ||||||
|  | // or retrieve existence one by reading session ID from HTTP request if it's valid.
 | ||||||
|  | func (m *Manager) Start(resp http.ResponseWriter, req *http.Request) (RawStore, error) { | ||||||
|  | 	sid := GetCookie(req, m.opt.CookieName) | ||||||
|  | 	valid, _ := m.validSessionID(sid) | ||||||
|  | 	if len(sid) > 0 && valid && m.provider.Exist(sid) { | ||||||
|  | 		return m.provider.Read(sid) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sid = m.sessionID() | ||||||
|  | 	sess, err := m.provider.Read(sid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cookie := &http.Cookie{ | ||||||
|  | 		Name:     m.opt.CookieName, | ||||||
|  | 		Value:    sid, | ||||||
|  | 		Path:     m.opt.CookiePath, | ||||||
|  | 		HttpOnly: true, | ||||||
|  | 		Secure:   m.opt.Secure, | ||||||
|  | 		Domain:   m.opt.Domain, | ||||||
|  | 		SameSite: m.opt.SameSite, | ||||||
|  | 	} | ||||||
|  | 	if m.opt.CookieLifeTime >= 0 { | ||||||
|  | 		cookie.MaxAge = m.opt.CookieLifeTime | ||||||
|  | 	} | ||||||
|  | 	http.SetCookie(resp, cookie) | ||||||
|  | 	req.AddCookie(cookie) | ||||||
|  | 	return sess, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read returns raw session store by session ID.
 | ||||||
|  | func (m *Manager) Read(sid string) (RawStore, error) { | ||||||
|  | 	// Ensure we're trying to read a valid session ID
 | ||||||
|  | 	if _, err := m.validSessionID(sid); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return m.provider.Read(sid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Destroy deletes a session by given ID.
 | ||||||
|  | func (m *Manager) Destroy(resp http.ResponseWriter, req *http.Request) error { | ||||||
|  | 	sid := GetCookie(req, m.opt.CookieName) | ||||||
|  | 	if len(sid) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err := m.validSessionID(sid); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := m.provider.Destroy(sid); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	cookie := &http.Cookie{ | ||||||
|  | 		Name:     m.opt.CookieName, | ||||||
|  | 		Path:     m.opt.CookiePath, | ||||||
|  | 		HttpOnly: true, | ||||||
|  | 		Expires:  time.Now(), | ||||||
|  | 		MaxAge:   -1, | ||||||
|  | 	} | ||||||
|  | 	http.SetCookie(resp, cookie) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // RegenerateID regenerates a session store from old session ID to new one.
 | ||||||
|  | func (m *Manager) RegenerateID(resp http.ResponseWriter, req *http.Request) (sess RawStore, err error) { | ||||||
|  | 	sid := m.sessionID() | ||||||
|  | 	oldsid := GetCookie(req, m.opt.CookieName) | ||||||
|  | 	_, err = m.validSessionID(oldsid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	sess, err = m.provider.Regenerate(oldsid, sid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	cookie := &http.Cookie{ | ||||||
|  | 		Name:     m.opt.CookieName, | ||||||
|  | 		Value:    sid, | ||||||
|  | 		Path:     m.opt.CookiePath, | ||||||
|  | 		HttpOnly: true, | ||||||
|  | 		Secure:   m.opt.Secure, | ||||||
|  | 		Domain:   m.opt.Domain, | ||||||
|  | 		SameSite: m.opt.SameSite, | ||||||
|  | 	} | ||||||
|  | 	if m.opt.CookieLifeTime >= 0 { | ||||||
|  | 		cookie.MaxAge = m.opt.CookieLifeTime | ||||||
|  | 	} | ||||||
|  | 	http.SetCookie(resp, cookie) | ||||||
|  | 	req.AddCookie(cookie) | ||||||
|  | 	return sess, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count counts and returns number of sessions.
 | ||||||
|  | func (m *Manager) Count() int { | ||||||
|  | 	return m.provider.Count() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GC starts GC job in a certain period.
 | ||||||
|  | func (m *Manager) GC() { | ||||||
|  | 	m.provider.GC() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // startGC starts GC job in a certain period.
 | ||||||
|  | func (m *Manager) startGC() { | ||||||
|  | 	m.GC() | ||||||
|  | 	time.AfterFunc(time.Duration(m.opt.Gclifetime)*time.Second, func() { m.startGC() }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetSecure indicates whether to set cookie with HTTPS or not.
 | ||||||
|  | func (m *Manager) SetSecure(secure bool) { | ||||||
|  | 	m.opt.Secure = secure | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								vendor/gitea.com/go-chi/session/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/gitea.com/go-chi/session/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | ||||||
|  | // Copyright 2013 Beego Authors
 | ||||||
|  | // Copyright 2014 The Macaron Authors
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"): you may
 | ||||||
|  | // not use this file except in compliance with the License. You may obtain
 | ||||||
|  | // a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | ||||||
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | ||||||
|  | // License for the specific language governing permissions and limitations
 | ||||||
|  | // under the License.
 | ||||||
|  | 
 | ||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"crypto/rand" | ||||||
|  | 	"encoding/gob" | ||||||
|  | 	"io" | ||||||
|  | 
 | ||||||
|  | 	"github.com/unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	gob.Register([]interface{}{}) | ||||||
|  | 	gob.Register(map[int]interface{}{}) | ||||||
|  | 	gob.Register(map[string]interface{}{}) | ||||||
|  | 	gob.Register(map[interface{}]interface{}{}) | ||||||
|  | 	gob.Register(map[string]string{}) | ||||||
|  | 	gob.Register(map[int]string{}) | ||||||
|  | 	gob.Register(map[int]int{}) | ||||||
|  | 	gob.Register(map[int]int64{}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // EncodeGob encodes obj with gob
 | ||||||
|  | func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) { | ||||||
|  | 	for _, v := range obj { | ||||||
|  | 		gob.Register(v) | ||||||
|  | 	} | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	err := gob.NewEncoder(buf).Encode(obj) | ||||||
|  | 	return buf.Bytes(), err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DecodeGob decodes bytes to obj
 | ||||||
|  | func DecodeGob(encoded []byte) (out map[interface{}]interface{}, err error) { | ||||||
|  | 	buf := bytes.NewBuffer(encoded) | ||||||
|  | 	err = gob.NewDecoder(buf).Decode(&out) | ||||||
|  | 	return out, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NOTE: A local copy in case of underlying package change
 | ||||||
|  | var alphanum = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") | ||||||
|  | 
 | ||||||
|  | // generateRandomKey creates a random key with the given strength.
 | ||||||
|  | func generateRandomKey(strength int) []byte { | ||||||
|  | 	k := make([]byte, strength) | ||||||
|  | 	if n, err := io.ReadFull(rand.Reader, k); n != strength || err != nil { | ||||||
|  | 		return com.RandomCreateBytes(strength, alphanum...) | ||||||
|  | 	} | ||||||
|  | 	return k | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								vendor/github.com/couchbase/gomemcached/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/couchbase/gomemcached/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -4,3 +4,4 @@ | ||||||
| *.swp | *.swp | ||||||
| /gocache/gocache | /gocache/gocache | ||||||
| c.out | c.out | ||||||
|  | .idea | ||||||
							
								
								
									
										25
									
								
								vendor/github.com/couchbase/gomemcached/client/collections_filter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/couchbase/gomemcached/client/collections_filter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -17,11 +17,7 @@ type CollectionsFilter struct { | ||||||
| 	ScopeId         uint32 | 	ScopeId         uint32 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type nonStreamIdNonResumeScopeMeta struct { | type nonStreamIdNonCollectionsMeta struct { | ||||||
| 	ScopeId string `json:"scope"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type nonStreamIdResumeScopeMeta struct { |  | ||||||
| 	ManifestId string `json:"uid"` | 	ManifestId string `json:"uid"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -29,7 +25,7 @@ type nonStreamIdNonResumeCollectionsMeta struct { | ||||||
| 	CollectionsList []string `json:"collections"` | 	CollectionsList []string `json:"collections"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type nonStreamIdResumeCollectionsMeta struct { | type nonStreamIdCollectionsMeta struct { | ||||||
| 	ManifestId      string   `json:"uid"` | 	ManifestId      string   `json:"uid"` | ||||||
| 	CollectionsList []string `json:"collections"` | 	CollectionsList []string `json:"collections"` | ||||||
| } | } | ||||||
|  | @ -99,10 +95,19 @@ func (c *CollectionsFilter) ToStreamReqBody() ([]byte, error) { | ||||||
| 	case false: | 	case false: | ||||||
| 		switch c.UseManifestUid { | 		switch c.UseManifestUid { | ||||||
| 		case true: | 		case true: | ||||||
| 			filter := &nonStreamIdResumeScopeMeta{ | 			switch len(c.CollectionsList) > 0 { | ||||||
| 				ManifestId: fmt.Sprintf("%x", c.ManifestUid), | 			case true: | ||||||
|  | 				filter := &nonStreamIdCollectionsMeta{ | ||||||
|  | 					ManifestId:      fmt.Sprintf("%x", c.ManifestUid), | ||||||
|  | 					CollectionsList: c.outputCollectionsFilterColList(), | ||||||
|  | 				} | ||||||
|  | 				output = *filter | ||||||
|  | 			case false: | ||||||
|  | 				filter := &nonStreamIdNonCollectionsMeta{ | ||||||
|  | 					ManifestId: fmt.Sprintf("%x", c.ManifestUid), | ||||||
|  | 				} | ||||||
|  | 				output = *filter | ||||||
| 			} | 			} | ||||||
| 			output = *filter |  | ||||||
| 		case false: | 		case false: | ||||||
| 			switch len(c.CollectionsList) > 0 { | 			switch len(c.CollectionsList) > 0 { | ||||||
| 			case true: | 			case true: | ||||||
|  | @ -111,7 +116,7 @@ func (c *CollectionsFilter) ToStreamReqBody() ([]byte, error) { | ||||||
| 				} | 				} | ||||||
| 				output = *filter | 				output = *filter | ||||||
| 			case false: | 			case false: | ||||||
| 				output = nonStreamIdNonResumeScopeMeta{ScopeId: c.outputScopeId()} | 				return nil, fmt.Errorf("Specifying scopeID must require the use of streamId") | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								vendor/github.com/couchbase/gomemcached/client/mc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/couchbase/gomemcached/client/mc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -375,6 +375,25 @@ func (c *Client) setCollection(req *gomemcached.MCRequest, context ...*ClientCon | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Sets collection info in extras
 | ||||||
|  | func (c *Client) setExtrasCollection(req *gomemcached.MCRequest, context ...*ClientContext) error { | ||||||
|  | 	collectionId := uint32(0) | ||||||
|  | 	if len(context) > 0 { | ||||||
|  | 		collectionId = context[0].CollId | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// if the optional collection is specified, it must be default for clients that haven't turned on collections
 | ||||||
|  | 	if atomic.LoadUint32(&c.collectionsEnabled) == 0 { | ||||||
|  | 		if collectionId != 0 { | ||||||
|  | 			return fmt.Errorf("Client does not use collections but a collection was specified") | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		req.Extras = make([]byte, 4) | ||||||
|  | 		binary.BigEndian.PutUint32(req.Extras, collectionId) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (c *Client) setVbSeqnoContext(req *gomemcached.MCRequest, context ...*ClientContext) error { | func (c *Client) setVbSeqnoContext(req *gomemcached.MCRequest, context ...*ClientContext) error { | ||||||
| 	if len(context) == 0 || req == nil { | 	if len(context) == 0 || req == nil { | ||||||
| 		return nil | 		return nil | ||||||
|  | @ -516,9 +535,14 @@ func (c *Client) Del(vb uint16, key string, context ...*ClientContext) (*gomemca | ||||||
| 
 | 
 | ||||||
| // Get a random document
 | // Get a random document
 | ||||||
| func (c *Client) GetRandomDoc(context ...*ClientContext) (*gomemcached.MCResponse, error) { | func (c *Client) GetRandomDoc(context ...*ClientContext) (*gomemcached.MCResponse, error) { | ||||||
| 	return c.Send(&gomemcached.MCRequest{ | 	req := &gomemcached.MCRequest{ | ||||||
| 		Opcode: 0xB6, | 		Opcode: 0xB6, | ||||||
| 	}) | 	} | ||||||
|  | 	err := c.setExtrasCollection(req, context...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return c.Send(req) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AuthList lists SASL auth mechanisms.
 | // AuthList lists SASL auth mechanisms.
 | ||||||
|  |  | ||||||
							
								
								
									
										54
									
								
								vendor/github.com/couchbase/gomemcached/client/upr_event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/couchbase/gomemcached/client/upr_event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -83,7 +83,8 @@ type UprEvent struct { | ||||||
| 	SystemEvent     SystemEventType         // Only valid if IsSystemEvent() is true
 | 	SystemEvent     SystemEventType         // Only valid if IsSystemEvent() is true
 | ||||||
| 	SysEventVersion uint8                   // Based on the version, the way Extra bytes is parsed is different
 | 	SysEventVersion uint8                   // Based on the version, the way Extra bytes is parsed is different
 | ||||||
| 	ValueLen        int                     // Cache it to avoid len() calls for performance
 | 	ValueLen        int                     // Cache it to avoid len() calls for performance
 | ||||||
| 	CollectionId    uint64                  // Valid if Collection is in use
 | 	CollectionId    uint32                  // Valid if Collection is in use
 | ||||||
|  | 	StreamId        *uint16                 // Nil if not in use
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // FailoverLog containing vvuid and sequnce number
 | // FailoverLog containing vvuid and sequnce number
 | ||||||
|  | @ -103,7 +104,7 @@ func makeUprEvent(rq gomemcached.MCRequest, stream *UprStream, bytesReceivedFrom | ||||||
| 		DataType:     rq.DataType, | 		DataType:     rq.DataType, | ||||||
| 		ValueLen:     len(rq.Body), | 		ValueLen:     len(rq.Body), | ||||||
| 		SystemEvent:  InvalidSysEvent, | 		SystemEvent:  InvalidSysEvent, | ||||||
| 		CollectionId: math.MaxUint64, | 		CollectionId: math.MaxUint32, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	event.PopulateFieldsBasedOnStreamType(rq, stream.StreamType) | 	event.PopulateFieldsBasedOnStreamType(rq, stream.StreamType) | ||||||
|  | @ -153,6 +154,8 @@ func makeUprEvent(rq gomemcached.MCRequest, stream *UprStream, bytesReceivedFrom | ||||||
| 		event.PopulateEvent(rq.Extras) | 		event.PopulateEvent(rq.Extras) | ||||||
| 	} else if event.IsSeqnoAdv() { | 	} else if event.IsSeqnoAdv() { | ||||||
| 		event.PopulateSeqnoAdv(rq.Extras) | 		event.PopulateSeqnoAdv(rq.Extras) | ||||||
|  | 	} else if event.IsOsoSnapshot() { | ||||||
|  | 		event.PopulateOso(rq.Extras) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return event | 	return event | ||||||
|  | @ -160,6 +163,15 @@ func makeUprEvent(rq gomemcached.MCRequest, stream *UprStream, bytesReceivedFrom | ||||||
| 
 | 
 | ||||||
| func (event *UprEvent) PopulateFieldsBasedOnStreamType(rq gomemcached.MCRequest, streamType DcpStreamType) { | func (event *UprEvent) PopulateFieldsBasedOnStreamType(rq gomemcached.MCRequest, streamType DcpStreamType) { | ||||||
| 	switch streamType { | 	switch streamType { | ||||||
|  | 	case CollectionsStreamId: | ||||||
|  | 		for _, extra := range rq.FramingExtras { | ||||||
|  | 			streamId, streamIdErr := extra.GetStreamId() | ||||||
|  | 			if streamIdErr == nil { | ||||||
|  | 				event.StreamId = &streamId | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// After parsing streamID, still need to populate regular collectionID
 | ||||||
|  | 		fallthrough | ||||||
| 	case CollectionsNonStreamId: | 	case CollectionsNonStreamId: | ||||||
| 		switch rq.Opcode { | 		switch rq.Opcode { | ||||||
| 		// Only these will have CID encoded within the key
 | 		// Only these will have CID encoded within the key
 | ||||||
|  | @ -167,15 +179,12 @@ func (event *UprEvent) PopulateFieldsBasedOnStreamType(rq gomemcached.MCRequest, | ||||||
| 			gomemcached.UPR_DELETION, | 			gomemcached.UPR_DELETION, | ||||||
| 			gomemcached.UPR_EXPIRATION: | 			gomemcached.UPR_EXPIRATION: | ||||||
| 			uleb128 := Uleb128(rq.Key) | 			uleb128 := Uleb128(rq.Key) | ||||||
| 			result, bytesShifted := uleb128.ToUint64(rq.Keylen) | 			result, bytesShifted := uleb128.ToUint32(rq.Keylen) | ||||||
| 			event.CollectionId = result | 			event.CollectionId = result | ||||||
| 			event.Key = rq.Key[bytesShifted:] | 			event.Key = rq.Key[bytesShifted:] | ||||||
| 		default: | 		default: | ||||||
| 			event.Key = rq.Key | 			event.Key = rq.Key | ||||||
| 		} | 		} | ||||||
| 	case CollectionsStreamId: |  | ||||||
| 		// TODO - not implemented
 |  | ||||||
| 		fallthrough |  | ||||||
| 	case NonCollectionStream: | 	case NonCollectionStream: | ||||||
| 		// Let default behavior be legacy stream type
 | 		// Let default behavior be legacy stream type
 | ||||||
| 		fallthrough | 		fallthrough | ||||||
|  | @ -208,6 +217,10 @@ func (event *UprEvent) IsSeqnoAdv() bool { | ||||||
| 	return event.Opcode == gomemcached.DCP_SEQNO_ADV | 	return event.Opcode == gomemcached.DCP_SEQNO_ADV | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (event *UprEvent) IsOsoSnapshot() bool { | ||||||
|  | 	return event.Opcode == gomemcached.DCP_OSO_SNAPSHOT | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (event *UprEvent) PopulateEvent(extras []byte) { | func (event *UprEvent) PopulateEvent(extras []byte) { | ||||||
| 	if len(extras) < dcpSystemEventExtraLen { | 	if len(extras) < dcpSystemEventExtraLen { | ||||||
| 		// Wrong length, don't parse
 | 		// Wrong length, don't parse
 | ||||||
|  | @ -229,6 +242,14 @@ func (event *UprEvent) PopulateSeqnoAdv(extras []byte) { | ||||||
| 	event.Seqno = binary.BigEndian.Uint64(extras[:8]) | 	event.Seqno = binary.BigEndian.Uint64(extras[:8]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (event *UprEvent) PopulateOso(extras []byte) { | ||||||
|  | 	if len(extras) < dcpOsoExtraLen { | ||||||
|  | 		// Wrong length, don't parse
 | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	event.Flags = binary.BigEndian.Uint32(extras[:4]) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (event *UprEvent) GetSystemEventName() (string, error) { | func (event *UprEvent) GetSystemEventName() (string, error) { | ||||||
| 	switch event.SystemEvent { | 	switch event.SystemEvent { | ||||||
| 	case CollectionCreate: | 	case CollectionCreate: | ||||||
|  | @ -345,15 +366,32 @@ func (event *UprEvent) GetMaxTTL() (uint32, error) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Only if error is nil:
 | ||||||
|  | // Returns true if event states oso begins
 | ||||||
|  | // Return false if event states oso ends
 | ||||||
|  | func (event *UprEvent) GetOsoBegin() (bool, error) { | ||||||
|  | 	if !event.IsOsoSnapshot() { | ||||||
|  | 		return false, ErrorInvalidOp | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if event.Flags == 1 { | ||||||
|  | 		return true, nil | ||||||
|  | 	} else if event.Flags == 2 { | ||||||
|  | 		return false, nil | ||||||
|  | 	} else { | ||||||
|  | 		return false, ErrorInvalidOp | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type Uleb128 []byte | type Uleb128 []byte | ||||||
| 
 | 
 | ||||||
| func (u Uleb128) ToUint64(cachedLen int) (result uint64, bytesShifted int) { | func (u Uleb128) ToUint32(cachedLen int) (result uint32, bytesShifted int) { | ||||||
| 	var shift uint = 0 | 	var shift uint = 0 | ||||||
| 
 | 
 | ||||||
| 	for curByte := 0; curByte < cachedLen; curByte++ { | 	for curByte := 0; curByte < cachedLen; curByte++ { | ||||||
| 		oneByte := u[curByte] | 		oneByte := u[curByte] | ||||||
| 		last7Bits := 0x7f & oneByte | 		last7Bits := 0x7f & oneByte | ||||||
| 		result |= uint64(last7Bits) << shift | 		result |= uint32(last7Bits) << shift | ||||||
| 		bytesShifted++ | 		bytesShifted++ | ||||||
| 		if oneByte&0x80 == 0 { | 		if oneByte&0x80 == 0 { | ||||||
| 			break | 			break | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/couchbase/gomemcached/client/upr_feed.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/couchbase/gomemcached/client/upr_feed.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -26,6 +26,7 @@ const opaqueOpen = 0xBEAF0001 | ||||||
| const opaqueFailover = 0xDEADBEEF | const opaqueFailover = 0xDEADBEEF | ||||||
| const opaqueGetSeqno = 0xDEADBEEF | const opaqueGetSeqno = 0xDEADBEEF | ||||||
| const uprDefaultNoopInterval = 120 | const uprDefaultNoopInterval = 120 | ||||||
|  | const dcpOsoExtraLen = 4 | ||||||
| 
 | 
 | ||||||
| // Counter on top of opaqueOpen that others can draw from for open and control msgs
 | // Counter on top of opaqueOpen that others can draw from for open and control msgs
 | ||||||
| var opaqueOpenCtrlWell uint32 = opaqueOpen | var opaqueOpenCtrlWell uint32 = opaqueOpen | ||||||
|  | @ -117,6 +118,7 @@ type UprFeatures struct { | ||||||
| 	DcpPriority         PriorityType | 	DcpPriority         PriorityType | ||||||
| 	EnableExpiry        bool | 	EnableExpiry        bool | ||||||
| 	EnableStreamId      bool | 	EnableStreamId      bool | ||||||
|  | 	EnableOso           bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -601,6 +603,20 @@ func (feed *UprFeed) uprOpen(name string, sequence uint32, bufSize uint32, featu | ||||||
| 		activatedFeatures.EnableStreamId = true | 		activatedFeatures.EnableStreamId = true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if features.EnableOso { | ||||||
|  | 		rq := &gomemcached.MCRequest{ | ||||||
|  | 			Opcode: gomemcached.UPR_CONTROL, | ||||||
|  | 			Key:    []byte("enable_out_of_order_snapshots"), | ||||||
|  | 			Body:   []byte("true"), | ||||||
|  | 			Opaque: getUprOpenCtrlOpaque(), | ||||||
|  | 		} | ||||||
|  | 		err = sendMcRequestSync(feed.conn, rq) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		activatedFeatures.EnableOso = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// everything is ok so far, set upr feed to open state
 | 	// everything is ok so far, set upr feed to open state
 | ||||||
| 	feed.activatedFeatures = activatedFeatures | 	feed.activatedFeatures = activatedFeatures | ||||||
| 	feed.setOpen() | 	feed.setOpen() | ||||||
|  | @ -976,6 +992,12 @@ loop: | ||||||
| 						break loop | 						break loop | ||||||
| 					} | 					} | ||||||
| 					event = makeUprEvent(pkt, stream, bytes) | 					event = makeUprEvent(pkt, stream, bytes) | ||||||
|  | 				case gomemcached.DCP_OSO_SNAPSHOT: | ||||||
|  | 					if stream == nil { | ||||||
|  | 						logging.Infof("Stream not found for vb %d: %#v", vb, pkt) | ||||||
|  | 						break loop | ||||||
|  | 					} | ||||||
|  | 					event = makeUprEvent(pkt, stream, bytes) | ||||||
| 				default: | 				default: | ||||||
| 					logging.Infof("Recived an unknown response for vbucket %d", vb) | 					logging.Infof("Recived an unknown response for vbucket %d", vb) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								vendor/github.com/couchbase/gomemcached/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/couchbase/gomemcached/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | module github.com/couchbase/gomemcached | ||||||
|  | 
 | ||||||
|  | go 1.13 | ||||||
							
								
								
									
										9
									
								
								vendor/github.com/couchbase/gomemcached/mc_constants.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/couchbase/gomemcached/mc_constants.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -104,6 +104,7 @@ const ( | ||||||
| 
 | 
 | ||||||
| 	DCP_SYSTEM_EVENT = CommandCode(0x5f) // A system event has occurred
 | 	DCP_SYSTEM_EVENT = CommandCode(0x5f) // A system event has occurred
 | ||||||
| 	DCP_SEQNO_ADV    = CommandCode(0x64) // Sent when the vb seqno has advanced due to an unsubscribed event
 | 	DCP_SEQNO_ADV    = CommandCode(0x64) // Sent when the vb seqno has advanced due to an unsubscribed event
 | ||||||
|  | 	DCP_OSO_SNAPSHOT = CommandCode(0x65) // Marks the begin and end of out-of-sequence-number stream
 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // command codes that are counted toward DCP control buffer
 | // command codes that are counted toward DCP control buffer
 | ||||||
|  | @ -117,6 +118,7 @@ var BufferedCommandCodeMap = map[CommandCode]bool{ | ||||||
| 	UPR_EXPIRATION:   true, | 	UPR_EXPIRATION:   true, | ||||||
| 	DCP_SYSTEM_EVENT: true, | 	DCP_SYSTEM_EVENT: true, | ||||||
| 	DCP_SEQNO_ADV:    true, | 	DCP_SEQNO_ADV:    true, | ||||||
|  | 	DCP_OSO_SNAPSHOT: true, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Status field for memcached response.
 | // Status field for memcached response.
 | ||||||
|  | @ -156,6 +158,9 @@ const ( | ||||||
| 	SUBDOC_PATH_NOT_FOUND             = Status(0xc0) | 	SUBDOC_PATH_NOT_FOUND             = Status(0xc0) | ||||||
| 	SUBDOC_BAD_MULTI                  = Status(0xcc) | 	SUBDOC_BAD_MULTI                  = Status(0xcc) | ||||||
| 	SUBDOC_MULTI_PATH_FAILURE_DELETED = Status(0xd3) | 	SUBDOC_MULTI_PATH_FAILURE_DELETED = Status(0xd3) | ||||||
|  | 
 | ||||||
|  | 	// Not a Memcached status
 | ||||||
|  | 	UNKNOWN_STATUS = Status(0xffff) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // for log redaction
 | // for log redaction
 | ||||||
|  | @ -174,6 +179,10 @@ var isFatal = map[Status]bool{ | ||||||
| 	EACCESS:       true, | 	EACCESS:       true, | ||||||
| 	ENOMEM:        true, | 	ENOMEM:        true, | ||||||
| 	NOT_SUPPORTED: true, | 	NOT_SUPPORTED: true, | ||||||
|  | 
 | ||||||
|  | 	// consider statuses coming from outside couchbase (eg OS errors) as fatal for the connection
 | ||||||
|  | 	// as there might be unread data left over on the wire
 | ||||||
|  | 	UNKNOWN_STATUS: true, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // the producer/consumer bit in dcp flags
 | // the producer/consumer bit in dcp flags
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								vendor/github.com/couchbase/gomemcached/mc_res.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/couchbase/gomemcached/mc_res.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -38,7 +38,7 @@ func (res *MCResponse) Error() string { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func errStatus(e error) Status { | func errStatus(e error) Status { | ||||||
| 	status := Status(0xffff) | 	status := UNKNOWN_STATUS | ||||||
| 	if res, ok := e.(*MCResponse); ok { | 	if res, ok := e.(*MCResponse); ok { | ||||||
| 		status = res.Status | 		status = res.Status | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								vendor/github.com/go-chi/chi/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/go-chi/chi/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,20 +0,0 @@ | ||||||
| language: go |  | ||||||
| 
 |  | ||||||
| go: |  | ||||||
|   - 1.10.x |  | ||||||
|   - 1.11.x |  | ||||||
|   - 1.12.x |  | ||||||
|   - 1.13.x |  | ||||||
|   - 1.14.x |  | ||||||
| 
 |  | ||||||
| script: |  | ||||||
|   - go get -d -t ./... |  | ||||||
|   - go vet ./... |  | ||||||
|   - go test ./... |  | ||||||
|   - > |  | ||||||
|     go_version=$(go version); |  | ||||||
|     if [ ${go_version:13:4} = "1.12" ]; then |  | ||||||
|       go get -u golang.org/x/tools/cmd/goimports; |  | ||||||
|       goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :; |  | ||||||
|     fi |  | ||||||
| 
 |  | ||||||
							
								
								
									
										62
									
								
								vendor/github.com/go-chi/chi/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/go-chi/chi/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,5 +1,66 @@ | ||||||
| # Changelog | # Changelog | ||||||
| 
 | 
 | ||||||
|  | ## v1.5.1 (2020-12-06) | ||||||
|  | 
 | ||||||
|  | - Performance improvement: removing 1 allocation by foregoing context.WithValue, thank you @bouk for | ||||||
|  |   your contribution (https://github.com/go-chi/chi/pull/555). Note: new benchmarks posted in README. | ||||||
|  | - `middleware.CleanPath`: new middleware that clean's request path of double slashes | ||||||
|  | - deprecate & remove `chi.ServerBaseContext` in favour of stdlib `http.Server#BaseContext` | ||||||
|  | - plus other tiny improvements, see full commit history below | ||||||
|  | - History of changes: see https://github.com/go-chi/chi/compare/v4.1.2...v1.5.1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## v1.5.0 (2020-11-12) - now with go.mod support | ||||||
|  | 
 | ||||||
|  | `chi` dates back to 2016 with it's original implementation as one of the first routers to adopt the newly introduced | ||||||
|  | context.Context api to the stdlib -- set out to design a router that is faster, more modular and simpler than anything | ||||||
|  | else out there -- while not introducing any custom handler types or dependencies. Today, `chi` still has zero dependencies, | ||||||
|  | and in many ways is future proofed from changes, given it's minimal nature. Between versions, chi's iterations have been very | ||||||
|  | incremental, with the architecture and api being the same today as it was originally designed in 2016. For this reason it  | ||||||
|  | makes chi a pretty easy project to maintain, as well thanks to the many amazing community contributions over the years | ||||||
|  | to who all help make chi better (total of 86 contributors to date -- thanks all!). | ||||||
|  | 
 | ||||||
|  | Chi has been an labour of love, art and engineering, with the goals to offer beautiful ergonomics, flexibility, performance | ||||||
|  | and simplicity when building HTTP services with Go. I've strived to keep the router very minimal in surface area / code size, | ||||||
|  | and always improving the code wherever possible -- and as of today the `chi` package is just 1082 lines of code (not counting | ||||||
|  | middlewares, which are all optional). As well, I don't have the exact metrics, but from my analysis and email exchanges from | ||||||
|  | companies and developers, chi is used by thousands of projects around the world -- thank you all as there is no better form of | ||||||
|  | joy for me than to have art I had started be helpful and enjoyed by others. And of course I use chi in all of my own projects too :) | ||||||
|  | 
 | ||||||
|  | For me, the asthetics of chi's code and usage are very important. With the introduction of Go's module support  | ||||||
|  | (which I'm a big fan of), chi's past versioning scheme choice to v2, v3 and v4 would mean I'd require the import path | ||||||
|  | of "github.com/go-chi/chi/v4", leading to the lengthy discussion at https://github.com/go-chi/chi/issues/462. | ||||||
|  | Haha, to some, you may be scratching your head why I've spent > 1 year stalling to adopt "/vXX" convention in the import | ||||||
|  | path -- which isn't horrible in general -- but for chi, I'm unable to accept it as I strive for perfection in it's API design, | ||||||
|  | aesthetics and simplicity. It just doesn't feel good to me given chi's simple nature -- I do not foresee a "v5" or "v6", | ||||||
|  | and upgrading between versions in the future will also be just incremental. | ||||||
|  | 
 | ||||||
|  | I do understand versioning is a part of the API design as well, which is why the solution for a while has been to "do nothing", | ||||||
|  | as Go supports both old and new import paths with/out go.mod. However, now that Go module support has had time to iron out kinks and | ||||||
|  | is adopted everywhere, it's time for chi to get with the times. Luckily, I've discovered a path forward that will make me happy, | ||||||
|  | while also not breaking anyone's app who adopted a prior versioning from tags in v2/v3/v4. I've made an experimental release of | ||||||
|  | v1.5.0 with go.mod silently, and tested it with new and old projects, to ensure the developer experience is preserved, and it's | ||||||
|  | largely unnoticed. Fortunately, Go's toolchain will check the tags of a repo and consider the "latest" tag the one with go.mod. | ||||||
|  | However, you can still request a specific older tag such as v4.1.2, and everything will "just work". But new users can just | ||||||
|  | `go get github.com/go-chi/chi` or `go get github.com/go-chi/chi@latest` and they will get the latest version which contains | ||||||
|  | go.mod support, which is v1.5.0+. `chi` will not change very much over the years, just like it hasn't changed much from 4 years ago. | ||||||
|  | Therefore, we will stay on v1.x from here on, starting from v1.5.0. Any breaking changes will bump a "minor" release and | ||||||
|  | backwards-compatible improvements/fixes will bump a "tiny" release. | ||||||
|  | 
 | ||||||
|  | For existing projects who want to upgrade to the latest go.mod version, run: `go get -u github.com/go-chi/chi@v1.5.0`, | ||||||
|  | which will get you on the go.mod version line (as Go's mod cache may still remember v4.x). Brand new systems can run | ||||||
|  | `go get -u github.com/go-chi/chi` or `go get -u github.com/go-chi/chi@latest` to install chi, which will install v1.5.0+ | ||||||
|  | built with go.mod support. | ||||||
|  | 
 | ||||||
|  | My apologies to the developers who will disagree with the decisions above, but, hope you'll try it and see it's a very | ||||||
|  | minor request which is backwards compatible and won't break your existing installations. | ||||||
|  | 
 | ||||||
|  | Cheers all, happy coding! | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ## v4.1.2 (2020-06-02) | ## v4.1.2 (2020-06-02) | ||||||
| 
 | 
 | ||||||
| - fix that handles MethodNotAllowed with path variables, thank you @caseyhadden for your contribution | - fix that handles MethodNotAllowed with path variables, thank you @caseyhadden for your contribution | ||||||
|  | @ -23,7 +84,6 @@ | ||||||
| - middleware.Recoverer: a bit prettier | - middleware.Recoverer: a bit prettier | ||||||
| - History of changes: see https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0 | - History of changes: see https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ## v4.0.4 (2020-03-24) | ## v4.0.4 (2020-03-24) | ||||||
| 
 | 
 | ||||||
| - middleware.Recoverer: new pretty stack trace printing (https://github.com/go-chi/chi/pull/496) | - middleware.Recoverer: new pretty stack trace printing (https://github.com/go-chi/chi/pull/496) | ||||||
|  |  | ||||||
							
								
								
									
										63
									
								
								vendor/github.com/go-chi/chi/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/go-chi/chi/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -15,7 +15,8 @@ public API service, which in turn powers all of our client-side applications. | ||||||
| The key considerations of chi's design are: project structure, maintainability, standard http | The key considerations of chi's design are: project structure, maintainability, standard http | ||||||
| handlers (stdlib-only), developer productivity, and deconstructing a large system into many small | handlers (stdlib-only), developer productivity, and deconstructing a large system into many small | ||||||
| parts. The core router `github.com/go-chi/chi` is quite small (less than 1000 LOC), but we've also | parts. The core router `github.com/go-chi/chi` is quite small (less than 1000 LOC), but we've also | ||||||
| included some useful/optional subpackages: [middleware](/middleware), [render](https://github.com/go-chi/render) and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too! | included some useful/optional subpackages: [middleware](/middleware), [render](https://github.com/go-chi/render) | ||||||
|  | and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too! | ||||||
| 
 | 
 | ||||||
| ## Install | ## Install | ||||||
| 
 | 
 | ||||||
|  | @ -27,10 +28,11 @@ included some useful/optional subpackages: [middleware](/middleware), [render](h | ||||||
| * **Lightweight** - cloc'd in ~1000 LOC for the chi router | * **Lightweight** - cloc'd in ~1000 LOC for the chi router | ||||||
| * **Fast** - yes, see [benchmarks](#benchmarks) | * **Fast** - yes, see [benchmarks](#benchmarks) | ||||||
| * **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http` | * **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http` | ||||||
| * **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and subrouter mounting | * **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and sub-router mounting | ||||||
| * **Context control** - built on new `context` package, providing value chaining, cancellations and timeouts | * **Context control** - built on new `context` package, providing value chaining, cancellations and timeouts | ||||||
| * **Robust** - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) | * **Robust** - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) | ||||||
| * **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown | * **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown | ||||||
|  | * **Go.mod support** - v1.x of chi (starting from v1.5.0), now has go.mod support (see [CHANGELOG](https://github.com/go-chi/chi/blob/master/CHANGELOG.md#v150-2020-11-12---now-with-gomod-support)) | ||||||
| * **No external dependencies** - plain ol' Go stdlib + net/http | * **No external dependencies** - plain ol' Go stdlib + net/http | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -334,9 +336,12 @@ with `net/http` can be used with chi's mux. | ||||||
| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ||||||
| | chi/middleware Handler | description                                                             | | | chi/middleware Handler | description                                                             | | ||||||
| | :--------------------- | :---------------------------------------------------------------------- | | | :--------------------- | :---------------------------------------------------------------------- | | ||||||
|  | | [AllowContentEncoding] | Enforces a whitelist of request Content-Encoding headers                | | ||||||
| | [AllowContentType]     | Explicit whitelist of accepted request Content-Types                    | | | [AllowContentType]     | Explicit whitelist of accepted request Content-Types                    | | ||||||
| | [BasicAuth]            | Basic HTTP authentication                                               | | | [BasicAuth]            | Basic HTTP authentication                                               | | ||||||
| | [Compress]             | Gzip compression for clients that accept compressed responses           | | | [Compress]             | Gzip compression for clients that accept compressed responses           | | ||||||
|  | | [ContentCharset]       | Ensure charset for Content-Type request headers                         | | ||||||
|  | | [CleanPath]            | Clean double slashes from request path                                  | | ||||||
| | [GetHead]              | Automatically route undefined HEAD requests to GET handlers             | | | [GetHead]              | Automatically route undefined HEAD requests to GET handlers             | | ||||||
| | [Heartbeat]            | Monitoring endpoint to check the servers pulse                          | | | [Heartbeat]            | Monitoring endpoint to check the servers pulse                          | | ||||||
| | [Logger]               | Logs the start and end of each request with the elapsed processing time | | | [Logger]               | Logs the start and end of each request with the elapsed processing time | | ||||||
|  | @ -346,6 +351,7 @@ with `net/http` can be used with chi's mux. | ||||||
| | [Recoverer]            | Gracefully absorb panics and prints the stack trace                     | | | [Recoverer]            | Gracefully absorb panics and prints the stack trace                     | | ||||||
| | [RequestID]            | Injects a request ID into the context of each request                   | | | [RequestID]            | Injects a request ID into the context of each request                   | | ||||||
| | [RedirectSlashes]      | Redirect slashes on routing paths                                       | | | [RedirectSlashes]      | Redirect slashes on routing paths                                       | | ||||||
|  | | [RouteHeaders]         | Route handling for request headers                                      | | ||||||
| | [SetHeader]            | Short-hand middleware to set a response header key/value                | | | [SetHeader]            | Short-hand middleware to set a response header key/value                | | ||||||
| | [StripSlashes]         | Strip slashes on routing paths                                          | | | [StripSlashes]         | Strip slashes on routing paths                                          | | ||||||
| | [Throttle]             | Puts a ceiling on the number of concurrent requests                     | | | [Throttle]             | Puts a ceiling on the number of concurrent requests                     | | ||||||
|  | @ -359,20 +365,19 @@ with `net/http` can be used with chi's mux. | ||||||
| [BasicAuth]: https://pkg.go.dev/github.com/go-chi/chi/middleware#BasicAuth | [BasicAuth]: https://pkg.go.dev/github.com/go-chi/chi/middleware#BasicAuth | ||||||
| [Compress]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compress | [Compress]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compress | ||||||
| [ContentCharset]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ContentCharset | [ContentCharset]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ContentCharset | ||||||
|  | [CleanPath]: https://pkg.go.dev/github.com/go-chi/chi/middleware#CleanPath | ||||||
| [GetHead]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetHead | [GetHead]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetHead | ||||||
| [GetReqID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetReqID | [GetReqID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetReqID | ||||||
| [Heartbeat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Heartbeat | [Heartbeat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Heartbeat | ||||||
| [Logger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Logger | [Logger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Logger | ||||||
| [New]: https://pkg.go.dev/github.com/go-chi/chi/middleware#New |  | ||||||
| [NextRequestID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#NextRequestID |  | ||||||
| [NoCache]: https://pkg.go.dev/github.com/go-chi/chi/middleware#NoCache | [NoCache]: https://pkg.go.dev/github.com/go-chi/chi/middleware#NoCache | ||||||
| [PrintPrettyStack]: https://pkg.go.dev/github.com/go-chi/chi/middleware#PrintPrettyStack |  | ||||||
| [Profiler]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Profiler | [Profiler]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Profiler | ||||||
| [RealIP]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RealIP | [RealIP]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RealIP | ||||||
| [Recoverer]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Recoverer | [Recoverer]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Recoverer | ||||||
| [RedirectSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RedirectSlashes | [RedirectSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RedirectSlashes | ||||||
| [RequestID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestID |  | ||||||
| [RequestLogger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestLogger | [RequestLogger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestLogger | ||||||
|  | [RequestID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestID | ||||||
|  | [RouteHeaders]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RouteHeaders | ||||||
| [SetHeader]: https://pkg.go.dev/github.com/go-chi/chi/middleware#SetHeader | [SetHeader]: https://pkg.go.dev/github.com/go-chi/chi/middleware#SetHeader | ||||||
| [StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes | [StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes | ||||||
| [Throttle]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Throttle | [Throttle]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Throttle | ||||||
|  | @ -390,7 +395,6 @@ with `net/http` can be used with chi's mux. | ||||||
| [LogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogEntry | [LogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogEntry | ||||||
| [LogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogFormatter | [LogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogFormatter | ||||||
| [LoggerInterface]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LoggerInterface | [LoggerInterface]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LoggerInterface | ||||||
| [Pattern]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Pattern |  | ||||||
| [ThrottleOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleOpts | [ThrottleOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleOpts | ||||||
| [WrapResponseWriter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WrapResponseWriter | [WrapResponseWriter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WrapResponseWriter | ||||||
| 
 | 
 | ||||||
|  | @ -430,25 +434,25 @@ and.. | ||||||
| 
 | 
 | ||||||
| The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark | The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark | ||||||
| 
 | 
 | ||||||
| Results as of Jan 9, 2019 with Go 1.11.4 on Linux X1 Carbon laptop | Results as of Nov 29, 2020 with Go 1.15.5 on Linux AMD 3950x | ||||||
| 
 | 
 | ||||||
| ```shell | ```shell | ||||||
| BenchmarkChi_Param            3000000         475 ns/op       432 B/op      3 allocs/op | BenchmarkChi_Param          	3075895	        384 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_Param5           2000000         696 ns/op       432 B/op      3 allocs/op | BenchmarkChi_Param5         	2116603	        566 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_Param20          1000000        1275 ns/op       432 B/op      3 allocs/op | BenchmarkChi_Param20        	 964117	       1227 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_ParamWrite       3000000         505 ns/op       432 B/op      3 allocs/op | BenchmarkChi_ParamWrite     	2863413	        420 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_GithubStatic     3000000         508 ns/op       432 B/op      3 allocs/op | BenchmarkChi_GithubStatic   	3045488	        395 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_GithubParam      2000000         669 ns/op       432 B/op      3 allocs/op | BenchmarkChi_GithubParam    	2204115	        540 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_GithubAll          10000      134627 ns/op     87699 B/op    609 allocs/op | BenchmarkChi_GithubAll      	  10000	     113811 ns/op	    81203 B/op    406 allocs/op | ||||||
| BenchmarkChi_GPlusStatic      3000000         402 ns/op       432 B/op      3 allocs/op | BenchmarkChi_GPlusStatic    	3337485	        359 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_GPlusParam       3000000         500 ns/op       432 B/op      3 allocs/op | BenchmarkChi_GPlusParam     	2825853	        423 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_GPlus2Params     3000000         586 ns/op       432 B/op      3 allocs/op | BenchmarkChi_GPlus2Params   	2471697	        483 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_GPlusAll          200000        7237 ns/op      5616 B/op     39 allocs/op | BenchmarkChi_GPlusAll       	 194220	       5950 ns/op	     5200 B/op     26 allocs/op | ||||||
| BenchmarkChi_ParseStatic      3000000         408 ns/op       432 B/op      3 allocs/op | BenchmarkChi_ParseStatic    	3365324	        356 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_ParseParam       3000000         488 ns/op       432 B/op      3 allocs/op | BenchmarkChi_ParseParam     	2976614	        404 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_Parse2Params     3000000         551 ns/op       432 B/op      3 allocs/op | BenchmarkChi_Parse2Params   	2638084	        439 ns/op	      400 B/op      2 allocs/op | ||||||
| BenchmarkChi_ParseAll          100000       13508 ns/op     11232 B/op     78 allocs/op | BenchmarkChi_ParseAll       	 109567	      11295 ns/op	    10400 B/op     52 allocs/op | ||||||
| BenchmarkChi_StaticAll          20000       81933 ns/op     67826 B/op    471 allocs/op | BenchmarkChi_StaticAll      	  16846	      71308 ns/op	    62802 B/op    314 allocs/op | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc | Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc | ||||||
|  | @ -459,6 +463,17 @@ on the duplicated (alloc'd) request and returns it the new request object. This | ||||||
| how setting context on a request in Go works. | how setting context on a request in Go works. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ## Go module support & note on chi's versioning | ||||||
|  | 
 | ||||||
|  | * Go.mod support means we reset our versioning starting from v1.5 (see [CHANGELOG](https://github.com/go-chi/chi/blob/master/CHANGELOG.md#v150-2020-11-12---now-with-gomod-support)) | ||||||
|  | * All older tags are preserved, are backwards-compatible and will "just work" as they | ||||||
|  | * Brand new systems can run `go get -u github.com/go-chi/chi` as normal, or `go get -u github.com/go-chi/chi@latest` | ||||||
|  | to install chi, which will install v1.x+ built with go.mod support, starting from v1.5.0. | ||||||
|  | * For existing projects who want to upgrade to the latest go.mod version, run: `go get -u github.com/go-chi/chi@v1.5.0`, | ||||||
|  | which will get you on the go.mod version line (as Go's mod cache may still remember v4.x). | ||||||
|  | * Any breaking changes will bump a "minor" release and backwards-compatible improvements/fixes will bump a "tiny" release. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ## Credits | ## Credits | ||||||
| 
 | 
 | ||||||
| * Carl Jackson for https://github.com/zenazn/goji | * Carl Jackson for https://github.com/zenazn/goji | ||||||
|  |  | ||||||
							
								
								
									
										54
									
								
								vendor/github.com/go-chi/chi/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/go-chi/chi/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -2,9 +2,9 @@ package chi | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"net" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // URLParam returns the url parameter from a http.Request object.
 | // URLParam returns the url parameter from a http.Request object.
 | ||||||
|  | @ -30,26 +30,6 @@ func RouteContext(ctx context.Context) *Context { | ||||||
| 	return val | 	return val | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ServerBaseContext wraps an http.Handler to set the request context to the
 |  | ||||||
| // `baseCtx`.
 |  | ||||||
| func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler { |  | ||||||
| 	fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 		ctx := r.Context() |  | ||||||
| 		baseCtx := baseCtx |  | ||||||
| 
 |  | ||||||
| 		// Copy over default net/http server context keys
 |  | ||||||
| 		if v, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok { |  | ||||||
| 			baseCtx = context.WithValue(baseCtx, http.ServerContextKey, v) |  | ||||||
| 		} |  | ||||||
| 		if v, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok { |  | ||||||
| 			baseCtx = context.WithValue(baseCtx, http.LocalAddrContextKey, v) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		h.ServeHTTP(w, r.WithContext(baseCtx)) |  | ||||||
| 	}) |  | ||||||
| 	return fn |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewRouteContext returns a new routing Context object.
 | // NewRouteContext returns a new routing Context object.
 | ||||||
| func NewRouteContext() *Context { | func NewRouteContext() *Context { | ||||||
| 	return &Context{} | 	return &Context{} | ||||||
|  | @ -92,6 +72,11 @@ type Context struct { | ||||||
| 
 | 
 | ||||||
| 	// methodNotAllowed hint
 | 	// methodNotAllowed hint
 | ||||||
| 	methodNotAllowed bool | 	methodNotAllowed bool | ||||||
|  | 
 | ||||||
|  | 	// parentCtx is the parent of this one, for using Context as a
 | ||||||
|  | 	// context.Context directly. This is an optimization that saves
 | ||||||
|  | 	// 1 allocation.
 | ||||||
|  | 	parentCtx context.Context | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Reset a routing context to its initial state.
 | // Reset a routing context to its initial state.
 | ||||||
|  | @ -107,6 +92,7 @@ func (x *Context) Reset() { | ||||||
| 	x.routeParams.Keys = x.routeParams.Keys[:0] | 	x.routeParams.Keys = x.routeParams.Keys[:0] | ||||||
| 	x.routeParams.Values = x.routeParams.Values[:0] | 	x.routeParams.Values = x.routeParams.Values[:0] | ||||||
| 	x.methodNotAllowed = false | 	x.methodNotAllowed = false | ||||||
|  | 	x.parentCtx = nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // URLParam returns the corresponding URL parameter value from the request
 | // URLParam returns the corresponding URL parameter value from the request
 | ||||||
|  | @ -160,6 +146,32 @@ func (s *RouteParams) Add(key, value string) { | ||||||
| 	s.Values = append(s.Values, value) | 	s.Values = append(s.Values, value) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // directContext provides direct access to the routing *Context object,
 | ||||||
|  | // while implementing the context.Context interface, thereby allowing
 | ||||||
|  | // us to saving 1 allocation during routing.
 | ||||||
|  | type directContext Context | ||||||
|  | 
 | ||||||
|  | var _ context.Context = (*directContext)(nil) | ||||||
|  | 
 | ||||||
|  | func (d *directContext) Deadline() (deadline time.Time, ok bool) { | ||||||
|  | 	return d.parentCtx.Deadline() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (d *directContext) Done() <-chan struct{} { | ||||||
|  | 	return d.parentCtx.Done() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (d *directContext) Err() error { | ||||||
|  | 	return d.parentCtx.Err() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (d *directContext) Value(key interface{}) interface{} { | ||||||
|  | 	if key == RouteCtxKey { | ||||||
|  | 		return (*Context)(d) | ||||||
|  | 	} | ||||||
|  | 	return d.parentCtx.Value(key) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // contextKey is a value for use with context.WithValue. It's used as
 | // contextKey is a value for use with context.WithValue. It's used as
 | ||||||
| // a pointer so it fits in an interface{} without allocation. This technique
 | // a pointer so it fits in an interface{} without allocation. This technique
 | ||||||
| // for defining context keys was copied from Go 1.7's new use of context in net/http.
 | // for defining context keys was copied from Go 1.7's new use of context in net/http.
 | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								vendor/github.com/go-chi/chi/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/go-chi/chi/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,6 +1,7 @@ | ||||||
| package middleware | package middleware | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/subtle" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| ) | ) | ||||||
|  | @ -16,7 +17,7 @@ func BasicAuth(realm string, creds map[string]string) func(next http.Handler) ht | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			credPass, credUserOk := creds[user] | 			credPass, credUserOk := creds[user] | ||||||
| 			if !credUserOk || pass != credPass { | 			if !credUserOk || subtle.ConstantTimeCompare([]byte(pass), []byte(credPass)) != 1 { | ||||||
| 				basicAuthFailed(w, realm) | 				basicAuthFailed(w, realm) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								vendor/github.com/go-chi/chi/middleware/clean_path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/go-chi/chi/middleware/clean_path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | package middleware | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"path" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-chi/chi" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // CleanPath middleware will clean out double slash mistakes from a user's request path.
 | ||||||
|  | // For example, if a user requests /users//1 or //users////1 will both be treated as: /users/1
 | ||||||
|  | func CleanPath(next http.Handler) http.Handler { | ||||||
|  | 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		rctx := chi.RouteContext(r.Context()) | ||||||
|  | 
 | ||||||
|  | 		routePath := rctx.RoutePath | ||||||
|  | 		if routePath == "" { | ||||||
|  | 			if r.URL.RawPath != "" { | ||||||
|  | 				routePath = r.URL.RawPath | ||||||
|  | 			} else { | ||||||
|  | 				routePath = r.URL.Path | ||||||
|  | 			} | ||||||
|  | 			rctx.RoutePath = path.Clean(routePath) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		next.ServeHTTP(w, r) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								vendor/github.com/go-chi/chi/middleware/content_type.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/go-chi/chi/middleware/content_type.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -19,9 +19,9 @@ func SetHeader(key, value string) func(next http.Handler) http.Handler { | ||||||
| // AllowContentType enforces a whitelist of request Content-Types otherwise responds
 | // AllowContentType enforces a whitelist of request Content-Types otherwise responds
 | ||||||
| // with a 415 Unsupported Media Type status.
 | // with a 415 Unsupported Media Type status.
 | ||||||
| func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler { | func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler { | ||||||
| 	cT := []string{} | 	allowedContentTypes := make(map[string]struct{}, len(contentTypes)) | ||||||
| 	for _, t := range contentTypes { | 	for _, ctype := range contentTypes { | ||||||
| 		cT = append(cT, strings.ToLower(t)) | 		allowedContentTypes[strings.TrimSpace(strings.ToLower(ctype))] = struct{}{} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return func(next http.Handler) http.Handler { | 	return func(next http.Handler) http.Handler { | ||||||
|  | @ -37,11 +37,9 @@ func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handl | ||||||
| 				s = s[0:i] | 				s = s[0:i] | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			for _, t := range cT { | 			if _, ok := allowedContentTypes[s]; ok { | ||||||
| 				if t == s { | 				next.ServeHTTP(w, r) | ||||||
| 					next.ServeHTTP(w, r) | 				return | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			w.WriteHeader(http.StatusUnsupportedMediaType) | 			w.WriteHeader(http.StatusUnsupportedMediaType) | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								vendor/github.com/go-chi/chi/middleware/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/go-chi/chi/middleware/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -6,6 +6,7 @@ import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"runtime" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -16,7 +17,7 @@ var ( | ||||||
| 	// DefaultLogger is called by the Logger middleware handler to log each request.
 | 	// DefaultLogger is called by the Logger middleware handler to log each request.
 | ||||||
| 	// Its made a package-level variable so that it can be reconfigured for custom
 | 	// Its made a package-level variable so that it can be reconfigured for custom
 | ||||||
| 	// logging configurations.
 | 	// logging configurations.
 | ||||||
| 	DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false}) | 	DefaultLogger func(next http.Handler) http.Handler | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Logger is a middleware that logs the start and end of each request, along
 | // Logger is a middleware that logs the start and end of each request, along
 | ||||||
|  | @ -27,6 +28,16 @@ var ( | ||||||
| //
 | //
 | ||||||
| // Alternatively, look at https://github.com/goware/httplog for a more in-depth
 | // Alternatively, look at https://github.com/goware/httplog for a more in-depth
 | ||||||
| // http logger with structured logging support.
 | // http logger with structured logging support.
 | ||||||
|  | //
 | ||||||
|  | // IMPORTANT NOTE: Logger should go before any other middleware that may change
 | ||||||
|  | // the response, such as `middleware.Recoverer`. Example:
 | ||||||
|  | //
 | ||||||
|  | // ```go
 | ||||||
|  | // r := chi.NewRouter()
 | ||||||
|  | // r.Use(middleware.Logger)        // <--<< Logger should come before Recoverer
 | ||||||
|  | // r.Use(middleware.Recoverer)
 | ||||||
|  | // r.Get("/", handler)
 | ||||||
|  | // ```
 | ||||||
| func Logger(next http.Handler) http.Handler { | func Logger(next http.Handler) http.Handler { | ||||||
| 	return DefaultLogger(next) | 	return DefaultLogger(next) | ||||||
| } | } | ||||||
|  | @ -153,3 +164,11 @@ func (l *defaultLogEntry) Write(status, bytes int, header http.Header, elapsed t | ||||||
| func (l *defaultLogEntry) Panic(v interface{}, stack []byte) { | func (l *defaultLogEntry) Panic(v interface{}, stack []byte) { | ||||||
| 	PrintPrettyStack(v) | 	PrintPrettyStack(v) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	color := true | ||||||
|  | 	if runtime.GOOS == "windows" { | ||||||
|  | 		color = false | ||||||
|  | 	} | ||||||
|  | 	DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: !color}) | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								vendor/github.com/go-chi/chi/middleware/strip.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/go-chi/chi/middleware/strip.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -14,13 +14,18 @@ func StripSlashes(next http.Handler) http.Handler { | ||||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		var path string | 		var path string | ||||||
| 		rctx := chi.RouteContext(r.Context()) | 		rctx := chi.RouteContext(r.Context()) | ||||||
| 		if rctx.RoutePath != "" { | 		if rctx != nil && rctx.RoutePath != "" { | ||||||
| 			path = rctx.RoutePath | 			path = rctx.RoutePath | ||||||
| 		} else { | 		} else { | ||||||
| 			path = r.URL.Path | 			path = r.URL.Path | ||||||
| 		} | 		} | ||||||
| 		if len(path) > 1 && path[len(path)-1] == '/' { | 		if len(path) > 1 && path[len(path)-1] == '/' { | ||||||
| 			rctx.RoutePath = path[:len(path)-1] | 			newPath := path[:len(path)-1] | ||||||
|  | 			if rctx == nil { | ||||||
|  | 				r.URL.Path = newPath | ||||||
|  | 			} else { | ||||||
|  | 				rctx.RoutePath = newPath | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		next.ServeHTTP(w, r) | 		next.ServeHTTP(w, r) | ||||||
| 	} | 	} | ||||||
|  | @ -36,7 +41,7 @@ func RedirectSlashes(next http.Handler) http.Handler { | ||||||
| 	fn := func(w http.ResponseWriter, r *http.Request) { | 	fn := func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		var path string | 		var path string | ||||||
| 		rctx := chi.RouteContext(r.Context()) | 		rctx := chi.RouteContext(r.Context()) | ||||||
| 		if rctx.RoutePath != "" { | 		if rctx != nil && rctx.RoutePath != "" { | ||||||
| 			path = rctx.RoutePath | 			path = rctx.RoutePath | ||||||
| 		} else { | 		} else { | ||||||
| 			path = r.URL.Path | 			path = r.URL.Path | ||||||
|  | @ -47,7 +52,8 @@ func RedirectSlashes(next http.Handler) http.Handler { | ||||||
| 			} else { | 			} else { | ||||||
| 				path = path[:len(path)-1] | 				path = path[:len(path)-1] | ||||||
| 			} | 			} | ||||||
| 			http.Redirect(w, r, path, 301) | 			redirectUrl := fmt.Sprintf("//%s%s", r.Host, path) | ||||||
|  | 			http.Redirect(w, r, redirectUrl, 301) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		next.ServeHTTP(w, r) | 		next.ServeHTTP(w, r) | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								vendor/github.com/go-chi/chi/middleware/url_format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/go-chi/chi/middleware/url_format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -53,7 +53,7 @@ func URLFormat(next http.Handler) http.Handler { | ||||||
| 
 | 
 | ||||||
| 		if strings.Index(path, ".") > 0 { | 		if strings.Index(path, ".") > 0 { | ||||||
| 			base := strings.LastIndex(path, "/") | 			base := strings.LastIndex(path, "/") | ||||||
| 			idx := strings.Index(path[base:], ".") | 			idx := strings.LastIndex(path[base:], ".") | ||||||
| 
 | 
 | ||||||
| 			if idx > 0 { | 			if idx > 0 { | ||||||
| 				idx += base | 				idx += base | ||||||
|  |  | ||||||
							
								
								
									
										46
									
								
								vendor/github.com/go-chi/chi/mux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/go-chi/chi/mux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,7 +1,6 @@ | ||||||
| package chi | package chi | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | @ -78,9 +77,10 @@ func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||||
| 	rctx = mx.pool.Get().(*Context) | 	rctx = mx.pool.Get().(*Context) | ||||||
| 	rctx.Reset() | 	rctx.Reset() | ||||||
| 	rctx.Routes = mx | 	rctx.Routes = mx | ||||||
|  | 	rctx.parentCtx = r.Context() | ||||||
| 
 | 
 | ||||||
| 	// NOTE: r.WithContext() causes 2 allocations and context.WithValue() causes 1 allocation
 | 	// NOTE: r.WithContext() causes 2 allocations
 | ||||||
| 	r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx)) | 	r = r.WithContext((*directContext)(rctx)) | ||||||
| 
 | 
 | ||||||
| 	// Serve the request and once its done, put the request context back in the sync pool
 | 	// Serve the request and once its done, put the request context back in the sync pool
 | ||||||
| 	mx.handler.ServeHTTP(w, r) | 	mx.handler.ServeHTTP(w, r) | ||||||
|  | @ -227,7 +227,7 @@ func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router { | ||||||
| 	// Similarly as in handle(), we must build the mux handler once additional
 | 	// Similarly as in handle(), we must build the mux handler once additional
 | ||||||
| 	// middleware registration isn't allowed for this stack, like now.
 | 	// middleware registration isn't allowed for this stack, like now.
 | ||||||
| 	if !mx.inline && mx.handler == nil { | 	if !mx.inline && mx.handler == nil { | ||||||
| 		mx.buildRouteHandler() | 		mx.updateRouteHandler() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Copy middlewares from parent inline muxs
 | 	// Copy middlewares from parent inline muxs
 | ||||||
|  | @ -261,10 +261,11 @@ func (mx *Mux) Group(fn func(r Router)) Router { | ||||||
| // along the `pattern` as a subrouter. Effectively, this is a short-hand
 | // along the `pattern` as a subrouter. Effectively, this is a short-hand
 | ||||||
| // call to Mount. See _examples/.
 | // call to Mount. See _examples/.
 | ||||||
| func (mx *Mux) Route(pattern string, fn func(r Router)) Router { | func (mx *Mux) Route(pattern string, fn func(r Router)) Router { | ||||||
| 	subRouter := NewRouter() | 	if fn == nil { | ||||||
| 	if fn != nil { | 		panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern)) | ||||||
| 		fn(subRouter) |  | ||||||
| 	} | 	} | ||||||
|  | 	subRouter := NewRouter() | ||||||
|  | 	fn(subRouter) | ||||||
| 	mx.Mount(pattern, subRouter) | 	mx.Mount(pattern, subRouter) | ||||||
| 	return subRouter | 	return subRouter | ||||||
| } | } | ||||||
|  | @ -277,6 +278,10 @@ func (mx *Mux) Route(pattern string, fn func(r Router)) Router { | ||||||
| // routing at the `handler`, which in most cases is another chi.Router. As a result,
 | // routing at the `handler`, which in most cases is another chi.Router. As a result,
 | ||||||
| // if you define two Mount() routes on the exact same pattern the mount will panic.
 | // if you define two Mount() routes on the exact same pattern the mount will panic.
 | ||||||
| func (mx *Mux) Mount(pattern string, handler http.Handler) { | func (mx *Mux) Mount(pattern string, handler http.Handler) { | ||||||
|  | 	if handler == nil { | ||||||
|  | 		panic(fmt.Sprintf("chi: attempting to Mount() a nil handler on '%s'", pattern)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Provide runtime safety for ensuring a pattern isn't mounted on an existing
 | 	// Provide runtime safety for ensuring a pattern isn't mounted on an existing
 | ||||||
| 	// routing pattern.
 | 	// routing pattern.
 | ||||||
| 	if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") { | 	if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") { | ||||||
|  | @ -294,7 +299,16 @@ func (mx *Mux) Mount(pattern string, handler http.Handler) { | ||||||
| 
 | 
 | ||||||
| 	mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 	mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		rctx := RouteContext(r.Context()) | 		rctx := RouteContext(r.Context()) | ||||||
|  | 
 | ||||||
|  | 		// shift the url path past the previous subrouter
 | ||||||
| 		rctx.RoutePath = mx.nextRoutePath(rctx) | 		rctx.RoutePath = mx.nextRoutePath(rctx) | ||||||
|  | 
 | ||||||
|  | 		// reset the wildcard URLParam which connects the subrouter
 | ||||||
|  | 		n := len(rctx.URLParams.Keys) - 1 | ||||||
|  | 		if n >= 0 && rctx.URLParams.Keys[n] == "*" && len(rctx.URLParams.Values) > n { | ||||||
|  | 			rctx.URLParams.Values[n] = "" | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		handler.ServeHTTP(w, r) | 		handler.ServeHTTP(w, r) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | @ -367,14 +381,6 @@ func (mx *Mux) MethodNotAllowedHandler() http.HandlerFunc { | ||||||
| 	return methodNotAllowedHandler | 	return methodNotAllowedHandler | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // buildRouteHandler builds the single mux handler that is a chain of the middleware
 |  | ||||||
| // stack, as defined by calls to Use(), and the tree router (Mux) itself. After this
 |  | ||||||
| // point, no other middlewares can be registered on this Mux's stack. But you can still
 |  | ||||||
| // compose additional middlewares via Group()'s or using a chained middleware handler.
 |  | ||||||
| func (mx *Mux) buildRouteHandler() { |  | ||||||
| 	mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // handle registers a http.Handler in the routing tree for a particular http method
 | // handle registers a http.Handler in the routing tree for a particular http method
 | ||||||
| // and routing pattern.
 | // and routing pattern.
 | ||||||
| func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node { | func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node { | ||||||
|  | @ -384,7 +390,7 @@ func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *n | ||||||
| 
 | 
 | ||||||
| 	// Build the computed routing handler for this routing pattern.
 | 	// Build the computed routing handler for this routing pattern.
 | ||||||
| 	if !mx.inline && mx.handler == nil { | 	if !mx.inline && mx.handler == nil { | ||||||
| 		mx.buildRouteHandler() | 		mx.updateRouteHandler() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Build endpoint handler with inline middlewares for the route
 | 	// Build endpoint handler with inline middlewares for the route
 | ||||||
|  | @ -458,6 +464,14 @@ func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // updateRouteHandler builds the single mux handler that is a chain of the middleware
 | ||||||
|  | // stack, as defined by calls to Use(), and the tree router (Mux) itself. After this
 | ||||||
|  | // point, no other middlewares can be registered on this Mux's stack. But you can still
 | ||||||
|  | // compose additional middlewares via Group()'s or using a chained middleware handler.
 | ||||||
|  | func (mx *Mux) updateRouteHandler() { | ||||||
|  | 	mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // methodNotAllowedHandler is a helper function to respond with a 405,
 | // methodNotAllowedHandler is a helper function to respond with a 405,
 | ||||||
| // method not allowed.
 | // method not allowed.
 | ||||||
| func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) { | func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) { | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								vendor/github.com/unrolled/render/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/unrolled/render/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | # Compiled Object files, Static and Dynamic libs (Shared Objects) | ||||||
|  | *.o | ||||||
|  | *.a | ||||||
|  | *.so | ||||||
|  | 
 | ||||||
|  | # Folders | ||||||
|  | _obj | ||||||
|  | _test | ||||||
|  | 
 | ||||||
|  | # Architecture specific extensions/prefixes | ||||||
|  | *.[568vq] | ||||||
|  | [568vq].out | ||||||
|  | 
 | ||||||
|  | *.cgo1.go | ||||||
|  | *.cgo2.c | ||||||
|  | _cgo_defun.c | ||||||
|  | _cgo_gotypes.go | ||||||
|  | _cgo_export.* | ||||||
|  | 
 | ||||||
|  | _testmain.go | ||||||
|  | 
 | ||||||
|  | *.exe | ||||||
|  | *.test | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | *.pem | ||||||
|  | .DS_Store | ||||||
							
								
								
									
										15
									
								
								vendor/github.com/unrolled/render/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/unrolled/render/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | language: go | ||||||
|  | 
 | ||||||
|  | go: | ||||||
|  |   - 1.11.x | ||||||
|  |   - 1.12.x | ||||||
|  |   - tip | ||||||
|  | 
 | ||||||
|  | env: | ||||||
|  |   - GO111MODULE=on | ||||||
|  | 
 | ||||||
|  | install: | ||||||
|  |   - go mod download  | ||||||
|  | 
 | ||||||
|  | script: | ||||||
|  |   - go test -v -race -tags=integration | ||||||
							
								
								
									
										20
									
								
								vendor/github.com/unrolled/render/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/unrolled/render/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | The MIT License (MIT) | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2014 Cory Jacobsen | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||||
|  | this software and associated documentation files (the "Software"), to deal in | ||||||
|  | the Software without restriction, including without limitation the rights to | ||||||
|  | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||||
|  | the Software, and to permit persons to whom the Software is furnished to do so, | ||||||
|  | subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||||
|  | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||||
|  | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||||
|  | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||||
|  | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
							
								
								
									
										508
									
								
								vendor/github.com/unrolled/render/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										508
									
								
								vendor/github.com/unrolled/render/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,508 @@ | ||||||
|  | # Render [](http://godoc.org/github.com/unrolled/render) [](https://travis-ci.org/unrolled/render) | ||||||
|  | 
 | ||||||
|  | Render is a package that provides functionality for easily rendering JSON, XML, text, binary data, and HTML templates. This package is based on the [Martini](https://github.com/go-martini/martini) [render](https://github.com/martini-contrib/render) work. | ||||||
|  | 
 | ||||||
|  | ## Block Deprecation Notice | ||||||
|  | Go 1.6 introduces a new [block](https://github.com/golang/go/blob/release-branch.go1.6/src/html/template/example_test.go#L128) action. This conflicts with Render's included `block` template function. To provide an easy migration path, a new function was created called `partial`. It is a duplicate of the old `block` function. It is advised that all users of the `block` function update their code to avoid any issues in the future. Previous to Go 1.6, Render's `block` functionality will continue to work but a message will be logged urging you to migrate to the new `partial` function. | ||||||
|  | 
 | ||||||
|  | ## Usage | ||||||
|  | Render can be used with pretty much any web framework providing you can access the `http.ResponseWriter` from your handler. The rendering functions simply wraps Go's existing functionality for marshaling and rendering data. | ||||||
|  | 
 | ||||||
|  | - HTML: Uses the [html/template](http://golang.org/pkg/html/template/) package to render HTML templates. | ||||||
|  | - JSON: Uses the [encoding/json](http://golang.org/pkg/encoding/json/) package to marshal data into a JSON-encoded response. | ||||||
|  | - XML: Uses the [encoding/xml](http://golang.org/pkg/encoding/xml/) package to marshal data into an XML-encoded response. | ||||||
|  | - Binary data: Passes the incoming data straight through to the `http.ResponseWriter`. | ||||||
|  | - Text: Passes the incoming string straight through to the `http.ResponseWriter`. | ||||||
|  | 
 | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "encoding/xml" | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ExampleXml struct { | ||||||
|  |     XMLName xml.Name `xml:"example"` | ||||||
|  |     One     string   `xml:"one,attr"` | ||||||
|  |     Two     string   `xml:"two,attr"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := render.New() | ||||||
|  |     mux := http.NewServeMux() | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         w.Write([]byte("Welcome, visit sub pages now.")) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.Data(w, http.StatusOK, []byte("Some binary data here.")) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/text", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.Text(w, http.StatusOK, "Plain text here") | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/json", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.JSON(w, http.StatusOK, map[string]string{"hello": "json"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/jsonp", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.JSONP(w, http.StatusOK, "callbackName", map[string]string{"hello": "jsonp"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/xml", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.XML(w, http.StatusOK, ExampleXml{One: "hello", Two: "xml"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/html", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         // Assumes you have a template in ./templates called "example.tmpl" | ||||||
|  |         // $ mkdir -p templates && echo "<h1>Hello {{.}}.</h1>" > templates/example.tmpl | ||||||
|  |         r.HTML(w, http.StatusOK, "example", "World") | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     http.ListenAndServe("127.0.0.1:3000", mux) | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ~~~ html | ||||||
|  | <!-- templates/example.tmpl --> | ||||||
|  | <h1>Hello {{.}}.</h1> | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### Available Options | ||||||
|  | Render comes with a variety of configuration options _(Note: these are not the default option values. See the defaults below.)_: | ||||||
|  | 
 | ||||||
|  | ~~~ go | ||||||
|  | // ... | ||||||
|  | r := render.New(render.Options{ | ||||||
|  |     Directory: "templates", // Specify what path to load the templates from. | ||||||
|  |     FileSystem: &LocalFileSystem{}, // Specify filesystem from where files are loaded. | ||||||
|  |     Asset: func(name string) ([]byte, error) { // Load from an Asset function instead of file. | ||||||
|  |       return []byte("template content"), nil | ||||||
|  |     }, | ||||||
|  |     AssetNames: func() []string { // Return a list of asset names for the Asset function | ||||||
|  |       return []string{"filename.tmpl"} | ||||||
|  |     }, | ||||||
|  |     Layout: "layout", // Specify a layout template. Layouts can call {{ yield }} to render the current template or {{ partial "css" }} to render a partial from the current template. | ||||||
|  |     Extensions: []string{".tmpl", ".html"}, // Specify extensions to load for templates. | ||||||
|  |     Funcs: []template.FuncMap{AppHelpers}, // Specify helper function maps for templates to access. | ||||||
|  |     Delims: render.Delims{"{[{", "}]}"}, // Sets delimiters to the specified strings. | ||||||
|  |     Charset: "UTF-8", // Sets encoding for content-types. Default is "UTF-8". | ||||||
|  |     DisableCharset: true, // Prevents the charset from being appended to the content type header. | ||||||
|  |     IndentJSON: true, // Output human readable JSON. | ||||||
|  |     IndentXML: true, // Output human readable XML. | ||||||
|  |     PrefixJSON: []byte(")]}',\n"), // Prefixes JSON responses with the given bytes. | ||||||
|  |     PrefixXML: []byte("<?xml version='1.0' encoding='UTF-8'?>"), // Prefixes XML responses with the given bytes. | ||||||
|  |     HTMLContentType: "application/xhtml+xml", // Output XHTML content type instead of default "text/html". | ||||||
|  |     IsDevelopment: true, // Render will now recompile the templates on every HTML response. | ||||||
|  |     UnEscapeHTML: true, // Replace ensure '&<>' are output correctly (JSON only). | ||||||
|  |     StreamingJSON: true, // Streams the JSON response via json.Encoder. | ||||||
|  |     RequirePartials: true, // Return an error if a template is missing a partial used in a layout. | ||||||
|  |     DisableHTTPErrorRendering: true, // Disables automatic rendering of http.StatusInternalServerError when an error occurs. | ||||||
|  | }) | ||||||
|  | // ... | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### Default Options | ||||||
|  | These are the preset options for Render: | ||||||
|  | 
 | ||||||
|  | ~~~ go | ||||||
|  | r := render.New() | ||||||
|  | 
 | ||||||
|  | // Is the same as the default configuration options: | ||||||
|  | 
 | ||||||
|  | r := render.New(render.Options{ | ||||||
|  |     Directory: "templates", | ||||||
|  |     FileSystem: &LocalFileSystem{}, | ||||||
|  |     Asset: nil, | ||||||
|  |     AssetNames: nil, | ||||||
|  |     Layout: "", | ||||||
|  |     Extensions: []string{".tmpl"}, | ||||||
|  |     Funcs: []template.FuncMap{}, | ||||||
|  |     Delims: render.Delims{"{{", "}}"}, | ||||||
|  |     Charset: "UTF-8", | ||||||
|  |     DisableCharset: false, | ||||||
|  |     IndentJSON: false, | ||||||
|  |     IndentXML: false, | ||||||
|  |     PrefixJSON: []byte(""), | ||||||
|  |     PrefixXML: []byte(""), | ||||||
|  |     BinaryContentType: "application/octet-stream", | ||||||
|  |     HTMLContentType: "text/html", | ||||||
|  |     JSONContentType: "application/json", | ||||||
|  |     JSONPContentType: "application/javascript", | ||||||
|  |     TextContentType: "text/plain", | ||||||
|  |     XMLContentType: "application/xhtml+xml", | ||||||
|  |     IsDevelopment: false, | ||||||
|  |     UnEscapeHTML: false, | ||||||
|  |     StreamingJSON: false, | ||||||
|  |     RequirePartials: false, | ||||||
|  |     DisableHTTPErrorRendering: false, | ||||||
|  | }) | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### JSON vs Streaming JSON | ||||||
|  | By default, Render does **not** stream JSON to the `http.ResponseWriter`. It instead marshalls your object into a byte array, and if no errors occurred, writes that byte array to the `http.ResponseWriter`. If you would like to use the built it in streaming functionality (`json.Encoder`), you can set the `StreamingJSON` setting to `true`. This will stream the output directly to the `http.ResponseWriter`. Also note that streaming is only implemented in `render.JSON` and not `render.JSONP`, and the `UnEscapeHTML` and `Indent` options are ignored when streaming. | ||||||
|  | 
 | ||||||
|  | ### Loading Templates | ||||||
|  | By default Render will attempt to load templates with a '.tmpl' extension from the "templates" directory. Templates are found by traversing the templates directory and are named by path and basename. For instance, the following directory structure: | ||||||
|  | 
 | ||||||
|  | ~~~ | ||||||
|  | templates/ | ||||||
|  |   | | ||||||
|  |   |__ admin/ | ||||||
|  |   |      | | ||||||
|  |   |      |__ index.tmpl | ||||||
|  |   |      | | ||||||
|  |   |      |__ edit.tmpl | ||||||
|  |   | | ||||||
|  |   |__ home.tmpl | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | Will provide the following templates: | ||||||
|  | ~~~ | ||||||
|  | admin/index | ||||||
|  | admin/edit | ||||||
|  | home | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | You can also load templates from memory by providing the Asset and AssetNames options, | ||||||
|  | e.g. when generating an asset file using [go-bindata](https://github.com/jteeuwen/go-bindata). | ||||||
|  | 
 | ||||||
|  | ### Layouts | ||||||
|  | Render provides `yield` and `partial` functions for layouts to access: | ||||||
|  | ~~~ go | ||||||
|  | // ... | ||||||
|  | r := render.New(render.Options{ | ||||||
|  |     Layout: "layout", | ||||||
|  | }) | ||||||
|  | // ... | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ~~~ html | ||||||
|  | <!-- templates/layout.tmpl --> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <title>My Layout</title> | ||||||
|  |     <!-- Render the partial template called `css-$current_template` here --> | ||||||
|  |     {{ partial "css" }} | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <!-- render the partial template called `header-$current_template` here --> | ||||||
|  |     {{ partial "header" }} | ||||||
|  |     <!-- Render the current template here --> | ||||||
|  |     {{ yield }} | ||||||
|  |     <!-- render the partial template called `footer-$current_template` here --> | ||||||
|  |     {{ partial "footer" }} | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | `current` can also be called to get the current template being rendered. | ||||||
|  | ~~~ html | ||||||
|  | <!-- templates/layout.tmpl --> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <title>My Layout</title> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     This is the {{ current }} page. | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | Partials are defined by individual templates as seen below. The partial template's | ||||||
|  | name needs to be defined as "{partial name}-{template name}". | ||||||
|  | ~~~ html | ||||||
|  | <!-- templates/home.tmpl --> | ||||||
|  | {{ define "header-home" }} | ||||||
|  | <h1>Home</h1> | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "footer-home"}} | ||||||
|  | <p>The End</p> | ||||||
|  | {{ end }} | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | By default, the template is not required to define all partials referenced in the | ||||||
|  | layout. If you want an error to be returned when a template does not define a | ||||||
|  | partial, set `Options.RequirePartials = true`. | ||||||
|  | 
 | ||||||
|  | ### Character Encodings | ||||||
|  | Render will automatically set the proper Content-Type header based on which function you call. See below for an example of what the default settings would output (note that UTF-8 is the default, and binary data does not output the charset): | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "encoding/xml" | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ExampleXml struct { | ||||||
|  |     XMLName xml.Name `xml:"example"` | ||||||
|  |     One     string   `xml:"one,attr"` | ||||||
|  |     Two     string   `xml:"two,attr"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := render.New(render.Options{}) | ||||||
|  |     mux := http.NewServeMux() | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "application/octet-stream". | ||||||
|  |     // Note that this does not receive a charset value. | ||||||
|  |     mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.Data(w, http.StatusOK, []byte("Some binary data here.")) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "application/json; charset=UTF-8". | ||||||
|  |     mux.HandleFunc("/json", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.JSON(w, http.StatusOK, map[string]string{"hello": "json"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "text/xml; charset=UTF-8". | ||||||
|  |     mux.HandleFunc("/xml", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.XML(w, http.StatusOK, ExampleXml{One: "hello", Two: "xml"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "text/plain; charset=UTF-8". | ||||||
|  |     mux.HandleFunc("/text", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.Text(w, http.StatusOK, "Plain text here") | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "text/html; charset=UTF-8". | ||||||
|  |     mux.HandleFunc("/html", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         // Assumes you have a template in ./templates called "example.tmpl" | ||||||
|  |         // $ mkdir -p templates && echo "<h1>Hello {{.}}.</h1>" > templates/example.tmpl | ||||||
|  |         r.HTML(w, http.StatusOK, "example", "World") | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     http.ListenAndServe("127.0.0.1:3000", mux) | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | In order to change the charset, you can set the `Charset` within the `render.Options` to your encoding value: | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "encoding/xml" | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ExampleXml struct { | ||||||
|  |     XMLName xml.Name `xml:"example"` | ||||||
|  |     One     string   `xml:"one,attr"` | ||||||
|  |     Two     string   `xml:"two,attr"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := render.New(render.Options{ | ||||||
|  |         Charset: "ISO-8859-1", | ||||||
|  |     }) | ||||||
|  |     mux := http.NewServeMux() | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "application/octet-stream". | ||||||
|  |     // Note that this does not receive a charset value. | ||||||
|  |     mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.Data(w, http.StatusOK, []byte("Some binary data here.")) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "application/json; charset=ISO-8859-1". | ||||||
|  |     mux.HandleFunc("/json", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.JSON(w, http.StatusOK, map[string]string{"hello": "json"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "text/xml; charset=ISO-8859-1". | ||||||
|  |     mux.HandleFunc("/xml", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.XML(w, http.StatusOK, ExampleXml{One: "hello", Two: "xml"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "text/plain; charset=ISO-8859-1". | ||||||
|  |     mux.HandleFunc("/text", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.Text(w, http.StatusOK, "Plain text here") | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // This will set the Content-Type header to "text/html; charset=ISO-8859-1". | ||||||
|  |     mux.HandleFunc("/html", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         // Assumes you have a template in ./templates called "example.tmpl" | ||||||
|  |         // $ mkdir -p templates && echo "<h1>Hello {{.}}.</h1>" > templates/example.tmpl | ||||||
|  |         r.HTML(w, http.StatusOK, "example", "World") | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     http.ListenAndServe("127.0.0.1:3000", mux) | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### Error Handling | ||||||
|  | 
 | ||||||
|  | The rendering functions return any errors from the rendering engine. | ||||||
|  | By default, they will also write the error to the HTTP response and set the status code to 500. You can disable | ||||||
|  | this behavior so that you can handle errors yourself by setting | ||||||
|  | `Options.DisableHTTPErrorRendering: true`. | ||||||
|  | 
 | ||||||
|  | ~~~go | ||||||
|  | r := render.New(render.Options{ | ||||||
|  |   DisableHTTPErrorRendering: true, | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | //... | ||||||
|  | 
 | ||||||
|  | err := r.HTML(w, http.StatusOK, "example", "World") | ||||||
|  | if err != nil{ | ||||||
|  |   http.Redirect(w, r, "/my-custom-500", http.StatusFound) | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ## Integration Examples | ||||||
|  | 
 | ||||||
|  | ### [Echo](https://github.com/labstack/echo) | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "io" | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/labstack/echo" | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type RenderWrapper struct { // We need to wrap the renderer because we need a different signature for echo. | ||||||
|  |     rnd *render.Render | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *RenderWrapper) Render(w io.Writer, name string, data interface{},c echo.Context) error { | ||||||
|  |     return r.rnd.HTML(w, 0, name, data) // The zero status code is overwritten by echo. | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := &RenderWrapper{render.New()} | ||||||
|  | 
 | ||||||
|  |     e := echo.New() | ||||||
|  | 
 | ||||||
|  |     e.Renderer = r | ||||||
|  | 
 | ||||||
|  |     e.GET("/", func(c echo.Context) error { | ||||||
|  |         return c.Render(http.StatusOK, "TemplateName", "TemplateData") | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     e.Logger.Fatal(e.Start(":1323")) | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### [Gin](https://github.com/gin-gonic/gin) | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/gin-gonic/gin" | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := render.New(render.Options{ | ||||||
|  |         IndentJSON: true, | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     router := gin.Default() | ||||||
|  | 
 | ||||||
|  |     router.GET("/", func(c *gin.Context) { | ||||||
|  |         r.JSON(c.Writer, http.StatusOK, map[string]string{"welcome": "This is rendered JSON!"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     router.Run(":3000") | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### [Goji](https://github.com/zenazn/goji) | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/zenazn/goji" | ||||||
|  |     "github.com/zenazn/goji/web" | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := render.New(render.Options{ | ||||||
|  |         IndentJSON: true, | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     goji.Get("/", func(c web.C, w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.JSON(w, http.StatusOK, map[string]string{"welcome": "This is rendered JSON!"}) | ||||||
|  |     }) | ||||||
|  |     goji.Serve()  // Defaults to ":8000". | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### [Negroni](https://github.com/codegangsta/negroni) | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/urfave/negroni" | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := render.New(render.Options{ | ||||||
|  |         IndentJSON: true, | ||||||
|  |     }) | ||||||
|  |     mux := http.NewServeMux() | ||||||
|  | 
 | ||||||
|  |     mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |         r.JSON(w, http.StatusOK, map[string]string{"welcome": "This is rendered JSON!"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     n := negroni.Classic() | ||||||
|  |     n.UseHandler(mux) | ||||||
|  |     n.Run(":3000") | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
|  | 
 | ||||||
|  | ### [Traffic](https://github.com/pilu/traffic) | ||||||
|  | ~~~ go | ||||||
|  | // main.go | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "net/http" | ||||||
|  | 
 | ||||||
|  |     "github.com/pilu/traffic" | ||||||
|  |     "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  |     r := render.New(render.Options{ | ||||||
|  |         IndentJSON: true, | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     router := traffic.New() | ||||||
|  |     router.Get("/", func(w traffic.ResponseWriter, req *traffic.Request) { | ||||||
|  |         r.JSON(w, http.StatusOK, map[string]string{"welcome": "This is rendered JSON!"}) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     router.Run() | ||||||
|  | } | ||||||
|  | ~~~ | ||||||
							
								
								
									
										46
									
								
								vendor/github.com/unrolled/render/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/unrolled/render/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | package render | ||||||
|  | 
 | ||||||
|  | import "bytes" | ||||||
|  | 
 | ||||||
|  | // bufPool represents a reusable buffer pool for executing templates into.
 | ||||||
|  | var bufPool *BufferPool | ||||||
|  | 
 | ||||||
|  | // BufferPool implements a pool of bytes.Buffers in the form of a bounded channel.
 | ||||||
|  | // Pulled from the github.com/oxtoacart/bpool package (Apache licensed).
 | ||||||
|  | type BufferPool struct { | ||||||
|  | 	c chan *bytes.Buffer | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewBufferPool creates a new BufferPool bounded to the given size.
 | ||||||
|  | func NewBufferPool(size int) (bp *BufferPool) { | ||||||
|  | 	return &BufferPool{ | ||||||
|  | 		c: make(chan *bytes.Buffer, size), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get gets a Buffer from the BufferPool, or creates a new one if none are
 | ||||||
|  | // available in the pool.
 | ||||||
|  | func (bp *BufferPool) Get() (b *bytes.Buffer) { | ||||||
|  | 	select { | ||||||
|  | 	case b = <-bp.c: | ||||||
|  | 	// reuse existing buffer
 | ||||||
|  | 	default: | ||||||
|  | 		// create new buffer
 | ||||||
|  | 		b = bytes.NewBuffer([]byte{}) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Put returns the given Buffer to the BufferPool.
 | ||||||
|  | func (bp *BufferPool) Put(b *bytes.Buffer) { | ||||||
|  | 	b.Reset() | ||||||
|  | 	select { | ||||||
|  | 	case bp.c <- b: | ||||||
|  | 	default: // Discard the buffer if the pool is full.
 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Initialize buffer pool for writing templates into.
 | ||||||
|  | func init() { | ||||||
|  | 	bufPool = NewBufferPool(64) | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								vendor/github.com/unrolled/render/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/unrolled/render/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | ||||||
|  | /*Package render is a package that provides functionality for easily rendering JSON, XML, binary data, and HTML templates. | ||||||
|  | 
 | ||||||
|  |   package main | ||||||
|  | 
 | ||||||
|  |   import ( | ||||||
|  |       "encoding/xml" | ||||||
|  |       "net/http" | ||||||
|  | 
 | ||||||
|  |       "github.com/unrolled/render"  // or "gopkg.in/unrolled/render.v1"
 | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|  |   type ExampleXml struct { | ||||||
|  |       XMLName xml.Name `xml:"example"` | ||||||
|  |       One     string   `xml:"one,attr"` | ||||||
|  |       Two     string   `xml:"two,attr"` | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   func main() { | ||||||
|  |       r := render.New() | ||||||
|  |       mux := http.NewServeMux() | ||||||
|  | 
 | ||||||
|  |       mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |           w.Write([]byte("Welcome, visit sub pages now.")) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |           r.Data(w, http.StatusOK, []byte("Some binary data here.")) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       mux.HandleFunc("/text", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |           r.Text(w, http.StatusOK, "Plain text here") | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       mux.HandleFunc("/json", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |           r.JSON(w, http.StatusOK, map[string]string{"hello": "json"}) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       mux.HandleFunc("/jsonp", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |           r.JSONP(w, http.StatusOK, "callbackName", map[string]string{"hello": "jsonp"}) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       mux.HandleFunc("/xml", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |           r.XML(w, http.StatusOK, ExampleXml{One: "hello", Two: "xml"}) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       mux.HandleFunc("/html", func(w http.ResponseWriter, req *http.Request) { | ||||||
|  |           // Assumes you have a template in ./templates called "example.tmpl".
 | ||||||
|  |           // $ mkdir -p templates && echo "<h1>Hello HTML world.</h1>" > templates/example.tmpl
 | ||||||
|  |           r.HTML(w, http.StatusOK, "example", nil) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       http.ListenAndServe("0.0.0.0:3000", mux) | ||||||
|  |   } | ||||||
|  | */ | ||||||
|  | package render | ||||||
							
								
								
									
										217
									
								
								vendor/github.com/unrolled/render/engine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								vendor/github.com/unrolled/render/engine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,217 @@ | ||||||
|  | package render | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"encoding/xml" | ||||||
|  | 	"html/template" | ||||||
|  | 	"io" | ||||||
|  | 	"net/http" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Engine is the generic interface for all responses.
 | ||||||
|  | type Engine interface { | ||||||
|  | 	Render(io.Writer, interface{}) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Head defines the basic ContentType and Status fields.
 | ||||||
|  | type Head struct { | ||||||
|  | 	ContentType string | ||||||
|  | 	Status      int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Data built-in renderer.
 | ||||||
|  | type Data struct { | ||||||
|  | 	Head | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // HTML built-in renderer.
 | ||||||
|  | type HTML struct { | ||||||
|  | 	Head | ||||||
|  | 	Name      string | ||||||
|  | 	Templates *template.Template | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // JSON built-in renderer.
 | ||||||
|  | type JSON struct { | ||||||
|  | 	Head | ||||||
|  | 	Indent        bool | ||||||
|  | 	UnEscapeHTML  bool | ||||||
|  | 	Prefix        []byte | ||||||
|  | 	StreamingJSON bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // JSONP built-in renderer.
 | ||||||
|  | type JSONP struct { | ||||||
|  | 	Head | ||||||
|  | 	Indent   bool | ||||||
|  | 	Callback string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Text built-in renderer.
 | ||||||
|  | type Text struct { | ||||||
|  | 	Head | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // XML built-in renderer.
 | ||||||
|  | type XML struct { | ||||||
|  | 	Head | ||||||
|  | 	Indent bool | ||||||
|  | 	Prefix []byte | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Write outputs the header content.
 | ||||||
|  | func (h Head) Write(w http.ResponseWriter) { | ||||||
|  | 	w.Header().Set(ContentType, h.ContentType) | ||||||
|  | 	w.WriteHeader(h.Status) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render a data response.
 | ||||||
|  | func (d Data) Render(w io.Writer, v interface{}) error { | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); ok { | ||||||
|  | 		c := hw.Header().Get(ContentType) | ||||||
|  | 		if c != "" { | ||||||
|  | 			d.Head.ContentType = c | ||||||
|  | 		} | ||||||
|  | 		d.Head.Write(hw) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	w.Write(v.([]byte)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render a HTML response.
 | ||||||
|  | func (h HTML) Render(w io.Writer, binding interface{}) error { | ||||||
|  | 	// Retrieve a buffer from the pool to write to.
 | ||||||
|  | 	out := bufPool.Get() | ||||||
|  | 	err := h.Templates.ExecuteTemplate(out, h.Name, binding) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); ok { | ||||||
|  | 		h.Head.Write(hw) | ||||||
|  | 	} | ||||||
|  | 	out.WriteTo(w) | ||||||
|  | 
 | ||||||
|  | 	// Return the buffer to the pool.
 | ||||||
|  | 	bufPool.Put(out) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render a JSON response.
 | ||||||
|  | func (j JSON) Render(w io.Writer, v interface{}) error { | ||||||
|  | 	if j.StreamingJSON { | ||||||
|  | 		return j.renderStreamingJSON(w, v) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var result []byte | ||||||
|  | 	var err error | ||||||
|  | 
 | ||||||
|  | 	if j.Indent { | ||||||
|  | 		result, err = json.MarshalIndent(v, "", "  ") | ||||||
|  | 		result = append(result, '\n') | ||||||
|  | 	} else { | ||||||
|  | 		result, err = json.Marshal(v) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Unescape HTML if needed.
 | ||||||
|  | 	if j.UnEscapeHTML { | ||||||
|  | 		result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1) | ||||||
|  | 		result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1) | ||||||
|  | 		result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// JSON marshaled fine, write out the result.
 | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); ok { | ||||||
|  | 		j.Head.Write(hw) | ||||||
|  | 	} | ||||||
|  | 	if len(j.Prefix) > 0 { | ||||||
|  | 		w.Write(j.Prefix) | ||||||
|  | 	} | ||||||
|  | 	w.Write(result) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (j JSON) renderStreamingJSON(w io.Writer, v interface{}) error { | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); ok { | ||||||
|  | 		j.Head.Write(hw) | ||||||
|  | 	} | ||||||
|  | 	if len(j.Prefix) > 0 { | ||||||
|  | 		w.Write(j.Prefix) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return json.NewEncoder(w).Encode(v) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render a JSONP response.
 | ||||||
|  | func (j JSONP) Render(w io.Writer, v interface{}) error { | ||||||
|  | 	var result []byte | ||||||
|  | 	var err error | ||||||
|  | 
 | ||||||
|  | 	if j.Indent { | ||||||
|  | 		result, err = json.MarshalIndent(v, "", "  ") | ||||||
|  | 	} else { | ||||||
|  | 		result, err = json.Marshal(v) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// JSON marshaled fine, write out the result.
 | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); ok { | ||||||
|  | 		j.Head.Write(hw) | ||||||
|  | 	} | ||||||
|  | 	w.Write([]byte(j.Callback + "(")) | ||||||
|  | 	w.Write(result) | ||||||
|  | 	w.Write([]byte(");")) | ||||||
|  | 
 | ||||||
|  | 	// If indenting, append a new line.
 | ||||||
|  | 	if j.Indent { | ||||||
|  | 		w.Write([]byte("\n")) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render a text response.
 | ||||||
|  | func (t Text) Render(w io.Writer, v interface{}) error { | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); ok { | ||||||
|  | 		c := hw.Header().Get(ContentType) | ||||||
|  | 		if c != "" { | ||||||
|  | 			t.Head.ContentType = c | ||||||
|  | 		} | ||||||
|  | 		t.Head.Write(hw) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	w.Write([]byte(v.(string))) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render an XML response.
 | ||||||
|  | func (x XML) Render(w io.Writer, v interface{}) error { | ||||||
|  | 	var result []byte | ||||||
|  | 	var err error | ||||||
|  | 
 | ||||||
|  | 	if x.Indent { | ||||||
|  | 		result, err = xml.MarshalIndent(v, "", "  ") | ||||||
|  | 		result = append(result, '\n') | ||||||
|  | 	} else { | ||||||
|  | 		result, err = xml.Marshal(v) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// XML marshaled fine, write out the result.
 | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); ok { | ||||||
|  | 		x.Head.Write(hw) | ||||||
|  | 	} | ||||||
|  | 	if len(x.Prefix) > 0 { | ||||||
|  | 		w.Write(x.Prefix) | ||||||
|  | 	} | ||||||
|  | 	w.Write(result) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								vendor/github.com/unrolled/render/fs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/unrolled/render/fs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | package render | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"path/filepath" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type FileSystem interface { | ||||||
|  | 	Walk(root string, walkFn filepath.WalkFunc) error | ||||||
|  | 	ReadFile(filename string) ([]byte, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type LocalFileSystem struct{} | ||||||
|  | 
 | ||||||
|  | func (LocalFileSystem) Walk(root string, walkFn filepath.WalkFunc) error { | ||||||
|  | 	return filepath.Walk(root, walkFn) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (LocalFileSystem) ReadFile(filename string) ([]byte, error) { | ||||||
|  | 	return ioutil.ReadFile(filename) | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								vendor/github.com/unrolled/render/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/unrolled/render/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | module github.com/unrolled/render | ||||||
|  | 
 | ||||||
|  | go 1.12 | ||||||
|  | 
 | ||||||
|  | require github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 | ||||||
							
								
								
									
										2
									
								
								vendor/github.com/unrolled/render/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/unrolled/render/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= | ||||||
|  | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= | ||||||
							
								
								
									
										21
									
								
								vendor/github.com/unrolled/render/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/unrolled/render/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | // +build go1.6
 | ||||||
|  | 
 | ||||||
|  | package render | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"html/template" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Included helper functions for use when rendering HTML.
 | ||||||
|  | var helperFuncs = template.FuncMap{ | ||||||
|  | 	"yield": func() (string, error) { | ||||||
|  | 		return "", fmt.Errorf("yield called with no layout defined") | ||||||
|  | 	}, | ||||||
|  | 	"partial": func() (string, error) { | ||||||
|  | 		return "", fmt.Errorf("block called with no layout defined") | ||||||
|  | 	}, | ||||||
|  | 	"current": func() (string, error) { | ||||||
|  | 		return "", nil | ||||||
|  | 	}, | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								vendor/github.com/unrolled/render/helpers_pre16.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/unrolled/render/helpers_pre16.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | // +build !go1.6
 | ||||||
|  | 
 | ||||||
|  | package render | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"html/template" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Included helper functions for use when rendering HTML.
 | ||||||
|  | var helperFuncs = template.FuncMap{ | ||||||
|  | 	"yield": func() (string, error) { | ||||||
|  | 		return "", fmt.Errorf("yield called with no layout defined") | ||||||
|  | 	}, | ||||||
|  | 	// `block` is deprecated! Use the `partial` below if you need this functionality still.
 | ||||||
|  | 	// Otherwise, checkout Go's `block` implementation introduced in 1.6
 | ||||||
|  | 	"block": func() (string, error) { | ||||||
|  | 		return "", fmt.Errorf("block called with no layout defined") | ||||||
|  | 	}, | ||||||
|  | 	"partial": func() (string, error) { | ||||||
|  | 		return "", fmt.Errorf("block called with no layout defined") | ||||||
|  | 	}, | ||||||
|  | 	"current": func() (string, error) { | ||||||
|  | 		return "", nil | ||||||
|  | 	}, | ||||||
|  | } | ||||||
							
								
								
									
										480
									
								
								vendor/github.com/unrolled/render/render.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										480
									
								
								vendor/github.com/unrolled/render/render.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,480 @@ | ||||||
|  | package render | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"html/template" | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	// ContentBinary header value for binary data.
 | ||||||
|  | 	ContentBinary = "application/octet-stream" | ||||||
|  | 	// ContentHTML header value for HTML data.
 | ||||||
|  | 	ContentHTML = "text/html" | ||||||
|  | 	// ContentJSON header value for JSON data.
 | ||||||
|  | 	ContentJSON = "application/json" | ||||||
|  | 	// ContentJSONP header value for JSONP data.
 | ||||||
|  | 	ContentJSONP = "application/javascript" | ||||||
|  | 	// ContentLength header constant.
 | ||||||
|  | 	ContentLength = "Content-Length" | ||||||
|  | 	// ContentText header value for Text data.
 | ||||||
|  | 	ContentText = "text/plain" | ||||||
|  | 	// ContentType header constant.
 | ||||||
|  | 	ContentType = "Content-Type" | ||||||
|  | 	// ContentXHTML header value for XHTML data.
 | ||||||
|  | 	ContentXHTML = "application/xhtml+xml" | ||||||
|  | 	// ContentXML header value for XML data.
 | ||||||
|  | 	ContentXML = "text/xml" | ||||||
|  | 	// Default character encoding.
 | ||||||
|  | 	defaultCharset = "UTF-8" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // helperFuncs had to be moved out. See helpers.go|helpers_pre16.go files.
 | ||||||
|  | 
 | ||||||
|  | // Delims represents a set of Left and Right delimiters for HTML template rendering.
 | ||||||
|  | type Delims struct { | ||||||
|  | 	// Left delimiter, defaults to {{.
 | ||||||
|  | 	Left string | ||||||
|  | 	// Right delimiter, defaults to }}.
 | ||||||
|  | 	Right string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Options is a struct for specifying configuration options for the render.Render object.
 | ||||||
|  | type Options struct { | ||||||
|  | 	// Directory to load templates. Default is "templates".
 | ||||||
|  | 	Directory string | ||||||
|  | 	// FileSystem to access files
 | ||||||
|  | 	FileSystem FileSystem | ||||||
|  | 	// Asset function to use in place of directory. Defaults to nil.
 | ||||||
|  | 	Asset func(name string) ([]byte, error) | ||||||
|  | 	// AssetNames function to use in place of directory. Defaults to nil.
 | ||||||
|  | 	AssetNames func() []string | ||||||
|  | 	// Layout template name. Will not render a layout if blank (""). Defaults to blank ("").
 | ||||||
|  | 	Layout string | ||||||
|  | 	// Extensions to parse template files from. Defaults to [".tmpl"].
 | ||||||
|  | 	Extensions []string | ||||||
|  | 	// Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Defaults to empty map.
 | ||||||
|  | 	Funcs []template.FuncMap | ||||||
|  | 	// Delims sets the action delimiters to the specified strings in the Delims struct.
 | ||||||
|  | 	Delims Delims | ||||||
|  | 	// Appends the given character set to the Content-Type header. Default is "UTF-8".
 | ||||||
|  | 	Charset string | ||||||
|  | 	// If DisableCharset is set to true, it will not append the above Charset value to the Content-Type header. Default is false.
 | ||||||
|  | 	DisableCharset bool | ||||||
|  | 	// Outputs human readable JSON.
 | ||||||
|  | 	IndentJSON bool | ||||||
|  | 	// Outputs human readable XML. Default is false.
 | ||||||
|  | 	IndentXML bool | ||||||
|  | 	// Prefixes the JSON output with the given bytes. Default is false.
 | ||||||
|  | 	PrefixJSON []byte | ||||||
|  | 	// Prefixes the XML output with the given bytes.
 | ||||||
|  | 	PrefixXML []byte | ||||||
|  | 	// Allows changing the binary content type.
 | ||||||
|  | 	BinaryContentType string | ||||||
|  | 	// Allows changing the HTML content type.
 | ||||||
|  | 	HTMLContentType string | ||||||
|  | 	// Allows changing the JSON content type.
 | ||||||
|  | 	JSONContentType string | ||||||
|  | 	// Allows changing the JSONP content type.
 | ||||||
|  | 	JSONPContentType string | ||||||
|  | 	// Allows changing the Text content type.
 | ||||||
|  | 	TextContentType string | ||||||
|  | 	// Allows changing the XML content type.
 | ||||||
|  | 	XMLContentType string | ||||||
|  | 	// If IsDevelopment is set to true, this will recompile the templates on every request. Default is false.
 | ||||||
|  | 	IsDevelopment bool | ||||||
|  | 	// Unescape HTML characters "&<>" to their original values. Default is false.
 | ||||||
|  | 	UnEscapeHTML bool | ||||||
|  | 	// Streams JSON responses instead of marshalling prior to sending. Default is false.
 | ||||||
|  | 	StreamingJSON bool | ||||||
|  | 	// Require that all partials executed in the layout are implemented in all templates using the layout. Default is false.
 | ||||||
|  | 	RequirePartials bool | ||||||
|  | 	// Deprecated: Use the above `RequirePartials` instead of this. As of Go 1.6, blocks are built in. Default is false.
 | ||||||
|  | 	RequireBlocks bool | ||||||
|  | 	// Disables automatic rendering of http.StatusInternalServerError when an error occurs. Default is false.
 | ||||||
|  | 	DisableHTTPErrorRendering bool | ||||||
|  | 	// Enables using partials without the current filename suffix which allows use of the same template in multiple files. e.g {{ partial "carosuel" }} inside the home template will match carosel-home or carosel.
 | ||||||
|  | 	// ***NOTE*** - This option should be named RenderPartialsWithoutSuffix as that is what it does. "Prefix" is a typo. Maintaining the existing name for backwards compatibility.
 | ||||||
|  | 	RenderPartialsWithoutPrefix bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // HTMLOptions is a struct for overriding some rendering Options for specific HTML call.
 | ||||||
|  | type HTMLOptions struct { | ||||||
|  | 	// Layout template name. Overrides Options.Layout.
 | ||||||
|  | 	Layout string | ||||||
|  | 	// Funcs added to Options.Funcs.
 | ||||||
|  | 	Funcs template.FuncMap | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render is a service that provides functions for easily writing JSON, XML,
 | ||||||
|  | // binary data, and HTML templates out to a HTTP Response.
 | ||||||
|  | type Render struct { | ||||||
|  | 	// Customize Secure with an Options struct.
 | ||||||
|  | 	opt             Options | ||||||
|  | 	templates       *template.Template | ||||||
|  | 	templatesLk     sync.Mutex | ||||||
|  | 	compiledCharset string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // New constructs a new Render instance with the supplied options.
 | ||||||
|  | func New(options ...Options) *Render { | ||||||
|  | 	var o Options | ||||||
|  | 	if len(options) > 0 { | ||||||
|  | 		o = options[0] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	r := Render{ | ||||||
|  | 		opt: o, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	r.prepareOptions() | ||||||
|  | 	r.compileTemplates() | ||||||
|  | 
 | ||||||
|  | 	return &r | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *Render) prepareOptions() { | ||||||
|  | 	// Fill in the defaults if need be.
 | ||||||
|  | 	if len(r.opt.Charset) == 0 { | ||||||
|  | 		r.opt.Charset = defaultCharset | ||||||
|  | 	} | ||||||
|  | 	if r.opt.DisableCharset == false { | ||||||
|  | 		r.compiledCharset = "; charset=" + r.opt.Charset | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(r.opt.Directory) == 0 { | ||||||
|  | 		r.opt.Directory = "templates" | ||||||
|  | 	} | ||||||
|  | 	if r.opt.FileSystem == nil { | ||||||
|  | 		r.opt.FileSystem = &LocalFileSystem{} | ||||||
|  | 	} | ||||||
|  | 	if len(r.opt.Extensions) == 0 { | ||||||
|  | 		r.opt.Extensions = []string{".tmpl"} | ||||||
|  | 	} | ||||||
|  | 	if len(r.opt.BinaryContentType) == 0 { | ||||||
|  | 		r.opt.BinaryContentType = ContentBinary | ||||||
|  | 	} | ||||||
|  | 	if len(r.opt.HTMLContentType) == 0 { | ||||||
|  | 		r.opt.HTMLContentType = ContentHTML | ||||||
|  | 	} | ||||||
|  | 	if len(r.opt.JSONContentType) == 0 { | ||||||
|  | 		r.opt.JSONContentType = ContentJSON | ||||||
|  | 	} | ||||||
|  | 	if len(r.opt.JSONPContentType) == 0 { | ||||||
|  | 		r.opt.JSONPContentType = ContentJSONP | ||||||
|  | 	} | ||||||
|  | 	if len(r.opt.TextContentType) == 0 { | ||||||
|  | 		r.opt.TextContentType = ContentText | ||||||
|  | 	} | ||||||
|  | 	if len(r.opt.XMLContentType) == 0 { | ||||||
|  | 		r.opt.XMLContentType = ContentXML | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *Render) compileTemplates() { | ||||||
|  | 	if r.opt.Asset == nil || r.opt.AssetNames == nil { | ||||||
|  | 		r.compileTemplatesFromDir() | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	r.compileTemplatesFromAsset() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *Render) compileTemplatesFromDir() { | ||||||
|  | 	dir := r.opt.Directory | ||||||
|  | 	r.templates = template.New(dir) | ||||||
|  | 	r.templates.Delims(r.opt.Delims.Left, r.opt.Delims.Right) | ||||||
|  | 
 | ||||||
|  | 	// Walk the supplied directory and compile any files that match our extension list.
 | ||||||
|  | 	r.opt.FileSystem.Walk(dir, func(path string, info os.FileInfo, err error) error { | ||||||
|  | 		// Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html".
 | ||||||
|  | 		// These dirs should be excluded as they are not valid golang templates, but files under
 | ||||||
|  | 		// them should be treat as normal.
 | ||||||
|  | 		// If is a dir, return immediately (dir is not a valid golang template).
 | ||||||
|  | 		if info == nil || info.IsDir() { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		rel, err := filepath.Rel(dir, path) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ext := "" | ||||||
|  | 		if strings.Index(rel, ".") != -1 { | ||||||
|  | 			ext = filepath.Ext(rel) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, extension := range r.opt.Extensions { | ||||||
|  | 			if ext == extension { | ||||||
|  | 				buf, err := r.opt.FileSystem.ReadFile(path) | ||||||
|  | 				if err != nil { | ||||||
|  | 					panic(err) | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				name := (rel[0 : len(rel)-len(ext)]) | ||||||
|  | 				tmpl := r.templates.New(filepath.ToSlash(name)) | ||||||
|  | 
 | ||||||
|  | 				// Add our funcmaps.
 | ||||||
|  | 				for _, funcs := range r.opt.Funcs { | ||||||
|  | 					tmpl.Funcs(funcs) | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// Break out if this parsing fails. We don't want any silent server starts.
 | ||||||
|  | 				template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf))) | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *Render) compileTemplatesFromAsset() { | ||||||
|  | 	dir := r.opt.Directory | ||||||
|  | 	r.templates = template.New(dir) | ||||||
|  | 	r.templates.Delims(r.opt.Delims.Left, r.opt.Delims.Right) | ||||||
|  | 
 | ||||||
|  | 	for _, path := range r.opt.AssetNames() { | ||||||
|  | 		if !strings.HasPrefix(path, dir) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		rel, err := filepath.Rel(dir, path) | ||||||
|  | 		if err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ext := "" | ||||||
|  | 		if strings.Index(rel, ".") != -1 { | ||||||
|  | 			ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".") | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, extension := range r.opt.Extensions { | ||||||
|  | 			if ext == extension { | ||||||
|  | 
 | ||||||
|  | 				buf, err := r.opt.Asset(path) | ||||||
|  | 				if err != nil { | ||||||
|  | 					panic(err) | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				name := (rel[0 : len(rel)-len(ext)]) | ||||||
|  | 				tmpl := r.templates.New(filepath.ToSlash(name)) | ||||||
|  | 
 | ||||||
|  | 				// Add our funcmaps.
 | ||||||
|  | 				for _, funcs := range r.opt.Funcs { | ||||||
|  | 					tmpl.Funcs(funcs) | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// Break out if this parsing fails. We don't want any silent server starts.
 | ||||||
|  | 				template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf))) | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TemplateLookup is a wrapper around template.Lookup and returns
 | ||||||
|  | // the template with the given name that is associated with t, or nil
 | ||||||
|  | // if there is no such template.
 | ||||||
|  | func (r *Render) TemplateLookup(t string) *template.Template { | ||||||
|  | 	return r.templates.Lookup(t) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *Render) execute(name string, binding interface{}) (*bytes.Buffer, error) { | ||||||
|  | 	buf := new(bytes.Buffer) | ||||||
|  | 	return buf, r.templates.ExecuteTemplate(buf, name, binding) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *Render) layoutFuncs(name string, binding interface{}) template.FuncMap { | ||||||
|  | 	return template.FuncMap{ | ||||||
|  | 		"yield": func() (template.HTML, error) { | ||||||
|  | 			buf, err := r.execute(name, binding) | ||||||
|  | 			// Return safe HTML here since we are rendering our own template.
 | ||||||
|  | 			return template.HTML(buf.String()), err | ||||||
|  | 		}, | ||||||
|  | 		"current": func() (string, error) { | ||||||
|  | 			return name, nil | ||||||
|  | 		}, | ||||||
|  | 		"block": func(partialName string) (template.HTML, error) { | ||||||
|  | 			log.Print("Render's `block` implementation is now depericated. Use `partial` as a drop in replacement.") | ||||||
|  | 			fullPartialName := fmt.Sprintf("%s-%s", partialName, name) | ||||||
|  | 			if r.TemplateLookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix { | ||||||
|  | 				fullPartialName = partialName | ||||||
|  | 			} | ||||||
|  | 			if r.opt.RequireBlocks || r.TemplateLookup(fullPartialName) != nil { | ||||||
|  | 				buf, err := r.execute(fullPartialName, binding) | ||||||
|  | 				// Return safe HTML here since we are rendering our own template.
 | ||||||
|  | 				return template.HTML(buf.String()), err | ||||||
|  | 			} | ||||||
|  | 			return "", nil | ||||||
|  | 		}, | ||||||
|  | 		"partial": func(partialName string) (template.HTML, error) { | ||||||
|  | 			fullPartialName := fmt.Sprintf("%s-%s", partialName, name) | ||||||
|  | 			if r.TemplateLookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix { | ||||||
|  | 				fullPartialName = partialName | ||||||
|  | 			} | ||||||
|  | 			if r.opt.RequirePartials || r.TemplateLookup(fullPartialName) != nil { | ||||||
|  | 				buf, err := r.execute(fullPartialName, binding) | ||||||
|  | 				// Return safe HTML here since we are rendering our own template.
 | ||||||
|  | 				return template.HTML(buf.String()), err | ||||||
|  | 			} | ||||||
|  | 			return "", nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *Render) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions { | ||||||
|  | 	layout := r.opt.Layout | ||||||
|  | 	funcs := template.FuncMap{} | ||||||
|  | 
 | ||||||
|  | 	for _, tmp := range r.opt.Funcs { | ||||||
|  | 		for k, v := range tmp { | ||||||
|  | 			funcs[k] = v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(htmlOpt) > 0 { | ||||||
|  | 		opt := htmlOpt[0] | ||||||
|  | 		if len(opt.Layout) > 0 { | ||||||
|  | 			layout = opt.Layout | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for k, v := range opt.Funcs { | ||||||
|  | 			funcs[k] = v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return HTMLOptions{ | ||||||
|  | 		Layout: layout, | ||||||
|  | 		Funcs:  funcs, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Render is the generic function called by XML, JSON, Data, HTML, and can be called by custom implementations.
 | ||||||
|  | func (r *Render) Render(w io.Writer, e Engine, data interface{}) error { | ||||||
|  | 	err := e.Render(w, data) | ||||||
|  | 	if hw, ok := w.(http.ResponseWriter); err != nil && !r.opt.DisableHTTPErrorRendering && ok { | ||||||
|  | 		http.Error(hw, err.Error(), http.StatusInternalServerError) | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Data writes out the raw bytes as binary data.
 | ||||||
|  | func (r *Render) Data(w io.Writer, status int, v []byte) error { | ||||||
|  | 	head := Head{ | ||||||
|  | 		ContentType: r.opt.BinaryContentType, | ||||||
|  | 		Status:      status, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	d := Data{ | ||||||
|  | 		Head: head, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return r.Render(w, d, v) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // HTML builds up the response from the specified template and bindings.
 | ||||||
|  | func (r *Render) HTML(w io.Writer, status int, name string, binding interface{}, htmlOpt ...HTMLOptions) error { | ||||||
|  | 	r.templatesLk.Lock() | ||||||
|  | 	defer r.templatesLk.Unlock() | ||||||
|  | 
 | ||||||
|  | 	// If we are in development mode, recompile the templates on every HTML request.
 | ||||||
|  | 	if r.opt.IsDevelopment { | ||||||
|  | 		r.compileTemplates() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	opt := r.prepareHTMLOptions(htmlOpt) | ||||||
|  | 	if tpl := r.templates.Lookup(name); tpl != nil { | ||||||
|  | 		if len(opt.Layout) > 0 { | ||||||
|  | 			tpl.Funcs(r.layoutFuncs(name, binding)) | ||||||
|  | 			name = opt.Layout | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if len(opt.Funcs) > 0 { | ||||||
|  | 			tpl.Funcs(opt.Funcs) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	head := Head{ | ||||||
|  | 		ContentType: r.opt.HTMLContentType + r.compiledCharset, | ||||||
|  | 		Status:      status, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	h := HTML{ | ||||||
|  | 		Head:      head, | ||||||
|  | 		Name:      name, | ||||||
|  | 		Templates: r.templates, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return r.Render(w, h, binding) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // JSON marshals the given interface object and writes the JSON response.
 | ||||||
|  | func (r *Render) JSON(w io.Writer, status int, v interface{}) error { | ||||||
|  | 	head := Head{ | ||||||
|  | 		ContentType: r.opt.JSONContentType + r.compiledCharset, | ||||||
|  | 		Status:      status, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	j := JSON{ | ||||||
|  | 		Head:          head, | ||||||
|  | 		Indent:        r.opt.IndentJSON, | ||||||
|  | 		Prefix:        r.opt.PrefixJSON, | ||||||
|  | 		UnEscapeHTML:  r.opt.UnEscapeHTML, | ||||||
|  | 		StreamingJSON: r.opt.StreamingJSON, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return r.Render(w, j, v) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // JSONP marshals the given interface object and writes the JSON response.
 | ||||||
|  | func (r *Render) JSONP(w io.Writer, status int, callback string, v interface{}) error { | ||||||
|  | 	head := Head{ | ||||||
|  | 		ContentType: r.opt.JSONPContentType + r.compiledCharset, | ||||||
|  | 		Status:      status, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	j := JSONP{ | ||||||
|  | 		Head:     head, | ||||||
|  | 		Indent:   r.opt.IndentJSON, | ||||||
|  | 		Callback: callback, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return r.Render(w, j, v) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Text writes out a string as plain text.
 | ||||||
|  | func (r *Render) Text(w io.Writer, status int, v string) error { | ||||||
|  | 	head := Head{ | ||||||
|  | 		ContentType: r.opt.TextContentType + r.compiledCharset, | ||||||
|  | 		Status:      status, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t := Text{ | ||||||
|  | 		Head: head, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return r.Render(w, t, v) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // XML marshals the given interface object and writes the XML response.
 | ||||||
|  | func (r *Render) XML(w io.Writer, status int, v interface{}) error { | ||||||
|  | 	head := Head{ | ||||||
|  | 		ContentType: r.opt.XMLContentType + r.compiledCharset, | ||||||
|  | 		Status:      status, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	x := XML{ | ||||||
|  | 		Head:   head, | ||||||
|  | 		Indent: r.opt.IndentXML, | ||||||
|  | 		Prefix: r.opt.PrefixXML, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return r.Render(w, x, v) | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							|  | @ -7,6 +7,13 @@ code.gitea.io/gitea-vet/checks | ||||||
| # code.gitea.io/sdk/gitea v0.13.1 | # code.gitea.io/sdk/gitea v0.13.1 | ||||||
| ## explicit | ## explicit | ||||||
| code.gitea.io/sdk/gitea | code.gitea.io/sdk/gitea | ||||||
|  | # gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27 | ||||||
|  | ## explicit | ||||||
|  | gitea.com/go-chi/session | ||||||
|  | gitea.com/go-chi/session/couchbase | ||||||
|  | gitea.com/go-chi/session/memcache | ||||||
|  | gitea.com/go-chi/session/mysql | ||||||
|  | gitea.com/go-chi/session/postgres | ||||||
| # gitea.com/lunny/levelqueue v0.3.0 | # gitea.com/lunny/levelqueue v0.3.0 | ||||||
| ## explicit | ## explicit | ||||||
| gitea.com/lunny/levelqueue | gitea.com/lunny/levelqueue | ||||||
|  | @ -188,7 +195,7 @@ github.com/cespare/xxhash/v2 | ||||||
| github.com/chris-ramon/douceur/parser | github.com/chris-ramon/douceur/parser | ||||||
| # github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 | # github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 | ||||||
| github.com/couchbase/go-couchbase | github.com/couchbase/go-couchbase | ||||||
| # github.com/couchbase/gomemcached v0.1.0 | # github.com/couchbase/gomemcached v0.1.1 | ||||||
| github.com/couchbase/gomemcached | github.com/couchbase/gomemcached | ||||||
| github.com/couchbase/gomemcached/client | github.com/couchbase/gomemcached/client | ||||||
| # github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 | # github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 | ||||||
|  | @ -257,7 +264,7 @@ github.com/gliderlabs/ssh | ||||||
| github.com/glycerine/go-unsnap-stream | github.com/glycerine/go-unsnap-stream | ||||||
| # github.com/go-asn1-ber/asn1-ber v1.5.1 | # github.com/go-asn1-ber/asn1-ber v1.5.1 | ||||||
| github.com/go-asn1-ber/asn1-ber | github.com/go-asn1-ber/asn1-ber | ||||||
| # github.com/go-chi/chi v1.5.0 | # github.com/go-chi/chi v1.5.1 | ||||||
| ## explicit | ## explicit | ||||||
| github.com/go-chi/chi | github.com/go-chi/chi | ||||||
| github.com/go-chi/chi/middleware | github.com/go-chi/chi/middleware | ||||||
|  | @ -630,8 +637,6 @@ github.com/oliamb/cutter | ||||||
| github.com/olivere/elastic/v7 | github.com/olivere/elastic/v7 | ||||||
| github.com/olivere/elastic/v7/config | github.com/olivere/elastic/v7/config | ||||||
| github.com/olivere/elastic/v7/uritemplates | github.com/olivere/elastic/v7/uritemplates | ||||||
| # github.com/onsi/ginkgo v1.13.0 |  | ||||||
| ## explicit |  | ||||||
| # github.com/pelletier/go-toml v1.8.1 | # github.com/pelletier/go-toml v1.8.1 | ||||||
| ## explicit | ## explicit | ||||||
| github.com/pelletier/go-toml | github.com/pelletier/go-toml | ||||||
|  | @ -750,6 +755,9 @@ github.com/unknwon/i18n | ||||||
| # github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae | # github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae | ||||||
| ## explicit | ## explicit | ||||||
| github.com/unknwon/paginater | github.com/unknwon/paginater | ||||||
|  | # github.com/unrolled/render v1.0.3 | ||||||
|  | ## explicit | ||||||
|  | github.com/unrolled/render | ||||||
| # github.com/urfave/cli v1.22.5 | # github.com/urfave/cli v1.22.5 | ||||||
| ## explicit | ## explicit | ||||||
| github.com/urfave/cli | github.com/urfave/cli | ||||||
|  | @ -916,7 +924,6 @@ golang.org/x/tools/internal/typesinternal | ||||||
| golang.org/x/xerrors | golang.org/x/xerrors | ||||||
| golang.org/x/xerrors/internal | golang.org/x/xerrors/internal | ||||||
| # google.golang.org/appengine v1.6.7 | # google.golang.org/appengine v1.6.7 | ||||||
| ## explicit |  | ||||||
| google.golang.org/appengine | google.golang.org/appengine | ||||||
| google.golang.org/appengine/internal | google.golang.org/appengine/internal | ||||||
| google.golang.org/appengine/internal/app_identity | google.golang.org/appengine/internal/app_identity | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue