diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2021-01-05 21:05:40 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-05 21:05:40 +0800 |
commit | 15a475b7dbcf7923d9518dff7764b20e404eb774 (patch) | |
tree | 8789f1f82c5e41345b442df4e58120bdd5f8bade /vendor | |
parent | 126c9331d6d8789563fae5d5bac2196d63fee0e8 (diff) | |
download | gitea-15a475b7dbcf7923d9518dff7764b20e404eb774.tar.gz gitea-15a475b7dbcf7923d9518dff7764b20e404eb774.zip |
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>
Diffstat (limited to 'vendor')
51 files changed, 4198 insertions, 123 deletions
diff --git a/vendor/gitea.com/go-chi/session/.drone.yml b/vendor/gitea.com/go-chi/session/.drone.yml new file mode 100644 index 0000000000..d941e57505 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/.drone.yml @@ -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
\ No newline at end of file diff --git a/vendor/gitea.com/go-chi/session/.gitignore b/vendor/gitea.com/go-chi/session/.gitignore new file mode 100644 index 0000000000..834722925c --- /dev/null +++ b/vendor/gitea.com/go-chi/session/.gitignore @@ -0,0 +1,4 @@ +ledis/tmp.db +nodb/tmp.db +/vendor +/.idea diff --git a/vendor/gitea.com/go-chi/session/LICENSE b/vendor/gitea.com/go-chi/session/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/go-chi/session/LICENSE @@ -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.
\ No newline at end of file diff --git a/vendor/gitea.com/go-chi/session/README.md b/vendor/gitea.com/go-chi/session/README.md new file mode 100644 index 0000000000..ed3258fcef --- /dev/null +++ b/vendor/gitea.com/go-chi/session/README.md @@ -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.
\ No newline at end of file diff --git a/vendor/gitea.com/go-chi/session/couchbase/couchbase.go b/vendor/gitea.com/go-chi/session/couchbase/couchbase.go new file mode 100644 index 0000000000..31899a8574 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/couchbase/couchbase.go @@ -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{}) +} diff --git a/vendor/gitea.com/go-chi/session/file.go b/vendor/gitea.com/go-chi/session/file.go new file mode 100644 index 0000000000..ce915344fb --- /dev/null +++ b/vendor/gitea.com/go-chi/session/file.go @@ -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{}) +} diff --git a/vendor/gitea.com/go-chi/session/go.mod b/vendor/gitea.com/go-chi/session/go.mod new file mode 100644 index 0000000000..6612080047 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/go.mod @@ -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 +) diff --git a/vendor/gitea.com/go-chi/session/go.sum b/vendor/gitea.com/go-chi/session/go.sum new file mode 100644 index 0000000000..e2c174b0c7 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/go.sum @@ -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= diff --git a/vendor/gitea.com/go-chi/session/memcache/memcache.go b/vendor/gitea.com/go-chi/session/memcache/memcache.go new file mode 100644 index 0000000000..ed08b915df --- /dev/null +++ b/vendor/gitea.com/go-chi/session/memcache/memcache.go @@ -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{}) +} diff --git a/vendor/gitea.com/go-chi/session/memcache/memcache.goconvey b/vendor/gitea.com/go-chi/session/memcache/memcache.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/memcache/memcache.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/go-chi/session/memory.go b/vendor/gitea.com/go-chi/session/memory.go new file mode 100644 index 0000000000..d3a623c672 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/memory.go @@ -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{})
+}
diff --git a/vendor/gitea.com/go-chi/session/mysql/mysql.go b/vendor/gitea.com/go-chi/session/mysql/mysql.go new file mode 100644 index 0000000000..7ec647bf3a --- /dev/null +++ b/vendor/gitea.com/go-chi/session/mysql/mysql.go @@ -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{}) +} diff --git a/vendor/gitea.com/go-chi/session/mysql/mysql.goconvey b/vendor/gitea.com/go-chi/session/mysql/mysql.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/mysql/mysql.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/go-chi/session/postgres/postgres.go b/vendor/gitea.com/go-chi/session/postgres/postgres.go new file mode 100644 index 0000000000..383906d8d7 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/postgres/postgres.go @@ -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{}) +} diff --git a/vendor/gitea.com/go-chi/session/postgres/postgres.goconvey b/vendor/gitea.com/go-chi/session/postgres/postgres.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/postgres/postgres.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/go-chi/session/secret.go b/vendor/gitea.com/go-chi/session/secret.go new file mode 100644 index 0000000000..e56a50615b --- /dev/null +++ b/vendor/gitea.com/go-chi/session/secret.go @@ -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 +} diff --git a/vendor/gitea.com/go-chi/session/session.go b/vendor/gitea.com/go-chi/session/session.go new file mode 100644 index 0000000000..97eb5ad365 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/session.go @@ -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 +} diff --git a/vendor/gitea.com/go-chi/session/utils.go b/vendor/gitea.com/go-chi/session/utils.go new file mode 100644 index 0000000000..001bae6d98 --- /dev/null +++ b/vendor/gitea.com/go-chi/session/utils.go @@ -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 +} diff --git a/vendor/github.com/couchbase/gomemcached/.gitignore b/vendor/github.com/couchbase/gomemcached/.gitignore index f75d85a841..cd8acba17e 100644 --- a/vendor/github.com/couchbase/gomemcached/.gitignore +++ b/vendor/github.com/couchbase/gomemcached/.gitignore @@ -4,3 +4,4 @@ *.swp /gocache/gocache c.out +.idea
\ No newline at end of file diff --git a/vendor/github.com/couchbase/gomemcached/client/collections_filter.go b/vendor/github.com/couchbase/gomemcached/client/collections_filter.go index 4da8b8f426..a34d353fec 100644 --- a/vendor/github.com/couchbase/gomemcached/client/collections_filter.go +++ b/vendor/github.com/couchbase/gomemcached/client/collections_filter.go @@ -17,11 +17,7 @@ type CollectionsFilter struct { ScopeId uint32 } -type nonStreamIdNonResumeScopeMeta struct { - ScopeId string `json:"scope"` -} - -type nonStreamIdResumeScopeMeta struct { +type nonStreamIdNonCollectionsMeta struct { ManifestId string `json:"uid"` } @@ -29,7 +25,7 @@ type nonStreamIdNonResumeCollectionsMeta struct { CollectionsList []string `json:"collections"` } -type nonStreamIdResumeCollectionsMeta struct { +type nonStreamIdCollectionsMeta struct { ManifestId string `json:"uid"` CollectionsList []string `json:"collections"` } @@ -99,10 +95,19 @@ func (c *CollectionsFilter) ToStreamReqBody() ([]byte, error) { case false: switch c.UseManifestUid { case true: - filter := &nonStreamIdResumeScopeMeta{ - ManifestId: fmt.Sprintf("%x", c.ManifestUid), + switch len(c.CollectionsList) > 0 { + 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: switch len(c.CollectionsList) > 0 { case true: @@ -111,7 +116,7 @@ func (c *CollectionsFilter) ToStreamReqBody() ([]byte, error) { } output = *filter case false: - output = nonStreamIdNonResumeScopeMeta{ScopeId: c.outputScopeId()} + return nil, fmt.Errorf("Specifying scopeID must require the use of streamId") } } } diff --git a/vendor/github.com/couchbase/gomemcached/client/mc.go b/vendor/github.com/couchbase/gomemcached/client/mc.go index 16dd2f8f7c..208bacdd98 100644 --- a/vendor/github.com/couchbase/gomemcached/client/mc.go +++ b/vendor/github.com/couchbase/gomemcached/client/mc.go @@ -375,6 +375,25 @@ func (c *Client) setCollection(req *gomemcached.MCRequest, context ...*ClientCon 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 { if len(context) == 0 || req == nil { return nil @@ -516,9 +535,14 @@ func (c *Client) Del(vb uint16, key string, context ...*ClientContext) (*gomemca // Get a random document func (c *Client) GetRandomDoc(context ...*ClientContext) (*gomemcached.MCResponse, error) { - return c.Send(&gomemcached.MCRequest{ + req := &gomemcached.MCRequest{ Opcode: 0xB6, - }) + } + err := c.setExtrasCollection(req, context...) + if err != nil { + return nil, err + } + return c.Send(req) } // AuthList lists SASL auth mechanisms. diff --git a/vendor/github.com/couchbase/gomemcached/client/upr_event.go b/vendor/github.com/couchbase/gomemcached/client/upr_event.go index 7ede0a128d..2d3454aecc 100644 --- a/vendor/github.com/couchbase/gomemcached/client/upr_event.go +++ b/vendor/github.com/couchbase/gomemcached/client/upr_event.go @@ -83,7 +83,8 @@ type UprEvent struct { SystemEvent SystemEventType // Only valid if IsSystemEvent() is true SysEventVersion uint8 // Based on the version, the way Extra bytes is parsed is different 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 @@ -103,7 +104,7 @@ func makeUprEvent(rq gomemcached.MCRequest, stream *UprStream, bytesReceivedFrom DataType: rq.DataType, ValueLen: len(rq.Body), SystemEvent: InvalidSysEvent, - CollectionId: math.MaxUint64, + CollectionId: math.MaxUint32, } event.PopulateFieldsBasedOnStreamType(rq, stream.StreamType) @@ -153,6 +154,8 @@ func makeUprEvent(rq gomemcached.MCRequest, stream *UprStream, bytesReceivedFrom event.PopulateEvent(rq.Extras) } else if event.IsSeqnoAdv() { event.PopulateSeqnoAdv(rq.Extras) + } else if event.IsOsoSnapshot() { + event.PopulateOso(rq.Extras) } return event @@ -160,6 +163,15 @@ func makeUprEvent(rq gomemcached.MCRequest, stream *UprStream, bytesReceivedFrom func (event *UprEvent) PopulateFieldsBasedOnStreamType(rq gomemcached.MCRequest, streamType DcpStreamType) { 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: switch rq.Opcode { // 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_EXPIRATION: uleb128 := Uleb128(rq.Key) - result, bytesShifted := uleb128.ToUint64(rq.Keylen) + result, bytesShifted := uleb128.ToUint32(rq.Keylen) event.CollectionId = result event.Key = rq.Key[bytesShifted:] default: event.Key = rq.Key } - case CollectionsStreamId: - // TODO - not implemented - fallthrough case NonCollectionStream: // Let default behavior be legacy stream type fallthrough @@ -208,6 +217,10 @@ func (event *UprEvent) IsSeqnoAdv() bool { 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) { if len(extras) < dcpSystemEventExtraLen { // Wrong length, don't parse @@ -229,6 +242,14 @@ func (event *UprEvent) PopulateSeqnoAdv(extras []byte) { 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) { switch event.SystemEvent { 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 -func (u Uleb128) ToUint64(cachedLen int) (result uint64, bytesShifted int) { +func (u Uleb128) ToUint32(cachedLen int) (result uint32, bytesShifted int) { var shift uint = 0 for curByte := 0; curByte < cachedLen; curByte++ { oneByte := u[curByte] last7Bits := 0x7f & oneByte - result |= uint64(last7Bits) << shift + result |= uint32(last7Bits) << shift bytesShifted++ if oneByte&0x80 == 0 { break diff --git a/vendor/github.com/couchbase/gomemcached/client/upr_feed.go b/vendor/github.com/couchbase/gomemcached/client/upr_feed.go index be676aa71c..cdbed16bd3 100644 --- a/vendor/github.com/couchbase/gomemcached/client/upr_feed.go +++ b/vendor/github.com/couchbase/gomemcached/client/upr_feed.go @@ -26,6 +26,7 @@ const opaqueOpen = 0xBEAF0001 const opaqueFailover = 0xDEADBEEF const opaqueGetSeqno = 0xDEADBEEF const uprDefaultNoopInterval = 120 +const dcpOsoExtraLen = 4 // Counter on top of opaqueOpen that others can draw from for open and control msgs var opaqueOpenCtrlWell uint32 = opaqueOpen @@ -117,6 +118,7 @@ type UprFeatures struct { DcpPriority PriorityType EnableExpiry bool EnableStreamId bool + EnableOso bool } /** @@ -601,6 +603,20 @@ func (feed *UprFeed) uprOpen(name string, sequence uint32, bufSize uint32, featu 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 feed.activatedFeatures = activatedFeatures feed.setOpen() @@ -976,6 +992,12 @@ loop: break loop } 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: logging.Infof("Recived an unknown response for vbucket %d", vb) } diff --git a/vendor/github.com/couchbase/gomemcached/go.mod b/vendor/github.com/couchbase/gomemcached/go.mod new file mode 100644 index 0000000000..3355d4ea75 --- /dev/null +++ b/vendor/github.com/couchbase/gomemcached/go.mod @@ -0,0 +1,3 @@ +module github.com/couchbase/gomemcached + +go 1.13 diff --git a/vendor/github.com/couchbase/gomemcached/mc_constants.go b/vendor/github.com/couchbase/gomemcached/mc_constants.go index 1dfe2febf2..19741f5a0d 100644 --- a/vendor/github.com/couchbase/gomemcached/mc_constants.go +++ b/vendor/github.com/couchbase/gomemcached/mc_constants.go @@ -104,6 +104,7 @@ const ( 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_OSO_SNAPSHOT = CommandCode(0x65) // Marks the begin and end of out-of-sequence-number stream ) // command codes that are counted toward DCP control buffer @@ -117,6 +118,7 @@ var BufferedCommandCodeMap = map[CommandCode]bool{ UPR_EXPIRATION: true, DCP_SYSTEM_EVENT: true, DCP_SEQNO_ADV: true, + DCP_OSO_SNAPSHOT: true, } // Status field for memcached response. @@ -156,6 +158,9 @@ const ( SUBDOC_PATH_NOT_FOUND = Status(0xc0) SUBDOC_BAD_MULTI = Status(0xcc) SUBDOC_MULTI_PATH_FAILURE_DELETED = Status(0xd3) + + // Not a Memcached status + UNKNOWN_STATUS = Status(0xffff) ) // for log redaction @@ -174,6 +179,10 @@ var isFatal = map[Status]bool{ EACCESS: true, ENOMEM: 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 diff --git a/vendor/github.com/couchbase/gomemcached/mc_res.go b/vendor/github.com/couchbase/gomemcached/mc_res.go index f6be989847..1e89020de2 100644 --- a/vendor/github.com/couchbase/gomemcached/mc_res.go +++ b/vendor/github.com/couchbase/gomemcached/mc_res.go @@ -38,7 +38,7 @@ func (res *MCResponse) Error() string { } func errStatus(e error) Status { - status := Status(0xffff) + status := UNKNOWN_STATUS if res, ok := e.(*MCResponse); ok { status = res.Status } diff --git a/vendor/github.com/go-chi/chi/.travis.yml b/vendor/github.com/go-chi/chi/.travis.yml deleted file mode 100644 index 7b8e26bcee..0000000000 --- a/vendor/github.com/go-chi/chi/.travis.yml +++ /dev/null @@ -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 - diff --git a/vendor/github.com/go-chi/chi/CHANGELOG.md b/vendor/github.com/go-chi/chi/CHANGELOG.md index 9a64a72eec..2e337506a4 100644 --- a/vendor/github.com/go-chi/chi/CHANGELOG.md +++ b/vendor/github.com/go-chi/chi/CHANGELOG.md @@ -1,5 +1,66 @@ # 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) - fix that handles MethodNotAllowed with path variables, thank you @caseyhadden for your contribution @@ -23,7 +84,6 @@ - middleware.Recoverer: a bit prettier - History of changes: see https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0 - ## v4.0.4 (2020-03-24) - middleware.Recoverer: new pretty stack trace printing (https://github.com/go-chi/chi/pull/496) diff --git a/vendor/github.com/go-chi/chi/README.md b/vendor/github.com/go-chi/chi/README.md index 3d53a61476..1b96d360d7 100644 --- a/vendor/github.com/go-chi/chi/README.md +++ b/vendor/github.com/go-chi/chi/README.md @@ -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 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 -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 @@ -27,10 +28,11 @@ included some useful/optional subpackages: [middleware](/middleware), [render](h * **Lightweight** - cloc'd in ~1000 LOC for the chi router * **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` -* **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 * **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 +* **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 @@ -334,9 +336,12 @@ with `net/http` can be used with chi's mux. ---------------------------------------------------------------------------------------------------- | chi/middleware Handler | description | | :--------------------- | :---------------------------------------------------------------------- | +| [AllowContentEncoding] | Enforces a whitelist of request Content-Encoding headers | | [AllowContentType] | Explicit whitelist of accepted request Content-Types | | [BasicAuth] | Basic HTTP authentication | | [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 | | [Heartbeat] | Monitoring endpoint to check the servers pulse | | [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 | | [RequestID] | Injects a request ID into the context of each request | | [RedirectSlashes] | Redirect slashes on routing paths | +| [RouteHeaders] | Route handling for request headers | | [SetHeader] | Short-hand middleware to set a response header key/value | | [StripSlashes] | Strip slashes on routing paths | | [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 [Compress]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compress [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 [GetReqID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetReqID [Heartbeat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Heartbeat [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 -[PrintPrettyStack]: https://pkg.go.dev/github.com/go-chi/chi/middleware#PrintPrettyStack [Profiler]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Profiler [RealIP]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RealIP [Recoverer]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Recoverer [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 +[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 [StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes [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 [LogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogFormatter [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 [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 -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 -BenchmarkChi_Param 3000000 475 ns/op 432 B/op 3 allocs/op -BenchmarkChi_Param5 2000000 696 ns/op 432 B/op 3 allocs/op -BenchmarkChi_Param20 1000000 1275 ns/op 432 B/op 3 allocs/op -BenchmarkChi_ParamWrite 3000000 505 ns/op 432 B/op 3 allocs/op -BenchmarkChi_GithubStatic 3000000 508 ns/op 432 B/op 3 allocs/op -BenchmarkChi_GithubParam 2000000 669 ns/op 432 B/op 3 allocs/op -BenchmarkChi_GithubAll 10000 134627 ns/op 87699 B/op 609 allocs/op -BenchmarkChi_GPlusStatic 3000000 402 ns/op 432 B/op 3 allocs/op -BenchmarkChi_GPlusParam 3000000 500 ns/op 432 B/op 3 allocs/op -BenchmarkChi_GPlus2Params 3000000 586 ns/op 432 B/op 3 allocs/op -BenchmarkChi_GPlusAll 200000 7237 ns/op 5616 B/op 39 allocs/op -BenchmarkChi_ParseStatic 3000000 408 ns/op 432 B/op 3 allocs/op -BenchmarkChi_ParseParam 3000000 488 ns/op 432 B/op 3 allocs/op -BenchmarkChi_Parse2Params 3000000 551 ns/op 432 B/op 3 allocs/op -BenchmarkChi_ParseAll 100000 13508 ns/op 11232 B/op 78 allocs/op -BenchmarkChi_StaticAll 20000 81933 ns/op 67826 B/op 471 allocs/op +BenchmarkChi_Param 3075895 384 ns/op 400 B/op 2 allocs/op +BenchmarkChi_Param5 2116603 566 ns/op 400 B/op 2 allocs/op +BenchmarkChi_Param20 964117 1227 ns/op 400 B/op 2 allocs/op +BenchmarkChi_ParamWrite 2863413 420 ns/op 400 B/op 2 allocs/op +BenchmarkChi_GithubStatic 3045488 395 ns/op 400 B/op 2 allocs/op +BenchmarkChi_GithubParam 2204115 540 ns/op 400 B/op 2 allocs/op +BenchmarkChi_GithubAll 10000 113811 ns/op 81203 B/op 406 allocs/op +BenchmarkChi_GPlusStatic 3337485 359 ns/op 400 B/op 2 allocs/op +BenchmarkChi_GPlusParam 2825853 423 ns/op 400 B/op 2 allocs/op +BenchmarkChi_GPlus2Params 2471697 483 ns/op 400 B/op 2 allocs/op +BenchmarkChi_GPlusAll 194220 5950 ns/op 5200 B/op 26 allocs/op +BenchmarkChi_ParseStatic 3365324 356 ns/op 400 B/op 2 allocs/op +BenchmarkChi_ParseParam 2976614 404 ns/op 400 B/op 2 allocs/op +BenchmarkChi_Parse2Params 2638084 439 ns/op 400 B/op 2 allocs/op +BenchmarkChi_ParseAll 109567 11295 ns/op 10400 B/op 52 allocs/op +BenchmarkChi_StaticAll 16846 71308 ns/op 62802 B/op 314 allocs/op ``` 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. +## 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 * Carl Jackson for https://github.com/zenazn/goji diff --git a/vendor/github.com/go-chi/chi/context.go b/vendor/github.com/go-chi/chi/context.go index 26c609ea2c..7dec3f0c01 100644 --- a/vendor/github.com/go-chi/chi/context.go +++ b/vendor/github.com/go-chi/chi/context.go @@ -2,9 +2,9 @@ package chi import ( "context" - "net" "net/http" "strings" + "time" ) // URLParam returns the url parameter from a http.Request object. @@ -30,26 +30,6 @@ func RouteContext(ctx context.Context) *Context { return val } -// ServerBaseContext wraps an http.Handler to set the request context to the -// `baseCtx`. -func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler { - fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - baseCtx := baseCtx - - // Copy over default net/http server context keys - if v, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok { - baseCtx = context.WithValue(baseCtx, http.ServerContextKey, v) - } - if v, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok { - baseCtx = context.WithValue(baseCtx, http.LocalAddrContextKey, v) - } - - h.ServeHTTP(w, r.WithContext(baseCtx)) - }) - return fn -} - // NewRouteContext returns a new routing Context object. func NewRouteContext() *Context { return &Context{} @@ -92,6 +72,11 @@ type Context struct { // methodNotAllowed hint 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. @@ -107,6 +92,7 @@ func (x *Context) Reset() { x.routeParams.Keys = x.routeParams.Keys[:0] x.routeParams.Values = x.routeParams.Values[:0] x.methodNotAllowed = false + x.parentCtx = nil } // 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) } +// 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 // 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. diff --git a/vendor/github.com/go-chi/chi/middleware/basic_auth.go b/vendor/github.com/go-chi/chi/middleware/basic_auth.go index 87b2641a6a..a546c9e9e8 100644 --- a/vendor/github.com/go-chi/chi/middleware/basic_auth.go +++ b/vendor/github.com/go-chi/chi/middleware/basic_auth.go @@ -1,6 +1,7 @@ package middleware import ( + "crypto/subtle" "fmt" "net/http" ) @@ -16,7 +17,7 @@ func BasicAuth(realm string, creds map[string]string) func(next http.Handler) ht } credPass, credUserOk := creds[user] - if !credUserOk || pass != credPass { + if !credUserOk || subtle.ConstantTimeCompare([]byte(pass), []byte(credPass)) != 1 { basicAuthFailed(w, realm) return } diff --git a/vendor/github.com/go-chi/chi/middleware/clean_path.go b/vendor/github.com/go-chi/chi/middleware/clean_path.go new file mode 100644 index 0000000000..d42bf28457 --- /dev/null +++ b/vendor/github.com/go-chi/chi/middleware/clean_path.go @@ -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) + }) +} diff --git a/vendor/github.com/go-chi/chi/middleware/content_type.go b/vendor/github.com/go-chi/chi/middleware/content_type.go index ee4957874f..023978fac0 100644 --- a/vendor/github.com/go-chi/chi/middleware/content_type.go +++ b/vendor/github.com/go-chi/chi/middleware/content_type.go @@ -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 // with a 415 Unsupported Media Type status. func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler { - cT := []string{} - for _, t := range contentTypes { - cT = append(cT, strings.ToLower(t)) + allowedContentTypes := make(map[string]struct{}, len(contentTypes)) + for _, ctype := range contentTypes { + allowedContentTypes[strings.TrimSpace(strings.ToLower(ctype))] = struct{}{} } 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] } - for _, t := range cT { - if t == s { - next.ServeHTTP(w, r) - return - } + if _, ok := allowedContentTypes[s]; ok { + next.ServeHTTP(w, r) + return } w.WriteHeader(http.StatusUnsupportedMediaType) diff --git a/vendor/github.com/go-chi/chi/middleware/logger.go b/vendor/github.com/go-chi/chi/middleware/logger.go index 158a6a3905..66edc3dda8 100644 --- a/vendor/github.com/go-chi/chi/middleware/logger.go +++ b/vendor/github.com/go-chi/chi/middleware/logger.go @@ -6,6 +6,7 @@ import ( "log" "net/http" "os" + "runtime" "time" ) @@ -16,7 +17,7 @@ var ( // 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 // 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 @@ -27,6 +28,16 @@ var ( // // Alternatively, look at https://github.com/goware/httplog for a more in-depth // 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 { 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) { PrintPrettyStack(v) } + +func init() { + color := true + if runtime.GOOS == "windows" { + color = false + } + DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: !color}) +} diff --git a/vendor/github.com/go-chi/chi/middleware/strip.go b/vendor/github.com/go-chi/chi/middleware/strip.go index 2b8b1842ab..1082d713ef 100644 --- a/vendor/github.com/go-chi/chi/middleware/strip.go +++ b/vendor/github.com/go-chi/chi/middleware/strip.go @@ -14,13 +14,18 @@ func StripSlashes(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { var path string rctx := chi.RouteContext(r.Context()) - if rctx.RoutePath != "" { + if rctx != nil && rctx.RoutePath != "" { path = rctx.RoutePath } else { path = r.URL.Path } 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) } @@ -36,7 +41,7 @@ func RedirectSlashes(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { var path string rctx := chi.RouteContext(r.Context()) - if rctx.RoutePath != "" { + if rctx != nil && rctx.RoutePath != "" { path = rctx.RoutePath } else { path = r.URL.Path @@ -47,7 +52,8 @@ func RedirectSlashes(next http.Handler) http.Handler { } else { 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 } next.ServeHTTP(w, r) diff --git a/vendor/github.com/go-chi/chi/middleware/url_format.go b/vendor/github.com/go-chi/chi/middleware/url_format.go index 5749e4f32b..d8f04b7cb9 100644 --- a/vendor/github.com/go-chi/chi/middleware/url_format.go +++ b/vendor/github.com/go-chi/chi/middleware/url_format.go @@ -53,7 +53,7 @@ func URLFormat(next http.Handler) http.Handler { if strings.Index(path, ".") > 0 { base := strings.LastIndex(path, "/") - idx := strings.Index(path[base:], ".") + idx := strings.LastIndex(path[base:], ".") if idx > 0 { idx += base diff --git a/vendor/github.com/go-chi/chi/mux.go b/vendor/github.com/go-chi/chi/mux.go index 52950e97b5..c6fdb8a0f3 100644 --- a/vendor/github.com/go-chi/chi/mux.go +++ b/vendor/github.com/go-chi/chi/mux.go @@ -1,7 +1,6 @@ package chi import ( - "context" "fmt" "net/http" "strings" @@ -78,9 +77,10 @@ func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { rctx = mx.pool.Get().(*Context) rctx.Reset() rctx.Routes = mx + rctx.parentCtx = r.Context() - // NOTE: r.WithContext() causes 2 allocations and context.WithValue() causes 1 allocation - r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx)) + // NOTE: r.WithContext() causes 2 allocations + r = r.WithContext((*directContext)(rctx)) // Serve the request and once its done, put the request context back in the sync pool 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 // middleware registration isn't allowed for this stack, like now. if !mx.inline && mx.handler == nil { - mx.buildRouteHandler() + mx.updateRouteHandler() } // 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 // call to Mount. See _examples/. func (mx *Mux) Route(pattern string, fn func(r Router)) Router { - subRouter := NewRouter() - if fn != nil { - fn(subRouter) + if fn == nil { + panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern)) } + subRouter := NewRouter() + fn(subRouter) mx.Mount(pattern, 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, // if you define two Mount() routes on the exact same pattern the mount will panic. 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 // routing 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) { rctx := RouteContext(r.Context()) + + // shift the url path past the previous subrouter 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) }) @@ -367,14 +381,6 @@ func (mx *Mux) MethodNotAllowedHandler() http.HandlerFunc { 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 // and routing pattern. 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. if !mx.inline && mx.handler == nil { - mx.buildRouteHandler() + mx.updateRouteHandler() } // 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, // method not allowed. func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) { diff --git a/vendor/github.com/unrolled/render/.gitignore b/vendor/github.com/unrolled/render/.gitignore new file mode 100644 index 0000000000..05f4eaf615 --- /dev/null +++ b/vendor/github.com/unrolled/render/.gitignore @@ -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 diff --git a/vendor/github.com/unrolled/render/.travis.yml b/vendor/github.com/unrolled/render/.travis.yml new file mode 100644 index 0000000000..9ec8d547fe --- /dev/null +++ b/vendor/github.com/unrolled/render/.travis.yml @@ -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 diff --git a/vendor/github.com/unrolled/render/LICENSE b/vendor/github.com/unrolled/render/LICENSE new file mode 100644 index 0000000000..9c62063ec1 --- /dev/null +++ b/vendor/github.com/unrolled/render/LICENSE @@ -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. diff --git a/vendor/github.com/unrolled/render/README.md b/vendor/github.com/unrolled/render/README.md new file mode 100644 index 0000000000..5ffa2a7c1c --- /dev/null +++ b/vendor/github.com/unrolled/render/README.md @@ -0,0 +1,508 @@ +# Render [![GoDoc](http://godoc.org/github.com/unrolled/render?status.svg)](http://godoc.org/github.com/unrolled/render) [![Build Status](https://travis-ci.org/unrolled/render.svg)](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() +} +~~~ diff --git a/vendor/github.com/unrolled/render/buffer.go b/vendor/github.com/unrolled/render/buffer.go new file mode 100644 index 0000000000..cdc92ffbde --- /dev/null +++ b/vendor/github.com/unrolled/render/buffer.go @@ -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) +} diff --git a/vendor/github.com/unrolled/render/doc.go b/vendor/github.com/unrolled/render/doc.go new file mode 100644 index 0000000000..d3487ffba2 --- /dev/null +++ b/vendor/github.com/unrolled/render/doc.go @@ -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 diff --git a/vendor/github.com/unrolled/render/engine.go b/vendor/github.com/unrolled/render/engine.go new file mode 100644 index 0000000000..cdf1a1b16f --- /dev/null +++ b/vendor/github.com/unrolled/render/engine.go @@ -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 +} diff --git a/vendor/github.com/unrolled/render/fs.go b/vendor/github.com/unrolled/render/fs.go new file mode 100644 index 0000000000..3e60776253 --- /dev/null +++ b/vendor/github.com/unrolled/render/fs.go @@ -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) +} diff --git a/vendor/github.com/unrolled/render/go.mod b/vendor/github.com/unrolled/render/go.mod new file mode 100644 index 0000000000..22d793cbcf --- /dev/null +++ b/vendor/github.com/unrolled/render/go.mod @@ -0,0 +1,5 @@ +module github.com/unrolled/render + +go 1.12 + +require github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 diff --git a/vendor/github.com/unrolled/render/go.sum b/vendor/github.com/unrolled/render/go.sum new file mode 100644 index 0000000000..2199959075 --- /dev/null +++ b/vendor/github.com/unrolled/render/go.sum @@ -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= diff --git a/vendor/github.com/unrolled/render/helpers.go b/vendor/github.com/unrolled/render/helpers.go new file mode 100644 index 0000000000..699508a491 --- /dev/null +++ b/vendor/github.com/unrolled/render/helpers.go @@ -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 + }, +} diff --git a/vendor/github.com/unrolled/render/helpers_pre16.go b/vendor/github.com/unrolled/render/helpers_pre16.go new file mode 100644 index 0000000000..999d9af497 --- /dev/null +++ b/vendor/github.com/unrolled/render/helpers_pre16.go @@ -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 + }, +} diff --git a/vendor/github.com/unrolled/render/render.go b/vendor/github.com/unrolled/render/render.go new file mode 100644 index 0000000000..3259f620eb --- /dev/null +++ b/vendor/github.com/unrolled/render/render.go @@ -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) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ee584932d2..426b0dc947 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,6 +7,13 @@ code.gitea.io/gitea-vet/checks # code.gitea.io/sdk/gitea v0.13.1 ## explicit 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 ## explicit gitea.com/lunny/levelqueue @@ -188,7 +195,7 @@ github.com/cespare/xxhash/v2 github.com/chris-ramon/douceur/parser # github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 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/client # 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/go-asn1-ber/asn1-ber v1.5.1 github.com/go-asn1-ber/asn1-ber -# github.com/go-chi/chi v1.5.0 +# github.com/go-chi/chi v1.5.1 ## explicit github.com/go-chi/chi github.com/go-chi/chi/middleware @@ -630,8 +637,6 @@ github.com/oliamb/cutter github.com/olivere/elastic/v7 github.com/olivere/elastic/v7/config github.com/olivere/elastic/v7/uritemplates -# github.com/onsi/ginkgo v1.13.0 -## explicit # github.com/pelletier/go-toml v1.8.1 ## explicit github.com/pelletier/go-toml @@ -750,6 +755,9 @@ github.com/unknwon/i18n # github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae ## explicit github.com/unknwon/paginater +# github.com/unrolled/render v1.0.3 +## explicit +github.com/unrolled/render # github.com/urfave/cli v1.22.5 ## explicit github.com/urfave/cli @@ -916,7 +924,6 @@ golang.org/x/tools/internal/typesinternal golang.org/x/xerrors golang.org/x/xerrors/internal # google.golang.org/appengine v1.6.7 -## explicit google.golang.org/appengine google.golang.org/appengine/internal google.golang.org/appengine/internal/app_identity |