summaryrefslogtreecommitdiffstats
path: root/vendor/github.com
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-09-23 10:17:51 -0400
committerGitHub <noreply@github.com>2016-09-23 10:17:51 -0400
commit2ca0e8f9a0f9863555a26e984cde15efff9ef8f8 (patch)
treedaae1ee67b14a3d0a84424f2a304885d9e75ce2b /vendor/github.com
parent6d62d65b2dc85855aabea036cbd44f6059e19d13 (diff)
downloadchat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.gz
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.bz2
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.zip
Updating golang dependancies (#4075)
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/NYTimes/gziphandler/.gitignore1
-rw-r--r--vendor/github.com/NYTimes/gziphandler/.travis.yml5
-rw-r--r--vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md75
-rw-r--r--vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md30
-rw-r--r--vendor/github.com/NYTimes/gziphandler/gzip.go170
-rw-r--r--vendor/github.com/NYTimes/gziphandler/gzip_test.go184
-rw-r--r--vendor/github.com/braintree/manners/README.md4
-rw-r--r--vendor/github.com/braintree/manners/helpers_test.go141
-rw-r--r--vendor/github.com/braintree/manners/interfaces.go7
-rw-r--r--vendor/github.com/braintree/manners/server.go141
-rw-r--r--vendor/github.com/braintree/manners/server_test.go153
-rw-r--r--vendor/github.com/braintree/manners/static.go14
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/certs.go29
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/conn.go13
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/listener.go34
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/temp_file.go27
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/wait_group.go33
-rw-r--r--vendor/github.com/braintree/manners/transition_test.go9
-rw-r--r--vendor/github.com/dgryski/dgoogauth/README.md2
-rw-r--r--vendor/github.com/disintegration/imaging/.travis.yml2
-rw-r--r--vendor/github.com/garyburd/redigo/.travis.yml16
-rw-r--r--vendor/github.com/garyburd/redigo/LICENSE175
-rw-r--r--vendor/github.com/garyburd/redigo/README.markdown50
-rw-r--r--vendor/github.com/garyburd/redigo/internal/commandinfo.go54
-rw-r--r--vendor/github.com/garyburd/redigo/internal/commandinfo_test.go27
-rw-r--r--vendor/github.com/garyburd/redigo/internal/redistest/testdb.go68
-rw-r--r--vendor/github.com/garyburd/redigo/redis/conn.go570
-rw-r--r--vendor/github.com/garyburd/redigo/redis/conn_test.go670
-rw-r--r--vendor/github.com/garyburd/redigo/redis/doc.go169
-rw-r--r--vendor/github.com/garyburd/redigo/redis/log.go117
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pool.go393
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pool_test.go684
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pubsub.go144
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pubsub_test.go148
-rw-r--r--vendor/github.com/garyburd/redigo/redis/redis.go44
-rw-r--r--vendor/github.com/garyburd/redigo/redis/reply.go393
-rw-r--r--vendor/github.com/garyburd/redigo/redis/reply_test.go179
-rw-r--r--vendor/github.com/garyburd/redigo/redis/scan.go555
-rw-r--r--vendor/github.com/garyburd/redigo/redis/scan_test.go440
-rw-r--r--vendor/github.com/garyburd/redigo/redis/script.go86
-rw-r--r--vendor/github.com/garyburd/redigo/redis/script_test.go100
-rw-r--r--vendor/github.com/garyburd/redigo/redis/test_test.go177
-rw-r--r--vendor/github.com/garyburd/redigo/redis/zpop_example_test.go113
-rw-r--r--vendor/github.com/garyburd/redigo/redisx/connmux.go152
-rw-r--r--vendor/github.com/garyburd/redigo/redisx/connmux_test.go259
-rw-r--r--vendor/github.com/garyburd/redigo/redisx/doc.go17
-rwxr-xr-xvendor/github.com/go-ldap/ldap/.githooks/pre-push6
-rw-r--r--vendor/github.com/go-ldap/ldap/.travis.yml11
-rw-r--r--vendor/github.com/go-ldap/ldap/Makefile42
-rw-r--r--vendor/github.com/go-ldap/ldap/README.md28
-rw-r--r--vendor/github.com/go-ldap/ldap/add.go49
-rw-r--r--vendor/github.com/go-ldap/ldap/bind.go42
-rw-r--r--vendor/github.com/go-ldap/ldap/compare.go18
-rw-r--r--vendor/github.com/go-ldap/ldap/conn.go147
-rw-r--r--vendor/github.com/go-ldap/ldap/conn_test.go295
-rw-r--r--vendor/github.com/go-ldap/ldap/control.go132
-rw-r--r--vendor/github.com/go-ldap/ldap/control_test.go58
-rw-r--r--vendor/github.com/go-ldap/ldap/del.go27
-rw-r--r--vendor/github.com/go-ldap/ldap/dn.go21
-rw-r--r--vendor/github.com/go-ldap/ldap/error.go8
-rw-r--r--vendor/github.com/go-ldap/ldap/error_test.go77
-rw-r--r--vendor/github.com/go-ldap/ldap/filter.go72
-rw-r--r--vendor/github.com/go-ldap/ldap/ldap.go3
-rw-r--r--vendor/github.com/go-ldap/ldap/modify.go66
-rw-r--r--vendor/github.com/go-ldap/ldap/passwdmodify.go34
-rw-r--r--vendor/github.com/go-ldap/ldap/search.go58
-rw-r--r--vendor/github.com/go-sql-driver/mysql/.travis.yml7
-rw-r--r--vendor/github.com/go-sql-driver/mysql/AUTHORS17
-rw-r--r--vendor/github.com/go-sql-driver/mysql/CHANGELOG.md31
-rw-r--r--vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md17
-rw-r--r--vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md21
-rw-r--r--vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md9
-rw-r--r--vendor/github.com/go-sql-driver/mysql/README.md90
-rw-r--r--vendor/github.com/go-sql-driver/mysql/benchmark_test.go40
-rw-r--r--vendor/github.com/go-sql-driver/mysql/buffer.go55
-rw-r--r--vendor/github.com/go-sql-driver/mysql/collations.go16
-rw-r--r--vendor/github.com/go-sql-driver/mysql/connection.go270
-rw-r--r--vendor/github.com/go-sql-driver/mysql/const.go37
-rw-r--r--vendor/github.com/go-sql-driver/mysql/driver.go85
-rw-r--r--vendor/github.com/go-sql-driver/mysql/driver_test.go893
-rw-r--r--vendor/github.com/go-sql-driver/mysql/dsn.go513
-rw-r--r--vendor/github.com/go-sql-driver/mysql/dsn_test.go207
-rw-r--r--vendor/github.com/go-sql-driver/mysql/errors.go22
-rw-r--r--vendor/github.com/go-sql-driver/mysql/infile.go53
-rw-r--r--vendor/github.com/go-sql-driver/mysql/packets.go337
-rw-r--r--vendor/github.com/go-sql-driver/mysql/rows.go50
-rw-r--r--vendor/github.com/go-sql-driver/mysql/statement.go40
-rw-r--r--vendor/github.com/go-sql-driver/mysql/utils.go574
-rw-r--r--vendor/github.com/go-sql-driver/mysql/utils_test.go143
-rw-r--r--vendor/github.com/goamz/goamz/aws/aws.go2
-rw-r--r--vendor/github.com/goamz/goamz/aws/regions.go23
-rw-r--r--vendor/github.com/goamz/goamz/exp/ses/ses.go10
-rw-r--r--vendor/github.com/goamz/goamz/exp/ses/ses_types.go4
-rw-r--r--vendor/github.com/goamz/goamz/s3/s3test/server.go4
-rw-r--r--vendor/github.com/goamz/goamz/sqs/sqs.go23
-rw-r--r--vendor/github.com/golang/freetype/example/genbasicfont/main.go237
-rw-r--r--vendor/github.com/golang/freetype/truetype/hint.go9
-rw-r--r--vendor/github.com/golang/groupcache/.gitignore1
-rw-r--r--vendor/github.com/golang/groupcache/LICENSE191
-rw-r--r--vendor/github.com/golang/groupcache/README.md73
-rw-r--r--vendor/github.com/golang/groupcache/byteview.go160
-rw-r--r--vendor/github.com/golang/groupcache/byteview_test.go142
-rw-r--r--vendor/github.com/golang/groupcache/consistenthash/consistenthash.go81
-rw-r--r--vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go110
-rw-r--r--vendor/github.com/golang/groupcache/groupcache.go489
-rw-r--r--vendor/github.com/golang/groupcache/groupcache_test.go447
-rw-r--r--vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go65
-rw-r--r--vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto34
-rw-r--r--vendor/github.com/golang/groupcache/http.go227
-rw-r--r--vendor/github.com/golang/groupcache/http_test.go166
-rw-r--r--vendor/github.com/golang/groupcache/lru/lru.go121
-rw-r--r--vendor/github.com/golang/groupcache/lru/lru_test.go73
-rw-r--r--vendor/github.com/golang/groupcache/peers.go71
-rw-r--r--vendor/github.com/golang/groupcache/singleflight/singleflight.go64
-rw-r--r--vendor/github.com/golang/groupcache/singleflight/singleflight_test.go85
-rw-r--r--vendor/github.com/golang/groupcache/sinks.go322
-rw-r--r--vendor/github.com/golang/groupcache/testpb/test.pb.go235
-rw-r--r--vendor/github.com/golang/groupcache/testpb/test.proto63
-rw-r--r--vendor/github.com/gorilla/context/.travis.yml1
-rw-r--r--vendor/github.com/gorilla/context/README.md3
-rw-r--r--vendor/github.com/gorilla/context/context_test.go2
-rw-r--r--vendor/github.com/gorilla/context/doc.go6
-rw-r--r--vendor/github.com/gorilla/handlers/.travis.yml7
-rw-r--r--vendor/github.com/gorilla/handlers/cors.go3
-rw-r--r--vendor/github.com/gorilla/handlers/cors_test.go18
-rw-r--r--vendor/github.com/gorilla/mux/.travis.yml3
-rw-r--r--vendor/github.com/gorilla/mux/mux.go46
-rw-r--r--vendor/github.com/gorilla/mux/mux_test.go18
-rw-r--r--vendor/github.com/gorilla/mux/route.go7
-rw-r--r--vendor/github.com/hashicorp/golang-lru/.gitignore23
-rw-r--r--vendor/github.com/hashicorp/golang-lru/2q.go212
-rw-r--r--vendor/github.com/hashicorp/golang-lru/2q_test.go306
-rw-r--r--vendor/github.com/hashicorp/golang-lru/LICENSE362
-rw-r--r--vendor/github.com/hashicorp/golang-lru/README.md25
-rw-r--r--vendor/github.com/hashicorp/golang-lru/arc.go257
-rw-r--r--vendor/github.com/hashicorp/golang-lru/arc_test.go377
-rw-r--r--vendor/github.com/hashicorp/golang-lru/lru.go114
-rw-r--r--vendor/github.com/hashicorp/golang-lru/lru_test.go221
-rw-r--r--vendor/github.com/hashicorp/golang-lru/simplelru/lru.go160
-rw-r--r--vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go167
-rw-r--r--vendor/github.com/jehiah/go-strftime/.gitignore22
-rwxr-xr-xvendor/github.com/lib/pq/.travis.sh73
-rw-r--r--vendor/github.com/lib/pq/.travis.yml75
-rw-r--r--vendor/github.com/lib/pq/README.md2
-rw-r--r--vendor/github.com/lib/pq/array.go727
-rw-r--r--vendor/github.com/lib/pq/array_test.go1153
-rw-r--r--vendor/github.com/lib/pq/certs/bogus_root.crt19
-rw-r--r--vendor/github.com/lib/pq/conn.go17
-rw-r--r--vendor/github.com/lib/pq/conn_test.go16
-rw-r--r--vendor/github.com/lib/pq/encode.go117
-rw-r--r--vendor/github.com/lib/pq/encode_test.go11
-rw-r--r--vendor/github.com/lib/pq/ssl_test.go43
-rw-r--r--vendor/github.com/mssola/user_agent/.travis.yml2
-rw-r--r--vendor/github.com/mssola/user_agent/LICENSE2
-rw-r--r--vendor/github.com/mssola/user_agent/README.md2
-rw-r--r--vendor/github.com/mssola/user_agent/all_test.go610
-rw-r--r--vendor/github.com/mssola/user_agent/bot.go14
-rw-r--r--vendor/github.com/mssola/user_agent/browser.go30
-rw-r--r--vendor/github.com/mssola/user_agent/operating_systems.go29
-rw-r--r--vendor/github.com/mssola/user_agent/user_agent.go15
-rw-r--r--vendor/github.com/pborman/uuid/CONTRIBUTING.md10
-rw-r--r--vendor/github.com/pborman/uuid/sql.go8
-rw-r--r--vendor/github.com/pborman/uuid/sql_test.go9
-rw-r--r--vendor/github.com/pborman/uuid/uuid.go25
-rw-r--r--vendor/github.com/pborman/uuid/uuid_test.go74
-rw-r--r--vendor/github.com/segmentio/analytics-go/cli/cli.go174
-rw-r--r--vendor/github.com/segmentio/backo-go/.gitmodules3
167 files changed, 7380 insertions, 13367 deletions
diff --git a/vendor/github.com/NYTimes/gziphandler/.gitignore b/vendor/github.com/NYTimes/gziphandler/.gitignore
new file mode 100644
index 000000000..1377554eb
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/.gitignore
@@ -0,0 +1 @@
+*.swp
diff --git a/vendor/github.com/NYTimes/gziphandler/.travis.yml b/vendor/github.com/NYTimes/gziphandler/.travis.yml
new file mode 100644
index 000000000..f918f8d76
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/.travis.yml
@@ -0,0 +1,5 @@
+language: go
+
+go:
+ - 1.7
+ - tip
diff --git a/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..cdbca194c
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md
@@ -0,0 +1,75 @@
+---
+layout: code-of-conduct
+version: v1.0
+---
+
+This code of conduct outlines our expectations for participants within the **NYTimes/gziphandler** community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community.
+
+Our open source community strives to:
+
+* **Be friendly and patient.**
+* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
+* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
+* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one.
+* **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable.
+* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
+
+## Definitions
+
+Harassment includes, but is not limited to:
+
+- Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation
+- Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment
+- Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle
+- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop
+- Threats of violence, both physical and psychological
+- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm
+- Deliberate intimidation
+- Stalking or following
+- Harassing photography or recording, including logging online activity for harassment purposes
+- Sustained disruption of discussion
+- Unwelcome sexual attention, including gratuitous or off-topic sexual images or behaviour
+- Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others
+- Continued one-on-one communication after requests to cease
+- Deliberate “outing” of any aspect of a person’s identity without their consent except as necessary to protect others from intentional abuse
+- Publication of non-harassing private communication
+
+Our open source community prioritizes marginalized people’s safety over privileged people’s comfort. We will not act on complaints regarding:
+
+- ‘Reverse’ -isms, including ‘reverse racism,’ ‘reverse sexism,’ and ‘cisphobia’
+- Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “I’m not discussing this with you”
+- Refusal to explain or debate social justice concepts
+- Communicating in a ‘tone’ you don’t find congenial
+- Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions
+
+
+### Diversity Statement
+
+We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
+
+Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected
+characteristics above, including participants with disabilities.
+
+### Reporting Issues
+
+If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via **code@nytimes.com**. All reports will be handled with discretion. In your report please include:
+
+- Your contact information.
+- Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please
+include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link.
+- Any additional information that may be helpful.
+
+After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse.
+
+### Attribution & Acknowledgements
+
+We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration:
+
+* [Django](https://www.djangoproject.com/conduct/reporting/)
+* [Python](https://www.python.org/community/diversity/)
+* [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct)
+* [Contributor Covenant](http://contributor-covenant.org/)
+* [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/)
+* [Citizen Code of Conduct](http://citizencodeofconduct.org/)
+
+This Code of Conduct was based on https://github.com/todogroup/opencodeofconduct
diff --git a/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md
new file mode 100644
index 000000000..b89a9eb4f
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# Contributing to NYTimes/gziphandler
+
+This is an open source project started by handful of developers at The New York Times and open to the entire Go community.
+
+We really appreciate your help!
+
+## Filing issues
+
+When filing an issue, make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+## Contributing code
+
+Before submitting changes, please follow these guidelines:
+
+1. Check the open issues and pull requests for existing discussions.
+2. Open an issue to discuss a new feature.
+3. Write tests.
+4. Make sure code follows the ['Go Code Review Comments'](https://github.com/golang/go/wiki/CodeReviewComments).
+5. Make sure your changes pass `go test`.
+6. Make sure the entire test suite passes locally and on Travis CI.
+7. Open a Pull Request.
+8. [Squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) after receiving feedback and add a [great commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
+
+Unless otherwise noted, the gziphandler source files are distributed under the Apache 2.0-style license found in the LICENSE.md file.
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go
index ee1526f42..39e8c5e75 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip.go
@@ -13,6 +13,8 @@ const (
vary = "Vary"
acceptEncoding = "Accept-Encoding"
contentEncoding = "Content-Encoding"
+ contentType = "Content-Type"
+ contentLength = "Content-Length"
)
type codings map[string]float64
@@ -22,57 +24,160 @@ type codings map[string]float64
// The examples seem to indicate that it is.
const DEFAULT_QVALUE = 1.0
-var gzipWriterPool = sync.Pool{
- New: func() interface{} { return gzip.NewWriter(nil) },
+// gzipWriterPools stores a sync.Pool for each compression level for reuse of
+// gzip.Writers. Use poolIndex to covert a compression level to an index into
+// gzipWriterPools.
+var gzipWriterPools [gzip.BestCompression - gzip.BestSpeed + 2]*sync.Pool
+
+func init() {
+ for i := gzip.BestSpeed; i <= gzip.BestCompression; i++ {
+ addLevelPool(i)
+ }
+ addLevelPool(gzip.DefaultCompression)
+}
+
+// poolIndex maps a compression level to its index into gzipWriterPools. It
+// assumes that level is a valid gzip compression level.
+func poolIndex(level int) int {
+ // gzip.DefaultCompression == -1, so we need to treat it special.
+ if level == gzip.DefaultCompression {
+ return gzip.BestCompression - gzip.BestSpeed + 1
+ }
+ return level - gzip.BestSpeed
+}
+
+func addLevelPool(level int) {
+ gzipWriterPools[poolIndex(level)] = &sync.Pool{
+ New: func() interface{} {
+ // NewWriterLevel only returns error on a bad level, we are guaranteeing
+ // that this will be a valid level so it is okay to ignore the returned
+ // error.
+ w, _ := gzip.NewWriterLevel(nil, level)
+ return w
+ },
+ }
}
// GzipResponseWriter provides an http.ResponseWriter interface, which gzips
-// bytes before writing them to the underlying response. This doesn't set the
-// Content-Encoding header, nor close the writers, so don't forget to do that.
+// bytes before writing them to the underlying response. This doesn't close the
+// writers, so don't forget to do that.
type GzipResponseWriter struct {
- gw *gzip.Writer
http.ResponseWriter
+ index int // Index for gzipWriterPools.
+ gw *gzip.Writer
}
// Write appends data to the gzip writer.
-func (w GzipResponseWriter) Write(b []byte) (int, error) {
- if _, ok := w.Header()["Content-Type"]; !ok {
+func (w *GzipResponseWriter) Write(b []byte) (int, error) {
+ // Lazily create the gzip.Writer, this allows empty bodies to be actually
+ // empty, for example in the case of status code 204 (no content).
+ if w.gw == nil {
+ w.init()
+ }
+
+ if _, ok := w.Header()[contentType]; !ok {
// If content type is not set, infer it from the uncompressed body.
- w.Header().Set("Content-Type", http.DetectContentType(b))
+ w.Header().Set(contentType, http.DetectContentType(b))
}
return w.gw.Write(b)
}
+// WriteHeader will check if the gzip writer needs to be lazily initiated and
+// then pass the code along to the underlying ResponseWriter.
+func (w *GzipResponseWriter) WriteHeader(code int) {
+ if w.gw == nil &&
+ code != http.StatusNotModified && code != http.StatusNoContent {
+ w.init()
+ }
+ w.ResponseWriter.WriteHeader(code)
+}
+
+// init graps a new gzip writer from the gzipWriterPool and writes the correct
+// content encoding header.
+func (w *GzipResponseWriter) init() {
+ // Bytes written during ServeHTTP are redirected to this gzip writer
+ // before being written to the underlying response.
+ gzw := gzipWriterPools[w.index].Get().(*gzip.Writer)
+ gzw.Reset(w.ResponseWriter)
+ w.gw = gzw
+ w.ResponseWriter.Header().Set(contentEncoding, "gzip")
+ // if the Content-Length is already set, then calls to Write on gzip
+ // will fail to set the Content-Length header since its already set
+ // See: https://github.com/golang/go/issues/14975
+ w.ResponseWriter.Header().Del(contentLength)
+}
+
+// Close will close the gzip.Writer and will put it back in the gzipWriterPool.
+func (w *GzipResponseWriter) Close() error {
+ if w.gw == nil {
+ return nil
+ }
+
+ err := w.gw.Close()
+ gzipWriterPools[w.index].Put(w.gw)
+ return err
+}
+
// Flush flushes the underlying *gzip.Writer and then the underlying
// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter
// an http.Flusher.
-func (w GzipResponseWriter) Flush() {
- w.gw.Flush()
+func (w *GzipResponseWriter) Flush() {
+ if w.gw != nil {
+ w.gw.Flush()
+ }
+
if fw, ok := w.ResponseWriter.(http.Flusher); ok {
fw.Flush()
}
}
+// MustNewGzipLevelHandler behaves just like NewGzipLevelHandler except that in
+// an error case it panics rather than returning an error.
+func MustNewGzipLevelHandler(level int) func(http.Handler) http.Handler {
+ wrap, err := NewGzipLevelHandler(level)
+ if err != nil {
+ panic(err)
+ }
+ return wrap
+}
+
+// NewGzipLevelHandler returns a wrapper function (often known as middleware)
+// which can be used to wrap an HTTP handler to transparently gzip the response
+// body if the client supports it (via the Accept-Encoding header). Responses will
+// be encoded at the given gzip compression level. An error will be returned only
+// if an invalid gzip compression level is given, so if one can ensure the level
+// is valid, the returned error can be safely ignored.
+func NewGzipLevelHandler(level int) (func(http.Handler) http.Handler, error) {
+ if level != gzip.DefaultCompression && (level < gzip.BestSpeed || level > gzip.BestCompression) {
+ return nil, fmt.Errorf("invalid compression level requested: %d", level)
+ }
+ return func(h http.Handler) http.Handler {
+ index := poolIndex(level)
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add(vary, acceptEncoding)
+
+ if acceptsGzip(r) {
+ gw := &GzipResponseWriter{
+ ResponseWriter: w,
+ index: index,
+ }
+ defer gw.Close()
+
+ h.ServeHTTP(gw, r)
+ } else {
+ h.ServeHTTP(w, r)
+ }
+ })
+ }, nil
+}
+
// GzipHandler wraps an HTTP handler, to transparently gzip the response body if
-// the client supports it (via the Accept-Encoding header).
+// the client supports it (via the Accept-Encoding header). This will compress at
+// the default compression level.
func GzipHandler(h http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Add(vary, acceptEncoding)
-
- if acceptsGzip(r) {
- // Bytes written during ServeHTTP are redirected to this gzip writer
- // before being written to the underlying response.
- gzw := gzipWriterPool.Get().(*gzip.Writer)
- defer gzipWriterPool.Put(gzw)
- gzw.Reset(w)
- defer gzw.Close()
-
- w.Header().Set(contentEncoding, "gzip")
- h.ServeHTTP(GzipResponseWriter{gzw, w}, r)
- } else {
- h.ServeHTTP(w, r)
- }
- })
+ wrapper, _ := NewGzipLevelHandler(gzip.DefaultCompression)
+ return wrapper(h)
}
// acceptsGzip returns true if the given HTTP request indicates that it will
@@ -84,21 +189,20 @@ func acceptsGzip(r *http.Request) bool {
// parseEncodings attempts to parse a list of codings, per RFC 2616, as might
// appear in an Accept-Encoding header. It returns a map of content-codings to
-// quality values, and an error containing the errors encounted. It's probably
+// quality values, and an error containing the errors encountered. It's probably
// safe to ignore those, because silently ignoring errors is how the internet
// works.
//
-// See: http://tools.ietf.org/html/rfc2616#section-14.3
+// See: http://tools.ietf.org/html/rfc2616#section-14.3.
func parseEncodings(s string) (codings, error) {
c := make(codings)
- e := make([]string, 0)
+ var e []string
for _, ss := range strings.Split(s, ",") {
coding, qvalue, err := parseCoding(ss)
if err != nil {
e = append(e, err.Error())
-
} else {
c[coding] = qvalue
}
@@ -123,13 +227,11 @@ func parseCoding(s string) (coding string, qvalue float64, err error) {
if n == 0 {
coding = strings.ToLower(part)
-
} else if strings.HasPrefix(part, "q=") {
qvalue, err = strconv.ParseFloat(strings.TrimPrefix(part, "q="), 64)
if qvalue < 0.0 {
qvalue = 0.0
-
} else if qvalue > 1.0 {
qvalue = 1.0
}
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
index 9a62bcbaa..f2d44e04f 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip_test.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
@@ -3,10 +3,14 @@ package gziphandler
import (
"bytes"
"compress/gzip"
+ "fmt"
"io"
"io/ioutil"
+ "net"
"net/http"
"net/http/httptest"
+ "net/url"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
@@ -17,15 +21,15 @@ func TestParseEncodings(t *testing.T) {
examples := map[string]codings{
// Examples from RFC 2616
- "compress, gzip": codings{"compress": 1.0, "gzip": 1.0},
- "": codings{},
- "*": codings{"*": 1.0},
- "compress;q=0.5, gzip;q=1.0": codings{"compress": 0.5, "gzip": 1.0},
- "gzip;q=1.0, identity; q=0.5, *;q=0": codings{"gzip": 1.0, "identity": 0.5, "*": 0.0},
+ "compress, gzip": {"compress": 1.0, "gzip": 1.0},
+ "": {},
+ "*": {"*": 1.0},
+ "compress;q=0.5, gzip;q=1.0": {"compress": 0.5, "gzip": 1.0},
+ "gzip;q=1.0, identity; q=0.5, *;q=0": {"gzip": 1.0, "identity": 0.5, "*": 0.0},
// More random stuff
- "AAA;q=1": codings{"aaa": 1.0},
- "BBB ; q = 2": codings{"bbb": 1.0},
+ "AAA;q=1": {"aaa": 1.0},
+ "BBB ; q = 2": {"bbb": 1.0},
}
for eg, exp := range examples {
@@ -43,25 +47,27 @@ func TestGzipHandler(t *testing.T) {
// requests without accept-encoding are passed along as-is
req1, _ := http.NewRequest("GET", "/whatever", nil)
- res1 := httptest.NewRecorder()
- handler.ServeHTTP(res1, req1)
+ resp1 := httptest.NewRecorder()
+ handler.ServeHTTP(resp1, req1)
+ res1 := resp1.Result()
- assert.Equal(t, 200, res1.Code)
- assert.Equal(t, "", res1.Header().Get("Content-Encoding"))
- assert.Equal(t, "Accept-Encoding", res1.Header().Get("Vary"))
- assert.Equal(t, testBody, res1.Body.String())
+ assert.Equal(t, 200, res1.StatusCode)
+ assert.Equal(t, "", res1.Header.Get("Content-Encoding"))
+ assert.Equal(t, "Accept-Encoding", res1.Header.Get("Vary"))
+ assert.Equal(t, testBody, resp1.Body.String())
// but requests with accept-encoding:gzip are compressed if possible
req2, _ := http.NewRequest("GET", "/whatever", nil)
req2.Header.Set("Accept-Encoding", "gzip")
- res2 := httptest.NewRecorder()
- handler.ServeHTTP(res2, req2)
+ resp2 := httptest.NewRecorder()
+ handler.ServeHTTP(resp2, req2)
+ res2 := resp2.Result()
- assert.Equal(t, 200, res2.Code)
- assert.Equal(t, "gzip", res2.Header().Get("Content-Encoding"))
- assert.Equal(t, "Accept-Encoding", res2.Header().Get("Vary"))
- assert.Equal(t, gzipStr(testBody), res2.Body.Bytes())
+ assert.Equal(t, 200, res2.StatusCode)
+ assert.Equal(t, "gzip", res2.Header.Get("Content-Encoding"))
+ assert.Equal(t, "Accept-Encoding", res2.Header.Get("Vary"))
+ assert.Equal(t, gzipStrLevel(testBody, gzip.DefaultCompression), resp2.Body.Bytes())
// content-type header is correctly set based on uncompressed body
@@ -73,6 +79,142 @@ func TestGzipHandler(t *testing.T) {
assert.Equal(t, http.DetectContentType([]byte(testBody)), res3.Header().Get("Content-Type"))
}
+func TestNewGzipLevelHandler(t *testing.T) {
+ testBody := "aaabbbccc"
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ io.WriteString(w, testBody)
+ })
+
+ for lvl := gzip.BestSpeed; lvl <= gzip.BestCompression; lvl++ {
+ wrapper, err := NewGzipLevelHandler(lvl)
+ if !assert.Nil(t, err, "NewGzipLevleHandler returned error for level:", lvl) {
+ continue
+ }
+
+ req, _ := http.NewRequest("GET", "/whatever", nil)
+ req.Header.Set("Accept-Encoding", "gzip")
+ resp := httptest.NewRecorder()
+ wrapper(handler).ServeHTTP(resp, req)
+ res := resp.Result()
+
+ assert.Equal(t, 200, res.StatusCode)
+ assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"))
+ assert.Equal(t, "Accept-Encoding", res.Header.Get("Vary"))
+ assert.Equal(t, gzipStrLevel(testBody, lvl), resp.Body.Bytes())
+
+ }
+}
+
+func TestNewGzipLevelHandlerReturnsErrorForInvalidLevels(t *testing.T) {
+ var err error
+ _, err = NewGzipLevelHandler(-42)
+ assert.NotNil(t, err)
+
+ _, err = NewGzipLevelHandler(42)
+ assert.NotNil(t, err)
+}
+
+func TestMustNewGzipLevelHandlerWillPanic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Error("panic was not called")
+ }
+ }()
+
+ _ = MustNewGzipLevelHandler(-42)
+}
+
+func TestGzipHandlerNoBody(t *testing.T) {
+ tests := []struct {
+ statusCode int
+ contentEncoding string
+ bodyLen int
+ }{
+ // Body must be empty.
+ {http.StatusNoContent, "", 0},
+ {http.StatusNotModified, "", 0},
+ // Body is going to get gzip'd no matter what.
+ {http.StatusOK, "gzip", 23},
+ }
+
+ for num, test := range tests {
+ handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(test.statusCode)
+ }))
+
+ rec := httptest.NewRecorder()
+ // TODO: in Go1.7 httptest.NewRequest was introduced this should be used
+ // once 1.6 is not longer supported.
+ req := &http.Request{
+ Method: "GET",
+ URL: &url.URL{Path: "/"},
+ Proto: "HTTP/1.1",
+ ProtoMinor: 1,
+ RemoteAddr: "192.0.2.1:1234",
+ Header: make(http.Header),
+ }
+ req.Header.Set("Accept-Encoding", "gzip")
+ handler.ServeHTTP(rec, req)
+
+ body, err := ioutil.ReadAll(rec.Body)
+ if err != nil {
+ t.Fatalf("Unexpected error reading response body: %v", err)
+ }
+
+ header := rec.Header()
+ assert.Equal(t, test.contentEncoding, header.Get("Content-Encoding"), fmt.Sprintf("for test iteration %d", num))
+ assert.Equal(t, "Accept-Encoding", header.Get("Vary"), fmt.Sprintf("for test iteration %d", num))
+ assert.Equal(t, test.bodyLen, len(body), fmt.Sprintf("for test iteration %d", num))
+ }
+}
+
+func TestGzipHandlerContentLength(t *testing.T) {
+ b := []byte("testtesttesttesttesttesttesttesttesttesttesttesttest")
+ handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Length", strconv.Itoa(len(b)))
+ w.Write(b)
+ }))
+ // httptest.NewRecorder doesn't give you access to the Content-Length
+ // header so instead, we create a server on a random port and make
+ // a request to that instead
+ ln, err := net.Listen("tcp", "127.0.0.1:")
+ if err != nil {
+ t.Fatalf("failed creating listen socket: %v", err)
+ }
+ defer ln.Close()
+ srv := &http.Server{
+ Handler: handler,
+ }
+ go srv.Serve(ln)
+
+ req := &http.Request{
+ Method: "GET",
+ URL: &url.URL{Path: "/", Scheme: "http", Host: ln.Addr().String()},
+ Header: make(http.Header),
+ Close: true,
+ }
+ req.Header.Set("Accept-Encoding", "gzip")
+ res, err := http.DefaultClient.Do(req)
+ if err != nil {
+ t.Fatalf("Unexpected error making http request: %v", err)
+ }
+ defer res.Body.Close()
+
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("Unexpected error reading response body: %v", err)
+ }
+
+ l, err := strconv.Atoi(res.Header.Get("Content-Length"))
+ if err != nil {
+ t.Fatalf("Unexpected error parsing Content-Length: %v", err)
+ }
+ assert.Len(t, body, l)
+ assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"))
+ assert.NotEqual(t, b, body)
+}
+
// --------------------------------------------------------------------
func BenchmarkGzipHandler_S2k(b *testing.B) { benchmark(b, false, 2048) }
@@ -84,9 +226,9 @@ func BenchmarkGzipHandler_P100k(b *testing.B) { benchmark(b, true, 102400) }
// --------------------------------------------------------------------
-func gzipStr(s string) []byte {
+func gzipStrLevel(s string, lvl int) []byte {
var b bytes.Buffer
- w := gzip.NewWriter(&b)
+ w, _ := gzip.NewWriterLevel(&b, lvl)
io.WriteString(w, s)
w.Close()
return b.Bytes()
diff --git a/vendor/github.com/braintree/manners/README.md b/vendor/github.com/braintree/manners/README.md
index 09f6f9693..78e0fc01a 100644
--- a/vendor/github.com/braintree/manners/README.md
+++ b/vendor/github.com/braintree/manners/README.md
@@ -23,10 +23,6 @@ Manners ensures that all requests are served by incrementing a WaitGroup when a
If your request handler spawns Goroutines that are not guaranteed to finish with the request, you can ensure they are also completed with the `StartRoutine` and `FinishRoutine` functions on the server.
-### Known Issues
-
-Manners does not correctly shut down long-lived keepalive connections when issued a shutdown command. Clients on an idle keepalive connection may see a connection reset error rather than a close. See https://github.com/braintree/manners/issues/13 for details.
-
### Compatability
Manners 0.3.0 and above uses standard library functionality introduced in Go 1.3.
diff --git a/vendor/github.com/braintree/manners/helpers_test.go b/vendor/github.com/braintree/manners/helpers_test.go
index 3c11a081d..ba1422cb4 100644
--- a/vendor/github.com/braintree/manners/helpers_test.go
+++ b/vendor/github.com/braintree/manners/helpers_test.go
@@ -3,27 +3,30 @@ package manners
import (
"bufio"
"crypto/tls"
+ "errors"
"io/ioutil"
"net"
"net/http"
+ "os"
+ "sync"
"testing"
)
+func newServer() *GracefulServer {
+ return NewWithServer(new(http.Server))
+}
+
// a simple step-controllable http client
type client struct {
tls bool
addr net.Addr
connected chan error
sendrequest chan bool
- response chan *rawResponse
+ idle chan error
+ idlerelease chan bool
closed chan bool
}
-type rawResponse struct {
- body []string
- err error
-}
-
func (c *client) Run() {
go func() {
var err error
@@ -39,21 +42,19 @@ func (c *client) Run() {
for <-c.sendrequest {
_, err = conn.Write([]byte("GET / HTTP/1.1\nHost: localhost:8000\n\n"))
if err != nil {
- c.response <- &rawResponse{err: err}
+ c.idle <- err
}
// Read response; no content
scanner := bufio.NewScanner(conn)
- var lines []string
for scanner.Scan() {
// our null handler doesn't send a body, so we know the request is
// done when we reach the blank line after the headers
- line := scanner.Text()
- if line == "" {
+ if scanner.Text() == "" {
break
}
- lines = append(lines, line)
}
- c.response <- &rawResponse{lines, scanner.Err()}
+ c.idle <- scanner.Err()
+ <-c.idlerelease
}
conn.Close()
ioutil.ReadAll(conn)
@@ -67,7 +68,8 @@ func newClient(addr net.Addr, tls bool) *client {
tls: tls,
connected: make(chan error),
sendrequest: make(chan bool),
- response: make(chan *rawResponse),
+ idle: make(chan error),
+ idlerelease: make(chan bool),
closed: make(chan bool),
}
}
@@ -86,6 +88,7 @@ func startGenericServer(t *testing.T, server *GracefulServer, statechanged chan
}
}
+ //server.up = make(chan chan bool))
server.up = make(chan net.Listener)
exitchan := make(chan error)
@@ -117,3 +120,115 @@ func startTLSServer(t *testing.T, server *GracefulServer, certFile, keyFile stri
return startGenericServer(t, server, statechanged, runner)
}
+
+type tempFile struct {
+ *os.File
+}
+
+func newTempFile(content []byte) (*tempFile, error) {
+ f, err := ioutil.TempFile("", "graceful-test")
+ if err != nil {
+ return nil, err
+ }
+
+ f.Write(content)
+ return &tempFile{f}, nil
+}
+
+func (tf *tempFile) Unlink() {
+ if tf.File != nil {
+ os.Remove(tf.Name())
+ tf.File = nil
+ }
+}
+
+type testWg struct {
+ sync.Mutex
+ count int
+ waitCalled chan int
+}
+
+func newTestWg() *testWg {
+ return &testWg{
+ waitCalled: make(chan int, 1),
+ }
+}
+
+func (wg *testWg) Add(delta int) {
+ wg.Lock()
+ wg.count++
+ wg.Unlock()
+}
+
+func (wg *testWg) Done() {
+ wg.Lock()
+ wg.count--
+ wg.Unlock()
+}
+
+func (wg *testWg) Wait() {
+ wg.Lock()
+ wg.waitCalled <- wg.count
+ wg.Unlock()
+}
+
+type fakeConn struct {
+ net.Conn
+ closeCalled bool
+}
+
+func (c *fakeConn) Close() error {
+ c.closeCalled = true
+ return nil
+}
+
+type fakeListener struct {
+ acceptRelease chan bool
+ closeCalled chan bool
+}
+
+func newFakeListener() *fakeListener { return &fakeListener{make(chan bool, 1), make(chan bool, 1)} }
+
+func (l *fakeListener) Addr() net.Addr {
+ addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
+ return addr
+}
+
+func (l *fakeListener) Close() error {
+ l.closeCalled <- true
+ l.acceptRelease <- true
+ return nil
+}
+
+func (l *fakeListener) Accept() (net.Conn, error) {
+ <-l.acceptRelease
+ return nil, errors.New("connection closed")
+}
+
+// localhostCert is a PEM-encoded TLS cert with SAN IPs
+// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
+// of ASN.1 time).
+// generated from src/pkg/crypto/tls:
+// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
+var (
+ localhostCert = []byte(`-----BEGIN CERTIFICATE-----
+MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
+bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
+bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
+IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
+AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
+EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
+AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
+Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
+-----END CERTIFICATE-----`)
+
+ localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
+0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
+NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
+AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
+MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
+EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
+1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
+-----END RSA PRIVATE KEY-----`)
+)
diff --git a/vendor/github.com/braintree/manners/interfaces.go b/vendor/github.com/braintree/manners/interfaces.go
deleted file mode 100644
index fd0732857..000000000
--- a/vendor/github.com/braintree/manners/interfaces.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package manners
-
-type waitGroup interface {
- Add(int)
- Done()
- Wait()
-}
diff --git a/vendor/github.com/braintree/manners/server.go b/vendor/github.com/braintree/manners/server.go
index dfd3b873b..d51d99b81 100644
--- a/vendor/github.com/braintree/manners/server.go
+++ b/vendor/github.com/braintree/manners/server.go
@@ -60,32 +60,29 @@ import (
type GracefulServer struct {
*http.Server
- shutdown chan bool
- shutdownFinished chan bool
- wg waitGroup
- routinesCount int
+ shutdown chan bool
+ wg waitGroup
- lcsmu sync.RWMutex
- connections map[net.Conn]bool
+ lcsmu sync.RWMutex
+ lastConnState map[net.Conn]http.ConnState
up chan net.Listener // Only used by test code.
}
-// NewServer creates a new GracefulServer.
-func NewServer() *GracefulServer {
- return NewWithServer(new(http.Server))
+type waitGroup interface {
+ Add(int)
+ Done()
+ Wait()
}
// NewWithServer wraps an existing http.Server object and returns a
// GracefulServer that supports all of the original Server operations.
func NewWithServer(s *http.Server) *GracefulServer {
return &GracefulServer{
- Server: s,
- shutdown: make(chan bool),
- shutdownFinished: make(chan bool, 1),
- wg: new(sync.WaitGroup),
- routinesCount: 0,
- connections: make(map[net.Conn]bool),
+ Server: s,
+ shutdown: make(chan bool),
+ wg: new(sync.WaitGroup),
+ lastConnState: make(map[net.Conn]http.ConnState),
}
}
@@ -95,14 +92,6 @@ func (s *GracefulServer) Close() bool {
return <-s.shutdown
}
-// BlockingClose is similar to Close, except that it blocks until the last
-// connection has been closed.
-func (s *GracefulServer) BlockingClose() bool {
- result := s.Close()
- <-s.shutdownFinished
- return result
-}
-
// ListenAndServe provides a graceful equivalent of net/http.Serve.ListenAndServe.
func (s *GracefulServer) ListenAndServe() error {
addr := s.Addr
@@ -149,64 +138,56 @@ func (s *GracefulServer) ListenAndServeTLS(certFile, keyFile string) error {
// Serve provides a graceful equivalent net/http.Server.Serve.
func (s *GracefulServer) Serve(listener net.Listener) error {
- // Wrap the server HTTP handler into graceful one, that will close kept
- // alive connections if a new request is received after shutdown.
- gracefulHandler := newGracefulHandler(s.Server.Handler)
- s.Server.Handler = gracefulHandler
-
- // Start a goroutine that waits for a shutdown signal and will stop the
- // listener when it receives the signal. That in turn will result in
- // unblocking of the http.Serve call.
+ var closing int32
+
go func() {
s.shutdown <- true
close(s.shutdown)
- gracefulHandler.Close()
+ atomic.StoreInt32(&closing, 1)
s.Server.SetKeepAlivesEnabled(false)
listener.Close()
}()
originalConnState := s.Server.ConnState
-
- // s.ConnState is invoked by the net/http.Server every time a connection
- // changes state. It keeps track of each connection's state over time,
- // enabling manners to handle persisted connections correctly.
s.ConnState = func(conn net.Conn, newState http.ConnState) {
s.lcsmu.RLock()
- protected := s.connections[conn]
+ lastConnState := s.lastConnState[conn]
s.lcsmu.RUnlock()
switch newState {
-
case http.StateNew:
// New connection -> StateNew
- protected = true
s.StartRoutine()
case http.StateActive:
// (StateNew, StateIdle) -> StateActive
- if gracefulHandler.IsClosed() {
- conn.Close()
- break
+ if lastConnState == http.StateIdle {
+ // The connection transitioned from idle back to active
+ s.StartRoutine()
}
- if !protected {
- protected = true
- s.StartRoutine()
+ case http.StateIdle:
+ // StateActive -> StateIdle
+ // Immediately close newly idle connections; if not they may make
+ // one more request before SetKeepAliveEnabled(false) takes effect.
+ if atomic.LoadInt32(&closing) == 1 {
+ conn.Close()
}
+ s.FinishRoutine()
- default:
- // (StateNew, StateActive) -> (StateIdle, StateClosed, StateHiJacked)
- if protected {
+ case http.StateClosed, http.StateHijacked:
+ // (StateNew, StateActive, StateIdle) -> (StateClosed, StateHiJacked)
+ // If the connection was idle we do not need to decrement the counter.
+ if lastConnState != http.StateIdle {
s.FinishRoutine()
- protected = false
}
}
s.lcsmu.Lock()
if newState == http.StateClosed || newState == http.StateHijacked {
- delete(s.connections, conn)
+ delete(s.lastConnState, conn)
} else {
- s.connections[conn] = protected
+ s.lastConnState[conn] = newState
}
s.lcsmu.Unlock()
@@ -220,16 +201,15 @@ func (s *GracefulServer) Serve(listener net.Listener) error {
if s.up != nil {
s.up <- listener
}
-
err := s.Server.Serve(listener)
- // An error returned on shutdown is not worth reporting.
- if err != nil && gracefulHandler.IsClosed() {
- err = nil
+
+ // This block is reached when the server has received a shut down command
+ // or a real error happened.
+ if err == nil || atomic.LoadInt32(&closing) == 1 {
+ s.wg.Wait()
+ return nil
}
- // Wait for pending requests to complete regardless the Serve result.
- s.wg.Wait()
- s.shutdownFinished <- true
return err
}
@@ -237,56 +217,11 @@ func (s *GracefulServer) Serve(listener net.Listener) error {
// starts more goroutines and these goroutines are not guaranteed to finish
// before the request.
func (s *GracefulServer) StartRoutine() {
- s.lcsmu.Lock()
- defer s.lcsmu.Unlock()
s.wg.Add(1)
- s.routinesCount++
}
// FinishRoutine decrements the server's WaitGroup. Use this to complement
// StartRoutine().
func (s *GracefulServer) FinishRoutine() {
- s.lcsmu.Lock()
- defer s.lcsmu.Unlock()
s.wg.Done()
- s.routinesCount--
-}
-
-// RoutinesCount returns the number of currently running routines
-func (s *GracefulServer) RoutinesCount() int {
- s.lcsmu.RLock()
- defer s.lcsmu.RUnlock()
- return s.routinesCount
-}
-
-// gracefulHandler is used by GracefulServer to prevent calling ServeHTTP on
-// to be closed kept-alive connections during the server shutdown.
-type gracefulHandler struct {
- closed int32 // accessed atomically.
- wrapped http.Handler
-}
-
-func newGracefulHandler(wrapped http.Handler) *gracefulHandler {
- return &gracefulHandler{
- wrapped: wrapped,
- }
-}
-
-func (gh *gracefulHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if atomic.LoadInt32(&gh.closed) == 0 {
- gh.wrapped.ServeHTTP(w, r)
- return
- }
- r.Body.Close()
- // Server is shutting down at this moment, and the connection that this
- // handler is being called on is about to be closed. So we do not need to
- // actually execute the handler logic.
-}
-
-func (gh *gracefulHandler) Close() {
- atomic.StoreInt32(&gh.closed, 1)
-}
-
-func (gh *gracefulHandler) IsClosed() bool {
- return atomic.LoadInt32(&gh.closed) == 1
}
diff --git a/vendor/github.com/braintree/manners/server_test.go b/vendor/github.com/braintree/manners/server_test.go
index 994284216..108863040 100644
--- a/vendor/github.com/braintree/manners/server_test.go
+++ b/vendor/github.com/braintree/manners/server_test.go
@@ -5,34 +5,13 @@ import (
"net/http"
"testing"
"time"
-
- helpers "github.com/braintree/manners/test_helpers"
)
-type httpInterface interface {
- ListenAndServe() error
- ListenAndServeTLS(certFile, keyFile string) error
- Serve(listener net.Listener) error
-}
-
-// Test that the method signatures of the methods we override from net/http/Server match those of the original.
-func TestInterface(t *testing.T) {
- var original, ours interface{}
- original = &http.Server{}
- ours = &GracefulServer{}
- if _, ok := original.(httpInterface); !ok {
- t.Errorf("httpInterface definition does not match the canonical server!")
- }
- if _, ok := ours.(httpInterface); !ok {
- t.Errorf("GracefulServer does not implement httpInterface")
- }
-}
-
// Tests that the server allows in-flight requests to complete
// before shutting down.
func TestGracefulness(t *testing.T) {
- server := NewServer()
- wg := helpers.NewWaitGroup()
+ server := newServer()
+ wg := newTestWg()
server.wg = wg
statechanged := make(chan http.ConnState)
listener, exitchan := startServer(t, server, statechanged)
@@ -44,13 +23,14 @@ func TestGracefulness(t *testing.T) {
if err := <-client.connected; err != nil {
t.Fatal("Client failed to connect to server", err)
}
- // Even though the client is connected, the server ConnState handler may
- // not know about that yet. So wait until it is called.
- waitForState(t, statechanged, http.StateNew, "Request not received")
+ // avoid a race between the client connection and the server accept
+ if state := <-statechanged; state != http.StateNew {
+ t.Fatal("Unexpected state", state)
+ }
server.Close()
- waiting := <-wg.WaitCalled
+ waiting := <-wg.waitCalled
if waiting < 1 {
t.Errorf("Expected the waitgroup to equal 1 at shutdown; actually %d", waiting)
}
@@ -64,23 +44,11 @@ func TestGracefulness(t *testing.T) {
}
}
-// Tests that starting the server and closing in 2 new, separate goroutines doesnot
-// get flagged by the race detector (need to run 'go test' w/the -race flag)
-func TestRacyClose(t *testing.T) {
- go func() {
- ListenAndServe(":9000", nil)
- }()
-
- go func() {
- Close()
- }()
-}
-
// Tests that the server begins to shut down when told to and does not accept
// new requests once shutdown has begun
func TestShutdown(t *testing.T) {
- server := NewServer()
- wg := helpers.NewWaitGroup()
+ server := newServer()
+ wg := newTestWg()
server.wg = wg
statechanged := make(chan http.ConnState)
listener, exitchan := startServer(t, server, statechanged)
@@ -92,9 +60,10 @@ func TestShutdown(t *testing.T) {
if err := <-client1.connected; err != nil {
t.Fatal("Client failed to connect to server", err)
}
- // Even though the client is connected, the server ConnState handler may
- // not know about that yet. So wait until it is called.
- waitForState(t, statechanged, http.StateNew, "Request not received")
+ // avoid a race between the client connection and the server accept
+ if state := <-statechanged; state != http.StateNew {
+ t.Fatal("Unexpected state", state)
+ }
// start the shutdown; once it hits waitgroup.Wait()
// the listener should of been closed, though client1 is still connected
@@ -105,7 +74,7 @@ func TestShutdown(t *testing.T) {
t.Fatal("second call to Close returned true")
}
- waiting := <-wg.WaitCalled
+ waiting := <-wg.waitCalled
if waiting != 1 {
t.Errorf("Waitcount should be one, got %d", waiting)
}
@@ -124,32 +93,36 @@ func TestShutdown(t *testing.T) {
<-exitchan
}
-// If a request is sent to a closed server via a kept alive connection then
-// the server closes the connection upon receiving the request.
-func TestRequestAfterClose(t *testing.T) {
- // Given
- server := NewServer()
- srvStateChangedCh := make(chan http.ConnState, 100)
- listener, srvClosedCh := startServer(t, server, srvStateChangedCh)
+// Test that a connection is closed upon reaching an idle state if and only if the server
+// is shutting down.
+func TestCloseOnIdle(t *testing.T) {
+ server := newServer()
+ wg := newTestWg()
+ server.wg = wg
+ fl := newFakeListener()
+ runner := func() error {
+ return server.Serve(fl)
+ }
- client := newClient(listener.Addr(), false)
- client.Run()
- <-client.connected
- client.sendrequest <- true
- <-client.response
+ startGenericServer(t, server, nil, runner)
- server.Close()
- if err := <-srvClosedCh; err != nil {
- t.Error("Unexpected error during shutdown", err)
+ // Change to idle state while server is not closing; Close should not be called
+ conn := &fakeConn{}
+ server.ConnState(conn, http.StateIdle)
+ if conn.closeCalled {
+ t.Error("Close was called unexpected")
}
- // When
- client.sendrequest <- true
- rr := <-client.response
+ server.Close()
- // Then
- if rr.body != nil || rr.err != nil {
- t.Errorf("Request should be rejected, body=%v, err=%v", rr.body, rr.err)
+ // wait until the server calls Close() on the listener
+ // by that point the atomic closing variable will have been updated, avoiding a race.
+ <-fl.closeCalled
+
+ conn = &fakeConn{}
+ server.ConnState(conn, http.StateIdle)
+ if !conn.closeCalled {
+ t.Error("Close was not called")
}
}
@@ -169,8 +142,8 @@ func waitForState(t *testing.T, waiter chan http.ConnState, state http.ConnState
// Test that a request moving from active->idle->active using an actual
// network connection still results in a corect shutdown
func TestStateTransitionActiveIdleActive(t *testing.T) {
- server := NewServer()
- wg := helpers.NewWaitGroup()
+ server := newServer()
+ wg := newTestWg()
statechanged := make(chan http.ConnState)
server.wg = wg
listener, exitchan := startServer(t, server, statechanged)
@@ -186,14 +159,15 @@ func TestStateTransitionActiveIdleActive(t *testing.T) {
for i := 0; i < 2; i++ {
client.sendrequest <- true
waitForState(t, statechanged, http.StateActive, "Client failed to reach active state")
- <-client.response
+ <-client.idle
+ client.idlerelease <- true
waitForState(t, statechanged, http.StateIdle, "Client failed to reach idle state")
}
// client is now in an idle state
server.Close()
- waiting := <-wg.WaitCalled
+ waiting := <-wg.waitCalled
if waiting != 0 {
t.Errorf("Waitcount should be zero, got %d", waiting)
}
@@ -211,8 +185,8 @@ func TestStateTransitionActiveIdleClosed(t *testing.T) {
exitchan chan error
)
- keyFile, err1 := helpers.NewTempFile(helpers.Key)
- certFile, err2 := helpers.NewTempFile(helpers.Cert)
+ keyFile, err1 := newTempFile(localhostKey)
+ certFile, err2 := newTempFile(localhostCert)
defer keyFile.Unlink()
defer certFile.Unlink()
@@ -221,8 +195,8 @@ func TestStateTransitionActiveIdleClosed(t *testing.T) {
}
for _, withTLS := range []bool{false, true} {
- server := NewServer()
- wg := helpers.NewWaitGroup()
+ server := newServer()
+ wg := newTestWg()
statechanged := make(chan http.ConnState)
server.wg = wg
if withTLS {
@@ -242,11 +216,12 @@ func TestStateTransitionActiveIdleClosed(t *testing.T) {
client.sendrequest <- true
waitForState(t, statechanged, http.StateActive, "Client failed to reach active state")
- rr := <-client.response
- if rr.err != nil {
- t.Fatalf("tls=%t unexpected error from client %s", withTLS, rr.err)
+ err := <-client.idle
+ if err != nil {
+ t.Fatalf("tls=%t unexpected error from client %s", withTLS, err)
}
+ client.idlerelease <- true
waitForState(t, statechanged, http.StateIdle, "Client failed to reach idle state")
// client is now in an idle state
@@ -255,7 +230,7 @@ func TestStateTransitionActiveIdleClosed(t *testing.T) {
waitForState(t, statechanged, http.StateClosed, "Client failed to reach closed state")
server.Close()
- waiting := <-wg.WaitCalled
+ waiting := <-wg.waitCalled
if waiting != 0 {
t.Errorf("Waitcount should be zero, got %d", waiting)
}
@@ -265,25 +240,3 @@ func TestStateTransitionActiveIdleClosed(t *testing.T) {
}
}
}
-
-func TestRoutinesCount(t *testing.T) {
- var count int
- server := NewServer()
-
- count = server.RoutinesCount()
- if count != 0 {
- t.Errorf("Expected the routines count to equal 0; actually %d", count)
- }
-
- server.StartRoutine()
- count = server.RoutinesCount()
- if count != 1 {
- t.Errorf("Expected the routines count to equal 1; actually %d", count)
- }
-
- server.FinishRoutine()
- count = server.RoutinesCount()
- if count != 0 {
- t.Errorf("Expected the routines count to equal 0; actually %d", count)
- }
-}
diff --git a/vendor/github.com/braintree/manners/static.go b/vendor/github.com/braintree/manners/static.go
index b53950675..2a74b094b 100644
--- a/vendor/github.com/braintree/manners/static.go
+++ b/vendor/github.com/braintree/manners/static.go
@@ -3,23 +3,14 @@ package manners
import (
"net"
"net/http"
- "sync"
)
-var (
- defaultServer *GracefulServer
- defaultServerLock = &sync.Mutex{}
-)
-
-func init() {
- defaultServerLock.Lock()
-}
+var defaultServer *GracefulServer
// ListenAndServe provides a graceful version of the function provided by the
// net/http package. Call Close() to stop the server.
func ListenAndServe(addr string, handler http.Handler) error {
defaultServer = NewWithServer(&http.Server{Addr: addr, Handler: handler})
- defaultServerLock.Unlock()
return defaultServer.ListenAndServe()
}
@@ -27,7 +18,6 @@ func ListenAndServe(addr string, handler http.Handler) error {
// net/http package. Call Close() to stop the server.
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
defaultServer = NewWithServer(&http.Server{Addr: addr, Handler: handler})
- defaultServerLock.Unlock()
return defaultServer.ListenAndServeTLS(certFile, keyFile)
}
@@ -35,13 +25,11 @@ func ListenAndServeTLS(addr string, certFile string, keyFile string, handler htt
// package. Call Close() to stop the server.
func Serve(l net.Listener, handler http.Handler) error {
defaultServer = NewWithServer(&http.Server{Handler: handler})
- defaultServerLock.Unlock()
return defaultServer.Serve(l)
}
// Shuts down the default server used by ListenAndServe, ListenAndServeTLS and
// Serve. It returns true if it's the first time Close is called.
func Close() bool {
- defaultServerLock.Lock()
return defaultServer.Close()
}
diff --git a/vendor/github.com/braintree/manners/test_helpers/certs.go b/vendor/github.com/braintree/manners/test_helpers/certs.go
deleted file mode 100644
index ede248b3d..000000000
--- a/vendor/github.com/braintree/manners/test_helpers/certs.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package test_helpers
-
-// A PEM-encoded TLS cert with SAN IPs "127.0.0.1" and "[::1]", expiring at the
-// last second of 2049 (the end of ASN.1 time).
-
-// generated from src/pkg/crypto/tls:
-// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
-var (
- Cert = []byte(`-----BEGIN CERTIFICATE-----
-MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
-bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
-bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
-IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
-AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
-EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
-AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
-Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
------END CERTIFICATE-----`)
-
- Key = []byte(`-----BEGIN RSA PRIVATE KEY-----
-MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
-0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
-NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
-AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
-MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
-EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
-1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
------END RSA PRIVATE KEY-----`)
-)
diff --git a/vendor/github.com/braintree/manners/test_helpers/conn.go b/vendor/github.com/braintree/manners/test_helpers/conn.go
deleted file mode 100644
index 8c610f58e..000000000
--- a/vendor/github.com/braintree/manners/test_helpers/conn.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package test_helpers
-
-import "net"
-
-type Conn struct {
- net.Conn
- CloseCalled bool
-}
-
-func (c *Conn) Close() error {
- c.CloseCalled = true
- return nil
-}
diff --git a/vendor/github.com/braintree/manners/test_helpers/listener.go b/vendor/github.com/braintree/manners/test_helpers/listener.go
deleted file mode 100644
index e3af35a6e..000000000
--- a/vendor/github.com/braintree/manners/test_helpers/listener.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package test_helpers
-
-import (
- "errors"
- "net"
-)
-
-type Listener struct {
- AcceptRelease chan bool
- CloseCalled chan bool
-}
-
-func NewListener() *Listener {
- return &Listener{
- make(chan bool, 1),
- make(chan bool, 1),
- }
-}
-
-func (l *Listener) Addr() net.Addr {
- addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
- return addr
-}
-
-func (l *Listener) Close() error {
- l.CloseCalled <- true
- l.AcceptRelease <- true
- return nil
-}
-
-func (l *Listener) Accept() (net.Conn, error) {
- <-l.AcceptRelease
- return nil, errors.New("connection closed")
-}
diff --git a/vendor/github.com/braintree/manners/test_helpers/temp_file.go b/vendor/github.com/braintree/manners/test_helpers/temp_file.go
deleted file mode 100644
index c4aa263a0..000000000
--- a/vendor/github.com/braintree/manners/test_helpers/temp_file.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package test_helpers
-
-import (
- "io/ioutil"
- "os"
-)
-
-type TempFile struct {
- *os.File
-}
-
-func NewTempFile(content []byte) (*TempFile, error) {
- f, err := ioutil.TempFile("", "graceful-test")
- if err != nil {
- return nil, err
- }
-
- f.Write(content)
- return &TempFile{f}, nil
-}
-
-func (tf *TempFile) Unlink() {
- if tf.File != nil {
- os.Remove(tf.Name())
- tf.File = nil
- }
-}
diff --git a/vendor/github.com/braintree/manners/test_helpers/wait_group.go b/vendor/github.com/braintree/manners/test_helpers/wait_group.go
deleted file mode 100644
index 1df590db7..000000000
--- a/vendor/github.com/braintree/manners/test_helpers/wait_group.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package test_helpers
-
-import "sync"
-
-type WaitGroup struct {
- sync.Mutex
- Count int
- WaitCalled chan int
-}
-
-func NewWaitGroup() *WaitGroup {
- return &WaitGroup{
- WaitCalled: make(chan int, 1),
- }
-}
-
-func (wg *WaitGroup) Add(delta int) {
- wg.Lock()
- wg.Count++
- wg.Unlock()
-}
-
-func (wg *WaitGroup) Done() {
- wg.Lock()
- wg.Count--
- wg.Unlock()
-}
-
-func (wg *WaitGroup) Wait() {
- wg.Lock()
- wg.WaitCalled <- wg.Count
- wg.Unlock()
-}
diff --git a/vendor/github.com/braintree/manners/transition_test.go b/vendor/github.com/braintree/manners/transition_test.go
index 5d398514e..ffa06d3eb 100644
--- a/vendor/github.com/braintree/manners/transition_test.go
+++ b/vendor/github.com/braintree/manners/transition_test.go
@@ -1,7 +1,6 @@
package manners
import (
- helpers "github.com/braintree/manners/test_helpers"
"net/http"
"strings"
"testing"
@@ -31,18 +30,18 @@ type transitionTest struct {
}
func testStateTransition(t *testing.T, test transitionTest) {
- server := NewServer()
- wg := helpers.NewWaitGroup()
+ server := newServer()
+ wg := newTestWg()
server.wg = wg
startServer(t, server, nil)
- conn := &helpers.Conn{}
+ conn := &fakeConn{}
for _, newState := range test.states {
server.ConnState(conn, newState)
}
server.Close()
- waiting := <-wg.WaitCalled
+ waiting := <-wg.waitCalled
if waiting != test.expectedWgCount {
names := make([]string, len(test.states))
for i, s := range test.states {
diff --git a/vendor/github.com/dgryski/dgoogauth/README.md b/vendor/github.com/dgryski/dgoogauth/README.md
index 75fdde78a..372a46084 100644
--- a/vendor/github.com/dgryski/dgoogauth/README.md
+++ b/vendor/github.com/dgryski/dgoogauth/README.md
@@ -1,6 +1,6 @@
This is a Go implementation of the Google Authenticator library.
-[![Build Status](https://travis-ci.org/dgryski/dgoogauth.png)](https://travis-ci.org/dgryski/dgoogauth)
+[![GoDoc](https://godoc.org/github.com/dgryski/dgoogauth?status.svg)](https://godoc.org/github.com/dgryski/dgoogauth) [![Build Status](https://travis-ci.org/dgryski/dgoogauth.png)](https://travis-ci.org/dgryski/dgoogauth)
Copyright (c) 2012 Damian Gryski <damian@gryski.com>
This code is licensed under the Apache License, version 2.0
diff --git a/vendor/github.com/disintegration/imaging/.travis.yml b/vendor/github.com/disintegration/imaging/.travis.yml
index 3bfcffd01..0e214c00d 100644
--- a/vendor/github.com/disintegration/imaging/.travis.yml
+++ b/vendor/github.com/disintegration/imaging/.travis.yml
@@ -8,7 +8,7 @@ go:
- 1.4
- 1.5
- 1.6
- - tip
+ - 1.7
before_install:
- go get golang.org/x/tools/cmd/cover
diff --git a/vendor/github.com/garyburd/redigo/.travis.yml b/vendor/github.com/garyburd/redigo/.travis.yml
deleted file mode 100644
index 80c179fe5..000000000
--- a/vendor/github.com/garyburd/redigo/.travis.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-language: go
-sudo: false
-services:
- - redis-server
-
-go:
- - 1.4
- - 1.5
- - 1.6
- - tip
-
-script:
- - go get -t -v ./...
- - diff -u <(echo -n) <(gofmt -d .)
- - go vet $(go list ./... | grep -v /vendor/)
- - go test -v -race ./...
diff --git a/vendor/github.com/garyburd/redigo/LICENSE b/vendor/github.com/garyburd/redigo/LICENSE
deleted file mode 100644
index 67db85882..000000000
--- a/vendor/github.com/garyburd/redigo/LICENSE
+++ /dev/null
@@ -1,175 +0,0 @@
-
- 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:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) 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
-
- (d) 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.
diff --git a/vendor/github.com/garyburd/redigo/README.markdown b/vendor/github.com/garyburd/redigo/README.markdown
deleted file mode 100644
index 662690b3d..000000000
--- a/vendor/github.com/garyburd/redigo/README.markdown
+++ /dev/null
@@ -1,50 +0,0 @@
-Redigo
-======
-
-[![Build Status](https://travis-ci.org/garyburd/redigo.svg?branch=master)](https://travis-ci.org/garyburd/redigo)
-[![GoDoc](https://godoc.org/github.com/garyburd/redigo/redis?status.svg)](https://godoc.org/github.com/garyburd/redigo/redis)
-
-Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database.
-
-Features
--------
-
-* A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands.
-* [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions.
-* [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe).
-* [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool).
-* [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA.
-* [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies.
-
-Documentation
--------------
-
-- [API Reference](http://godoc.org/github.com/garyburd/redigo/redis)
-- [FAQ](https://github.com/garyburd/redigo/wiki/FAQ)
-
-Installation
-------------
-
-Install Redigo using the "go get" command:
-
- go get github.com/garyburd/redigo/redis
-
-The Go distribution is Redigo's only dependency.
-
-Related Projects
-----------------
-
-- [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo.
-- [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation.
-- [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo
-- [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo
-
-Contributing
-------------
-
-Send email to Gary Burd (address in GitHub profile) before doing any work on Redigo.
-
-License
--------
-
-Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo.go b/vendor/github.com/garyburd/redigo/internal/commandinfo.go
deleted file mode 100644
index 11e584257..000000000
--- a/vendor/github.com/garyburd/redigo/internal/commandinfo.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 Gary Burd
-//
-// 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 internal // import "github.com/garyburd/redigo/internal"
-
-import (
- "strings"
-)
-
-const (
- WatchState = 1 << iota
- MultiState
- SubscribeState
- MonitorState
-)
-
-type CommandInfo struct {
- Set, Clear int
-}
-
-var commandInfos = map[string]CommandInfo{
- "WATCH": {Set: WatchState},
- "UNWATCH": {Clear: WatchState},
- "MULTI": {Set: MultiState},
- "EXEC": {Clear: WatchState | MultiState},
- "DISCARD": {Clear: WatchState | MultiState},
- "PSUBSCRIBE": {Set: SubscribeState},
- "SUBSCRIBE": {Set: SubscribeState},
- "MONITOR": {Set: MonitorState},
-}
-
-func init() {
- for n, ci := range commandInfos {
- commandInfos[strings.ToLower(n)] = ci
- }
-}
-
-func LookupCommandInfo(commandName string) CommandInfo {
- if ci, ok := commandInfos[commandName]; ok {
- return ci
- }
- return commandInfos[strings.ToUpper(commandName)]
-}
diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go b/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
deleted file mode 100644
index 118e94b67..000000000
--- a/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package internal
-
-import "testing"
-
-func TestLookupCommandInfo(t *testing.T) {
- for _, n := range []string{"watch", "WATCH", "wAtch"} {
- if LookupCommandInfo(n) == (CommandInfo{}) {
- t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n)
- }
- }
-}
-
-func benchmarkLookupCommandInfo(b *testing.B, names ...string) {
- for i := 0; i < b.N; i++ {
- for _, c := range names {
- LookupCommandInfo(c)
- }
- }
-}
-
-func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) {
- benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR")
-}
-
-func BenchmarkLookupCommandInfoMixedCase(b *testing.B) {
- benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR")
-}
diff --git a/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go b/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
deleted file mode 100644
index b6f205b7f..000000000
--- a/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2014 Gary Burd
-//
-// 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 redistest contains utilities for writing Redigo tests.
-package redistest
-
-import (
- "errors"
- "time"
-
- "github.com/garyburd/redigo/redis"
-)
-
-type testConn struct {
- redis.Conn
-}
-
-func (t testConn) Close() error {
- _, err := t.Conn.Do("SELECT", "9")
- if err != nil {
- return nil
- }
- _, err = t.Conn.Do("FLUSHDB")
- if err != nil {
- return err
- }
- return t.Conn.Close()
-}
-
-// Dial dials the local Redis server and selects database 9. To prevent
-// stomping on real data, DialTestDB fails if database 9 contains data. The
-// returned connection flushes database 9 on close.
-func Dial() (redis.Conn, error) {
- c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second)
- if err != nil {
- return nil, err
- }
-
- _, err = c.Do("SELECT", "9")
- if err != nil {
- c.Close()
- return nil, err
- }
-
- n, err := redis.Int(c.Do("DBSIZE"))
- if err != nil {
- c.Close()
- return nil, err
- }
-
- if n != 0 {
- c.Close()
- return nil, errors.New("database #9 is not empty, test can not continue")
- }
-
- return testConn{c}, nil
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/conn.go b/vendor/github.com/garyburd/redigo/redis/conn.go
deleted file mode 100644
index ed358c601..000000000
--- a/vendor/github.com/garyburd/redigo/redis/conn.go
+++ /dev/null
@@ -1,570 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "io"
- "net"
- "net/url"
- "regexp"
- "strconv"
- "sync"
- "time"
-)
-
-// conn is the low-level implementation of Conn
-type conn struct {
-
- // Shared
- mu sync.Mutex
- pending int
- err error
- conn net.Conn
-
- // Read
- readTimeout time.Duration
- br *bufio.Reader
-
- // Write
- writeTimeout time.Duration
- bw *bufio.Writer
-
- // Scratch space for formatting argument length.
- // '*' or '$', length, "\r\n"
- lenScratch [32]byte
-
- // Scratch space for formatting integers and floats.
- numScratch [40]byte
-}
-
-// DialTimeout acts like Dial but takes timeouts for establishing the
-// connection to the server, writing a command and reading a reply.
-//
-// Deprecated: Use Dial with options instead.
-func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) {
- return Dial(network, address,
- DialConnectTimeout(connectTimeout),
- DialReadTimeout(readTimeout),
- DialWriteTimeout(writeTimeout))
-}
-
-// DialOption specifies an option for dialing a Redis server.
-type DialOption struct {
- f func(*dialOptions)
-}
-
-type dialOptions struct {
- readTimeout time.Duration
- writeTimeout time.Duration
- dial func(network, addr string) (net.Conn, error)
- db int
- password string
-}
-
-// DialReadTimeout specifies the timeout for reading a single command reply.
-func DialReadTimeout(d time.Duration) DialOption {
- return DialOption{func(do *dialOptions) {
- do.readTimeout = d
- }}
-}
-
-// DialWriteTimeout specifies the timeout for writing a single command.
-func DialWriteTimeout(d time.Duration) DialOption {
- return DialOption{func(do *dialOptions) {
- do.writeTimeout = d
- }}
-}
-
-// DialConnectTimeout specifies the timeout for connecting to the Redis server.
-func DialConnectTimeout(d time.Duration) DialOption {
- return DialOption{func(do *dialOptions) {
- dialer := net.Dialer{Timeout: d}
- do.dial = dialer.Dial
- }}
-}
-
-// DialNetDial specifies a custom dial function for creating TCP
-// connections. If this option is left out, then net.Dial is
-// used. DialNetDial overrides DialConnectTimeout.
-func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
- return DialOption{func(do *dialOptions) {
- do.dial = dial
- }}
-}
-
-// DialDatabase specifies the database to select when dialing a connection.
-func DialDatabase(db int) DialOption {
- return DialOption{func(do *dialOptions) {
- do.db = db
- }}
-}
-
-// DialPassword specifies the password to use when connecting to
-// the Redis server.
-func DialPassword(password string) DialOption {
- return DialOption{func(do *dialOptions) {
- do.password = password
- }}
-}
-
-// Dial connects to the Redis server at the given network and
-// address using the specified options.
-func Dial(network, address string, options ...DialOption) (Conn, error) {
- do := dialOptions{
- dial: net.Dial,
- }
- for _, option := range options {
- option.f(&do)
- }
-
- netConn, err := do.dial(network, address)
- if err != nil {
- return nil, err
- }
- c := &conn{
- conn: netConn,
- bw: bufio.NewWriter(netConn),
- br: bufio.NewReader(netConn),
- readTimeout: do.readTimeout,
- writeTimeout: do.writeTimeout,
- }
-
- if do.password != "" {
- if _, err := c.Do("AUTH", do.password); err != nil {
- netConn.Close()
- return nil, err
- }
- }
-
- if do.db != 0 {
- if _, err := c.Do("SELECT", do.db); err != nil {
- netConn.Close()
- return nil, err
- }
- }
-
- return c, nil
-}
-
-var pathDBRegexp = regexp.MustCompile(`/(\d*)\z`)
-
-// DialURL connects to a Redis server at the given URL using the Redis
-// URI scheme. URLs should follow the draft IANA specification for the
-// scheme (https://www.iana.org/assignments/uri-schemes/prov/redis).
-func DialURL(rawurl string, options ...DialOption) (Conn, error) {
- u, err := url.Parse(rawurl)
- if err != nil {
- return nil, err
- }
-
- if u.Scheme != "redis" {
- return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
- }
-
- // As per the IANA draft spec, the host defaults to localhost and
- // the port defaults to 6379.
- host, port, err := net.SplitHostPort(u.Host)
- if err != nil {
- // assume port is missing
- host = u.Host
- port = "6379"
- }
- if host == "" {
- host = "localhost"
- }
- address := net.JoinHostPort(host, port)
-
- if u.User != nil {
- password, isSet := u.User.Password()
- if isSet {
- options = append(options, DialPassword(password))
- }
- }
-
- match := pathDBRegexp.FindStringSubmatch(u.Path)
- if len(match) == 2 {
- db := 0
- if len(match[1]) > 0 {
- db, err = strconv.Atoi(match[1])
- if err != nil {
- return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
- }
- }
- if db != 0 {
- options = append(options, DialDatabase(db))
- }
- } else if u.Path != "" {
- return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
- }
-
- return Dial("tcp", address, options...)
-}
-
-// NewConn returns a new Redigo connection for the given net connection.
-func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn {
- return &conn{
- conn: netConn,
- bw: bufio.NewWriter(netConn),
- br: bufio.NewReader(netConn),
- readTimeout: readTimeout,
- writeTimeout: writeTimeout,
- }
-}
-
-func (c *conn) Close() error {
- c.mu.Lock()
- err := c.err
- if c.err == nil {
- c.err = errors.New("redigo: closed")
- err = c.conn.Close()
- }
- c.mu.Unlock()
- return err
-}
-
-func (c *conn) fatal(err error) error {
- c.mu.Lock()
- if c.err == nil {
- c.err = err
- // Close connection to force errors on subsequent calls and to unblock
- // other reader or writer.
- c.conn.Close()
- }
- c.mu.Unlock()
- return err
-}
-
-func (c *conn) Err() error {
- c.mu.Lock()
- err := c.err
- c.mu.Unlock()
- return err
-}
-
-func (c *conn) writeLen(prefix byte, n int) error {
- c.lenScratch[len(c.lenScratch)-1] = '\n'
- c.lenScratch[len(c.lenScratch)-2] = '\r'
- i := len(c.lenScratch) - 3
- for {
- c.lenScratch[i] = byte('0' + n%10)
- i -= 1
- n = n / 10
- if n == 0 {
- break
- }
- }
- c.lenScratch[i] = prefix
- _, err := c.bw.Write(c.lenScratch[i:])
- return err
-}
-
-func (c *conn) writeString(s string) error {
- c.writeLen('$', len(s))
- c.bw.WriteString(s)
- _, err := c.bw.WriteString("\r\n")
- return err
-}
-
-func (c *conn) writeBytes(p []byte) error {
- c.writeLen('$', len(p))
- c.bw.Write(p)
- _, err := c.bw.WriteString("\r\n")
- return err
-}
-
-func (c *conn) writeInt64(n int64) error {
- return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
-}
-
-func (c *conn) writeFloat64(n float64) error {
- return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
-}
-
-func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
- c.writeLen('*', 1+len(args))
- err = c.writeString(cmd)
- for _, arg := range args {
- if err != nil {
- break
- }
- switch arg := arg.(type) {
- case string:
- err = c.writeString(arg)
- case []byte:
- err = c.writeBytes(arg)
- case int:
- err = c.writeInt64(int64(arg))
- case int64:
- err = c.writeInt64(arg)
- case float64:
- err = c.writeFloat64(arg)
- case bool:
- if arg {
- err = c.writeString("1")
- } else {
- err = c.writeString("0")
- }
- case nil:
- err = c.writeString("")
- default:
- var buf bytes.Buffer
- fmt.Fprint(&buf, arg)
- err = c.writeBytes(buf.Bytes())
- }
- }
- return err
-}
-
-type protocolError string
-
-func (pe protocolError) Error() string {
- return fmt.Sprintf("redigo: %s (possible server error or unsupported concurrent read by application)", string(pe))
-}
-
-func (c *conn) readLine() ([]byte, error) {
- p, err := c.br.ReadSlice('\n')
- if err == bufio.ErrBufferFull {
- return nil, protocolError("long response line")
- }
- if err != nil {
- return nil, err
- }
- i := len(p) - 2
- if i < 0 || p[i] != '\r' {
- return nil, protocolError("bad response line terminator")
- }
- return p[:i], nil
-}
-
-// parseLen parses bulk string and array lengths.
-func parseLen(p []byte) (int, error) {
- if len(p) == 0 {
- return -1, protocolError("malformed length")
- }
-
- if p[0] == '-' && len(p) == 2 && p[1] == '1' {
- // handle $-1 and $-1 null replies.
- return -1, nil
- }
-
- var n int
- for _, b := range p {
- n *= 10
- if b < '0' || b > '9' {
- return -1, protocolError("illegal bytes in length")
- }
- n += int(b - '0')
- }
-
- return n, nil
-}
-
-// parseInt parses an integer reply.
-func parseInt(p []byte) (interface{}, error) {
- if len(p) == 0 {
- return 0, protocolError("malformed integer")
- }
-
- var negate bool
- if p[0] == '-' {
- negate = true
- p = p[1:]
- if len(p) == 0 {
- return 0, protocolError("malformed integer")
- }
- }
-
- var n int64
- for _, b := range p {
- n *= 10
- if b < '0' || b > '9' {
- return 0, protocolError("illegal bytes in length")
- }
- n += int64(b - '0')
- }
-
- if negate {
- n = -n
- }
- return n, nil
-}
-
-var (
- okReply interface{} = "OK"
- pongReply interface{} = "PONG"
-)
-
-func (c *conn) readReply() (interface{}, error) {
- line, err := c.readLine()
- if err != nil {
- return nil, err
- }
- if len(line) == 0 {
- return nil, protocolError("short response line")
- }
- switch line[0] {
- case '+':
- switch {
- case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
- // Avoid allocation for frequent "+OK" response.
- return okReply, nil
- case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
- // Avoid allocation in PING command benchmarks :)
- return pongReply, nil
- default:
- return string(line[1:]), nil
- }
- case '-':
- return Error(string(line[1:])), nil
- case ':':
- return parseInt(line[1:])
- case '$':
- n, err := parseLen(line[1:])
- if n < 0 || err != nil {
- return nil, err
- }
- p := make([]byte, n)
- _, err = io.ReadFull(c.br, p)
- if err != nil {
- return nil, err
- }
- if line, err := c.readLine(); err != nil {
- return nil, err
- } else if len(line) != 0 {
- return nil, protocolError("bad bulk string format")
- }
- return p, nil
- case '*':
- n, err := parseLen(line[1:])
- if n < 0 || err != nil {
- return nil, err
- }
- r := make([]interface{}, n)
- for i := range r {
- r[i], err = c.readReply()
- if err != nil {
- return nil, err
- }
- }
- return r, nil
- }
- return nil, protocolError("unexpected response line")
-}
-
-func (c *conn) Send(cmd string, args ...interface{}) error {
- c.mu.Lock()
- c.pending += 1
- c.mu.Unlock()
- if c.writeTimeout != 0 {
- c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
- }
- if err := c.writeCommand(cmd, args); err != nil {
- return c.fatal(err)
- }
- return nil
-}
-
-func (c *conn) Flush() error {
- if c.writeTimeout != 0 {
- c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
- }
- if err := c.bw.Flush(); err != nil {
- return c.fatal(err)
- }
- return nil
-}
-
-func (c *conn) Receive() (reply interface{}, err error) {
- if c.readTimeout != 0 {
- c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
- }
- if reply, err = c.readReply(); err != nil {
- return nil, c.fatal(err)
- }
- // When using pub/sub, the number of receives can be greater than the
- // number of sends. To enable normal use of the connection after
- // unsubscribing from all channels, we do not decrement pending to a
- // negative value.
- //
- // The pending field is decremented after the reply is read to handle the
- // case where Receive is called before Send.
- c.mu.Lock()
- if c.pending > 0 {
- c.pending -= 1
- }
- c.mu.Unlock()
- if err, ok := reply.(Error); ok {
- return nil, err
- }
- return
-}
-
-func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
- c.mu.Lock()
- pending := c.pending
- c.pending = 0
- c.mu.Unlock()
-
- if cmd == "" && pending == 0 {
- return nil, nil
- }
-
- if c.writeTimeout != 0 {
- c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
- }
-
- if cmd != "" {
- if err := c.writeCommand(cmd, args); err != nil {
- return nil, c.fatal(err)
- }
- }
-
- if err := c.bw.Flush(); err != nil {
- return nil, c.fatal(err)
- }
-
- if c.readTimeout != 0 {
- c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
- }
-
- if cmd == "" {
- reply := make([]interface{}, pending)
- for i := range reply {
- r, e := c.readReply()
- if e != nil {
- return nil, c.fatal(e)
- }
- reply[i] = r
- }
- return reply, nil
- }
-
- var err error
- var reply interface{}
- for i := 0; i <= pending; i++ {
- var e error
- if reply, e = c.readReply(); e != nil {
- return nil, c.fatal(e)
- }
- if e, ok := reply.(Error); ok && err == nil {
- err = e
- }
- }
- return reply, err
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/conn_test.go b/vendor/github.com/garyburd/redigo/redis/conn_test.go
deleted file mode 100644
index 2ead63326..000000000
--- a/vendor/github.com/garyburd/redigo/redis/conn_test.go
+++ /dev/null
@@ -1,670 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis_test
-
-import (
- "bytes"
- "io"
- "math"
- "net"
- "os"
- "reflect"
- "strings"
- "testing"
- "time"
-
- "github.com/garyburd/redigo/redis"
-)
-
-type testConn struct {
- io.Reader
- io.Writer
-}
-
-func (*testConn) Close() error { return nil }
-func (*testConn) LocalAddr() net.Addr { return nil }
-func (*testConn) RemoteAddr() net.Addr { return nil }
-func (*testConn) SetDeadline(t time.Time) error { return nil }
-func (*testConn) SetReadDeadline(t time.Time) error { return nil }
-func (*testConn) SetWriteDeadline(t time.Time) error { return nil }
-
-func dialTestConn(r io.Reader, w io.Writer) redis.DialOption {
- return redis.DialNetDial(func(net, addr string) (net.Conn, error) {
- return &testConn{Reader: r, Writer: w}, nil
- })
-}
-
-var writeTests = []struct {
- args []interface{}
- expected string
-}{
- {
- []interface{}{"SET", "key", "value"},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
- },
- {
- []interface{}{"SET", "key", "value"},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
- },
- {
- []interface{}{"SET", "key", byte(100)},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
- },
- {
- []interface{}{"SET", "key", 100},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
- },
- {
- []interface{}{"SET", "key", int64(math.MinInt64)},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$20\r\n-9223372036854775808\r\n",
- },
- {
- []interface{}{"SET", "key", float64(1349673917.939762)},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$21\r\n1.349673917939762e+09\r\n",
- },
- {
- []interface{}{"SET", "key", ""},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
- },
- {
- []interface{}{"SET", "key", nil},
- "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
- },
- {
- []interface{}{"ECHO", true, false},
- "*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n",
- },
-}
-
-func TestWrite(t *testing.T) {
- for _, tt := range writeTests {
- var buf bytes.Buffer
- c, _ := redis.Dial("", "", dialTestConn(nil, &buf))
- err := c.Send(tt.args[0].(string), tt.args[1:]...)
- if err != nil {
- t.Errorf("Send(%v) returned error %v", tt.args, err)
- continue
- }
- c.Flush()
- actual := buf.String()
- if actual != tt.expected {
- t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected)
- }
- }
-}
-
-var errorSentinel = &struct{}{}
-
-var readTests = []struct {
- reply string
- expected interface{}
-}{
- {
- "+OK\r\n",
- "OK",
- },
- {
- "+PONG\r\n",
- "PONG",
- },
- {
- "@OK\r\n",
- errorSentinel,
- },
- {
- "$6\r\nfoobar\r\n",
- []byte("foobar"),
- },
- {
- "$-1\r\n",
- nil,
- },
- {
- ":1\r\n",
- int64(1),
- },
- {
- ":-2\r\n",
- int64(-2),
- },
- {
- "*0\r\n",
- []interface{}{},
- },
- {
- "*-1\r\n",
- nil,
- },
- {
- "*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n",
- []interface{}{[]byte("foo"), []byte("bar"), []byte("Hello"), []byte("World")},
- },
- {
- "*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n",
- []interface{}{[]byte("foo"), nil, []byte("bar")},
- },
-
- {
- // "x" is not a valid length
- "$x\r\nfoobar\r\n",
- errorSentinel,
- },
- {
- // -2 is not a valid length
- "$-2\r\n",
- errorSentinel,
- },
- {
- // "x" is not a valid integer
- ":x\r\n",
- errorSentinel,
- },
- {
- // missing \r\n following value
- "$6\r\nfoobar",
- errorSentinel,
- },
- {
- // short value
- "$6\r\nxx",
- errorSentinel,
- },
- {
- // long value
- "$6\r\nfoobarx\r\n",
- errorSentinel,
- },
-}
-
-func TestRead(t *testing.T) {
- for _, tt := range readTests {
- c, _ := redis.Dial("", "", dialTestConn(strings.NewReader(tt.reply), nil))
- actual, err := c.Receive()
- if tt.expected == errorSentinel {
- if err == nil {
- t.Errorf("Receive(%q) did not return expected error", tt.reply)
- }
- } else {
- if err != nil {
- t.Errorf("Receive(%q) returned error %v", tt.reply, err)
- continue
- }
- if !reflect.DeepEqual(actual, tt.expected) {
- t.Errorf("Receive(%q) = %v, want %v", tt.reply, actual, tt.expected)
- }
- }
- }
-}
-
-var testCommands = []struct {
- args []interface{}
- expected interface{}
-}{
- {
- []interface{}{"PING"},
- "PONG",
- },
- {
- []interface{}{"SET", "foo", "bar"},
- "OK",
- },
- {
- []interface{}{"GET", "foo"},
- []byte("bar"),
- },
- {
- []interface{}{"GET", "nokey"},
- nil,
- },
- {
- []interface{}{"MGET", "nokey", "foo"},
- []interface{}{nil, []byte("bar")},
- },
- {
- []interface{}{"INCR", "mycounter"},
- int64(1),
- },
- {
- []interface{}{"LPUSH", "mylist", "foo"},
- int64(1),
- },
- {
- []interface{}{"LPUSH", "mylist", "bar"},
- int64(2),
- },
- {
- []interface{}{"LRANGE", "mylist", 0, -1},
- []interface{}{[]byte("bar"), []byte("foo")},
- },
- {
- []interface{}{"MULTI"},
- "OK",
- },
- {
- []interface{}{"LRANGE", "mylist", 0, -1},
- "QUEUED",
- },
- {
- []interface{}{"PING"},
- "QUEUED",
- },
- {
- []interface{}{"EXEC"},
- []interface{}{
- []interface{}{[]byte("bar"), []byte("foo")},
- "PONG",
- },
- },
-}
-
-func TestDoCommands(t *testing.T) {
- c, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- for _, cmd := range testCommands {
- actual, err := c.Do(cmd.args[0].(string), cmd.args[1:]...)
- if err != nil {
- t.Errorf("Do(%v) returned error %v", cmd.args, err)
- continue
- }
- if !reflect.DeepEqual(actual, cmd.expected) {
- t.Errorf("Do(%v) = %v, want %v", cmd.args, actual, cmd.expected)
- }
- }
-}
-
-func TestPipelineCommands(t *testing.T) {
- c, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- for _, cmd := range testCommands {
- if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
- t.Fatalf("Send(%v) returned error %v", cmd.args, err)
- }
- }
- if err := c.Flush(); err != nil {
- t.Errorf("Flush() returned error %v", err)
- }
- for _, cmd := range testCommands {
- actual, err := c.Receive()
- if err != nil {
- t.Fatalf("Receive(%v) returned error %v", cmd.args, err)
- }
- if !reflect.DeepEqual(actual, cmd.expected) {
- t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
- }
- }
-}
-
-func TestBlankCommmand(t *testing.T) {
- c, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- for _, cmd := range testCommands {
- if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
- t.Fatalf("Send(%v) returned error %v", cmd.args, err)
- }
- }
- reply, err := redis.Values(c.Do(""))
- if err != nil {
- t.Fatalf("Do() returned error %v", err)
- }
- if len(reply) != len(testCommands) {
- t.Fatalf("len(reply)=%d, want %d", len(reply), len(testCommands))
- }
- for i, cmd := range testCommands {
- actual := reply[i]
- if !reflect.DeepEqual(actual, cmd.expected) {
- t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
- }
- }
-}
-
-func TestRecvBeforeSend(t *testing.T) {
- c, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
- done := make(chan struct{})
- go func() {
- c.Receive()
- close(done)
- }()
- time.Sleep(time.Millisecond)
- c.Send("PING")
- c.Flush()
- <-done
- _, err = c.Do("")
- if err != nil {
- t.Fatalf("error=%v", err)
- }
-}
-
-func TestError(t *testing.T) {
- c, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- c.Do("SET", "key", "val")
- _, err = c.Do("HSET", "key", "fld", "val")
- if err == nil {
- t.Errorf("Expected err for HSET on string key.")
- }
- if c.Err() != nil {
- t.Errorf("Conn has Err()=%v, expect nil", c.Err())
- }
- _, err = c.Do("SET", "key", "val")
- if err != nil {
- t.Errorf("Do(SET, key, val) returned error %v, expected nil.", err)
- }
-}
-
-func TestReadTimeout(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("net.Listen returned %v", err)
- }
- defer l.Close()
-
- go func() {
- for {
- c, err := l.Accept()
- if err != nil {
- return
- }
- go func() {
- time.Sleep(time.Second)
- c.Write([]byte("+OK\r\n"))
- c.Close()
- }()
- }
- }()
-
- // Do
-
- c1, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
- if err != nil {
- t.Fatalf("redis.Dial returned %v", err)
- }
- defer c1.Close()
-
- _, err = c1.Do("PING")
- if err == nil {
- t.Fatalf("c1.Do() returned nil, expect error")
- }
- if c1.Err() == nil {
- t.Fatalf("c1.Err() = nil, expect error")
- }
-
- // Send/Flush/Receive
-
- c2, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
- if err != nil {
- t.Fatalf("redis.Dial returned %v", err)
- }
- defer c2.Close()
-
- c2.Send("PING")
- c2.Flush()
- _, err = c2.Receive()
- if err == nil {
- t.Fatalf("c2.Receive() returned nil, expect error")
- }
- if c2.Err() == nil {
- t.Fatalf("c2.Err() = nil, expect error")
- }
-}
-
-var dialErrors = []struct {
- rawurl string
- expectedError string
-}{
- {
- "localhost",
- "invalid redis URL scheme",
- },
- // The error message for invalid hosts is diffferent in different
- // versions of Go, so just check that there is an error message.
- {
- "redis://weird url",
- "",
- },
- {
- "redis://foo:bar:baz",
- "",
- },
- {
- "http://www.google.com",
- "invalid redis URL scheme: http",
- },
- {
- "redis://localhost:6379/abc123",
- "invalid database: abc123",
- },
-}
-
-func TestDialURLErrors(t *testing.T) {
- for _, d := range dialErrors {
- _, err := redis.DialURL(d.rawurl)
- if err == nil || !strings.Contains(err.Error(), d.expectedError) {
- t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError)
- }
- }
-}
-
-func TestDialURLPort(t *testing.T) {
- checkPort := func(network, address string) (net.Conn, error) {
- if address != "localhost:6379" {
- t.Errorf("DialURL did not set port to 6379 by default (got %v)", address)
- }
- return nil, nil
- }
- _, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
- if err != nil {
- t.Error("dial error:", err)
- }
-}
-
-func TestDialURLHost(t *testing.T) {
- checkHost := func(network, address string) (net.Conn, error) {
- if address != "localhost:6379" {
- t.Errorf("DialURL did not set host to localhost by default (got %v)", address)
- }
- return nil, nil
- }
- _, err := redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
- if err != nil {
- t.Error("dial error:", err)
- }
-}
-
-func TestDialURLPassword(t *testing.T) {
- var buf bytes.Buffer
- _, err := redis.DialURL("redis://x:abc123@localhost", dialTestConn(strings.NewReader("+OK\r\n"), &buf))
- if err != nil {
- t.Error("dial error:", err)
- }
- expected := "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"
- actual := buf.String()
- if actual != expected {
- t.Errorf("commands = %q, want %q", actual, expected)
- }
-}
-
-func TestDialURLDatabase(t *testing.T) {
- var buf3 bytes.Buffer
- _, err3 := redis.DialURL("redis://localhost/3", dialTestConn(strings.NewReader("+OK\r\n"), &buf3))
- if err3 != nil {
- t.Error("dial error:", err3)
- }
- expected3 := "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"
- actual3 := buf3.String()
- if actual3 != expected3 {
- t.Errorf("commands = %q, want %q", actual3, expected3)
- }
- // empty DB means 0
- var buf0 bytes.Buffer
- _, err0 := redis.DialURL("redis://localhost/", dialTestConn(strings.NewReader("+OK\r\n"), &buf0))
- if err0 != nil {
- t.Error("dial error:", err0)
- }
- expected0 := ""
- actual0 := buf0.String()
- if actual0 != expected0 {
- t.Errorf("commands = %q, want %q", actual0, expected0)
- }
-}
-
-// Connect to local instance of Redis running on the default port.
-func ExampleDial() {
- c, err := redis.Dial("tcp", ":6379")
- if err != nil {
- // handle error
- }
- defer c.Close()
-}
-
-// Connect to remote instance of Redis using a URL.
-func ExampleDialURL() {
- c, err := redis.DialURL(os.Getenv("REDIS_URL"))
- if err != nil {
- // handle connection error
- }
- defer c.Close()
-}
-
-// TextExecError tests handling of errors in a transaction. See
-// http://redis.io/topics/transactions for information on how Redis handles
-// errors in a transaction.
-func TestExecError(t *testing.T) {
- c, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- // Execute commands that fail before EXEC is called.
-
- c.Do("DEL", "k0")
- c.Do("ZADD", "k0", 0, 0)
- c.Send("MULTI")
- c.Send("NOTACOMMAND", "k0", 0, 0)
- c.Send("ZINCRBY", "k0", 0, 0)
- v, err := c.Do("EXEC")
- if err == nil {
- t.Fatalf("EXEC returned values %v, expected error", v)
- }
-
- // Execute commands that fail after EXEC is called. The first command
- // returns an error.
-
- c.Do("DEL", "k1")
- c.Do("ZADD", "k1", 0, 0)
- c.Send("MULTI")
- c.Send("HSET", "k1", 0, 0)
- c.Send("ZINCRBY", "k1", 0, 0)
- v, err = c.Do("EXEC")
- if err != nil {
- t.Fatalf("EXEC returned error %v", err)
- }
-
- vs, err := redis.Values(v, nil)
- if err != nil {
- t.Fatalf("Values(v) returned error %v", err)
- }
-
- if len(vs) != 2 {
- t.Fatalf("len(vs) == %d, want 2", len(vs))
- }
-
- if _, ok := vs[0].(error); !ok {
- t.Fatalf("first result is type %T, expected error", vs[0])
- }
-
- if _, ok := vs[1].([]byte); !ok {
- t.Fatalf("second result is type %T, expected []byte", vs[1])
- }
-
- // Execute commands that fail after EXEC is called. The second command
- // returns an error.
-
- c.Do("ZADD", "k2", 0, 0)
- c.Send("MULTI")
- c.Send("ZINCRBY", "k2", 0, 0)
- c.Send("HSET", "k2", 0, 0)
- v, err = c.Do("EXEC")
- if err != nil {
- t.Fatalf("EXEC returned error %v", err)
- }
-
- vs, err = redis.Values(v, nil)
- if err != nil {
- t.Fatalf("Values(v) returned error %v", err)
- }
-
- if len(vs) != 2 {
- t.Fatalf("len(vs) == %d, want 2", len(vs))
- }
-
- if _, ok := vs[0].([]byte); !ok {
- t.Fatalf("first result is type %T, expected []byte", vs[0])
- }
-
- if _, ok := vs[1].(error); !ok {
- t.Fatalf("second result is type %T, expected error", vs[2])
- }
-}
-
-func BenchmarkDoEmpty(b *testing.B) {
- b.StopTimer()
- c, err := redis.DialDefaultServer()
- if err != nil {
- b.Fatal(err)
- }
- defer c.Close()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- if _, err := c.Do(""); err != nil {
- b.Fatal(err)
- }
- }
-}
-
-func BenchmarkDoPing(b *testing.B) {
- b.StopTimer()
- c, err := redis.DialDefaultServer()
- if err != nil {
- b.Fatal(err)
- }
- defer c.Close()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- if _, err := c.Do("PING"); err != nil {
- b.Fatal(err)
- }
- }
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/doc.go b/vendor/github.com/garyburd/redigo/redis/doc.go
deleted file mode 100644
index a5cd454af..000000000
--- a/vendor/github.com/garyburd/redigo/redis/doc.go
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis is a client for the Redis database.
-//
-// The Redigo FAQ (https://github.com/garyburd/redigo/wiki/FAQ) contains more
-// documentation about this package.
-//
-// Connections
-//
-// The Conn interface is the primary interface for working with Redis.
-// Applications create connections by calling the Dial, DialWithTimeout or
-// NewConn functions. In the future, functions will be added for creating
-// sharded and other types of connections.
-//
-// The application must call the connection Close method when the application
-// is done with the connection.
-//
-// Executing Commands
-//
-// The Conn interface has a generic method for executing Redis commands:
-//
-// Do(commandName string, args ...interface{}) (reply interface{}, err error)
-//
-// The Redis command reference (http://redis.io/commands) lists the available
-// commands. An example of using the Redis APPEND command is:
-//
-// n, err := conn.Do("APPEND", "key", "value")
-//
-// The Do method converts command arguments to binary strings for transmission
-// to the server as follows:
-//
-// Go Type Conversion
-// []byte Sent as is
-// string Sent as is
-// int, int64 strconv.FormatInt(v)
-// float64 strconv.FormatFloat(v, 'g', -1, 64)
-// bool true -> "1", false -> "0"
-// nil ""
-// all other types fmt.Print(v)
-//
-// Redis command reply types are represented using the following Go types:
-//
-// Redis type Go type
-// error redis.Error
-// integer int64
-// simple string string
-// bulk string []byte or nil if value not present.
-// array []interface{} or nil if value not present.
-//
-// Use type assertions or the reply helper functions to convert from
-// interface{} to the specific Go type for the command result.
-//
-// Pipelining
-//
-// Connections support pipelining using the Send, Flush and Receive methods.
-//
-// Send(commandName string, args ...interface{}) error
-// Flush() error
-// Receive() (reply interface{}, err error)
-//
-// Send writes the command to the connection's output buffer. Flush flushes the
-// connection's output buffer to the server. Receive reads a single reply from
-// the server. The following example shows a simple pipeline.
-//
-// c.Send("SET", "foo", "bar")
-// c.Send("GET", "foo")
-// c.Flush()
-// c.Receive() // reply from SET
-// v, err = c.Receive() // reply from GET
-//
-// The Do method combines the functionality of the Send, Flush and Receive
-// methods. The Do method starts by writing the command and flushing the output
-// buffer. Next, the Do method receives all pending replies including the reply
-// for the command just sent by Do. If any of the received replies is an error,
-// then Do returns the error. If there are no errors, then Do returns the last
-// reply. If the command argument to the Do method is "", then the Do method
-// will flush the output buffer and receive pending replies without sending a
-// command.
-//
-// Use the Send and Do methods to implement pipelined transactions.
-//
-// c.Send("MULTI")
-// c.Send("INCR", "foo")
-// c.Send("INCR", "bar")
-// r, err := c.Do("EXEC")
-// fmt.Println(r) // prints [1, 1]
-//
-// Concurrency
-//
-// Connections do not support concurrent calls to the write methods (Send,
-// Flush) or concurrent calls to the read method (Receive). Connections do
-// allow a concurrent reader and writer.
-//
-// Because the Do method combines the functionality of Send, Flush and Receive,
-// the Do method cannot be called concurrently with the other methods.
-//
-// For full concurrent access to Redis, use the thread-safe Pool to get and
-// release connections from within a goroutine.
-//
-// Publish and Subscribe
-//
-// Use the Send, Flush and Receive methods to implement Pub/Sub subscribers.
-//
-// c.Send("SUBSCRIBE", "example")
-// c.Flush()
-// for {
-// reply, err := c.Receive()
-// if err != nil {
-// return err
-// }
-// // process pushed message
-// }
-//
-// The PubSubConn type wraps a Conn with convenience methods for implementing
-// subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods
-// send and flush a subscription management command. The receive method
-// converts a pushed message to convenient types for use in a type switch.
-//
-// psc := redis.PubSubConn{c}
-// psc.Subscribe("example")
-// for {
-// switch v := psc.Receive().(type) {
-// case redis.Message:
-// fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
-// case redis.Subscription:
-// fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
-// case error:
-// return v
-// }
-// }
-//
-// Reply Helpers
-//
-// The Bool, Int, Bytes, String, Strings and Values functions convert a reply
-// to a value of a specific type. To allow convenient wrapping of calls to the
-// connection Do and Receive methods, the functions take a second argument of
-// type error. If the error is non-nil, then the helper function returns the
-// error. If the error is nil, the function converts the reply to the specified
-// type:
-//
-// exists, err := redis.Bool(c.Do("EXISTS", "foo"))
-// if err != nil {
-// // handle error return from c.Do or type conversion error.
-// }
-//
-// The Scan function converts elements of a array reply to Go types:
-//
-// var value1 int
-// var value2 string
-// reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
-// if err != nil {
-// // handle error
-// }
-// if _, err := redis.Scan(reply, &value1, &value2); err != nil {
-// // handle error
-// }
-package redis // import "github.com/garyburd/redigo/redis"
diff --git a/vendor/github.com/garyburd/redigo/redis/log.go b/vendor/github.com/garyburd/redigo/redis/log.go
deleted file mode 100644
index 129b86d67..000000000
--- a/vendor/github.com/garyburd/redigo/redis/log.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import (
- "bytes"
- "fmt"
- "log"
-)
-
-// NewLoggingConn returns a logging wrapper around a connection.
-func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn {
- if prefix != "" {
- prefix = prefix + "."
- }
- return &loggingConn{conn, logger, prefix}
-}
-
-type loggingConn struct {
- Conn
- logger *log.Logger
- prefix string
-}
-
-func (c *loggingConn) Close() error {
- err := c.Conn.Close()
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err)
- c.logger.Output(2, buf.String())
- return err
-}
-
-func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) {
- const chop = 32
- switch v := v.(type) {
- case []byte:
- if len(v) > chop {
- fmt.Fprintf(buf, "%q...", v[:chop])
- } else {
- fmt.Fprintf(buf, "%q", v)
- }
- case string:
- if len(v) > chop {
- fmt.Fprintf(buf, "%q...", v[:chop])
- } else {
- fmt.Fprintf(buf, "%q", v)
- }
- case []interface{}:
- if len(v) == 0 {
- buf.WriteString("[]")
- } else {
- sep := "["
- fin := "]"
- if len(v) > chop {
- v = v[:chop]
- fin = "...]"
- }
- for _, vv := range v {
- buf.WriteString(sep)
- c.printValue(buf, vv)
- sep = ", "
- }
- buf.WriteString(fin)
- }
- default:
- fmt.Fprint(buf, v)
- }
-}
-
-func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "%s%s(", c.prefix, method)
- if method != "Receive" {
- buf.WriteString(commandName)
- for _, arg := range args {
- buf.WriteString(", ")
- c.printValue(&buf, arg)
- }
- }
- buf.WriteString(") -> (")
- if method != "Send" {
- c.printValue(&buf, reply)
- buf.WriteString(", ")
- }
- fmt.Fprintf(&buf, "%v)", err)
- c.logger.Output(3, buf.String())
-}
-
-func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) {
- reply, err := c.Conn.Do(commandName, args...)
- c.print("Do", commandName, args, reply, err)
- return reply, err
-}
-
-func (c *loggingConn) Send(commandName string, args ...interface{}) error {
- err := c.Conn.Send(commandName, args...)
- c.print("Send", commandName, args, nil, err)
- return err
-}
-
-func (c *loggingConn) Receive() (interface{}, error) {
- reply, err := c.Conn.Receive()
- c.print("Receive", "", nil, reply, err)
- return reply, err
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/pool.go b/vendor/github.com/garyburd/redigo/redis/pool.go
deleted file mode 100644
index d66ef84b6..000000000
--- a/vendor/github.com/garyburd/redigo/redis/pool.go
+++ /dev/null
@@ -1,393 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import (
- "bytes"
- "container/list"
- "crypto/rand"
- "crypto/sha1"
- "errors"
- "io"
- "strconv"
- "sync"
- "time"
-
- "github.com/garyburd/redigo/internal"
-)
-
-var nowFunc = time.Now // for testing
-
-// ErrPoolExhausted is returned from a pool connection method (Do, Send,
-// Receive, Flush, Err) when the maximum number of database connections in the
-// pool has been reached.
-var ErrPoolExhausted = errors.New("redigo: connection pool exhausted")
-
-var (
- errPoolClosed = errors.New("redigo: connection pool closed")
- errConnClosed = errors.New("redigo: connection closed")
-)
-
-// Pool maintains a pool of connections. The application calls the Get method
-// to get a connection from the pool and the connection's Close method to
-// return the connection's resources to the pool.
-//
-// The following example shows how to use a pool in a web application. The
-// application creates a pool at application startup and makes it available to
-// request handlers using a global variable.
-//
-// func newPool(server, password string) *redis.Pool {
-// return &redis.Pool{
-// MaxIdle: 3,
-// IdleTimeout: 240 * time.Second,
-// Dial: func () (redis.Conn, error) {
-// c, err := redis.Dial("tcp", server)
-// if err != nil {
-// return nil, err
-// }
-// if _, err := c.Do("AUTH", password); err != nil {
-// c.Close()
-// return nil, err
-// }
-// return c, err
-// },
-// TestOnBorrow: func(c redis.Conn, t time.Time) error {
-// _, err := c.Do("PING")
-// return err
-// },
-// }
-// }
-//
-// var (
-// pool *redis.Pool
-// redisServer = flag.String("redisServer", ":6379", "")
-// redisPassword = flag.String("redisPassword", "", "")
-// )
-//
-// func main() {
-// flag.Parse()
-// pool = newPool(*redisServer, *redisPassword)
-// ...
-// }
-//
-// A request handler gets a connection from the pool and closes the connection
-// when the handler is done:
-//
-// func serveHome(w http.ResponseWriter, r *http.Request) {
-// conn := pool.Get()
-// defer conn.Close()
-// ....
-// }
-//
-type Pool struct {
-
- // Dial is an application supplied function for creating and configuring a
- // connection.
- //
- // The connection returned from Dial must not be in a special state
- // (subscribed to pubsub channel, transaction started, ...).
- Dial func() (Conn, error)
-
- // TestOnBorrow is an optional application supplied function for checking
- // the health of an idle connection before the connection is used again by
- // the application. Argument t is the time that the connection was returned
- // to the pool. If the function returns an error, then the connection is
- // closed.
- TestOnBorrow func(c Conn, t time.Time) error
-
- // Maximum number of idle connections in the pool.
- MaxIdle int
-
- // Maximum number of connections allocated by the pool at a given time.
- // When zero, there is no limit on the number of connections in the pool.
- MaxActive int
-
- // Close connections after remaining idle for this duration. If the value
- // is zero, then idle connections are not closed. Applications should set
- // the timeout to a value less than the server's timeout.
- IdleTimeout time.Duration
-
- // If Wait is true and the pool is at the MaxActive limit, then Get() waits
- // for a connection to be returned to the pool before returning.
- Wait bool
-
- // mu protects fields defined below.
- mu sync.Mutex
- cond *sync.Cond
- closed bool
- active int
-
- // Stack of idleConn with most recently used at the front.
- idle list.List
-}
-
-type idleConn struct {
- c Conn
- t time.Time
-}
-
-// NewPool creates a new pool.
-//
-// Deprecated: Initialize the Pool directory as shown in the example.
-func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
- return &Pool{Dial: newFn, MaxIdle: maxIdle}
-}
-
-// Get gets a connection. The application must close the returned connection.
-// This method always returns a valid connection so that applications can defer
-// error handling to the first use of the connection. If there is an error
-// getting an underlying connection, then the connection Err, Do, Send, Flush
-// and Receive methods return that error.
-func (p *Pool) Get() Conn {
- c, err := p.get()
- if err != nil {
- return errorConnection{err}
- }
- return &pooledConnection{p: p, c: c}
-}
-
-// ActiveCount returns the number of active connections in the pool.
-func (p *Pool) ActiveCount() int {
- p.mu.Lock()
- active := p.active
- p.mu.Unlock()
- return active
-}
-
-// Close releases the resources used by the pool.
-func (p *Pool) Close() error {
- p.mu.Lock()
- idle := p.idle
- p.idle.Init()
- p.closed = true
- p.active -= idle.Len()
- if p.cond != nil {
- p.cond.Broadcast()
- }
- p.mu.Unlock()
- for e := idle.Front(); e != nil; e = e.Next() {
- e.Value.(idleConn).c.Close()
- }
- return nil
-}
-
-// release decrements the active count and signals waiters. The caller must
-// hold p.mu during the call.
-func (p *Pool) release() {
- p.active -= 1
- if p.cond != nil {
- p.cond.Signal()
- }
-}
-
-// get prunes stale connections and returns a connection from the idle list or
-// creates a new connection.
-func (p *Pool) get() (Conn, error) {
- p.mu.Lock()
-
- // Prune stale connections.
-
- if timeout := p.IdleTimeout; timeout > 0 {
- for i, n := 0, p.idle.Len(); i < n; i++ {
- e := p.idle.Back()
- if e == nil {
- break
- }
- ic := e.Value.(idleConn)
- if ic.t.Add(timeout).After(nowFunc()) {
- break
- }
- p.idle.Remove(e)
- p.release()
- p.mu.Unlock()
- ic.c.Close()
- p.mu.Lock()
- }
- }
-
- for {
-
- // Get idle connection.
-
- for i, n := 0, p.idle.Len(); i < n; i++ {
- e := p.idle.Front()
- if e == nil {
- break
- }
- ic := e.Value.(idleConn)
- p.idle.Remove(e)
- test := p.TestOnBorrow
- p.mu.Unlock()
- if test == nil || test(ic.c, ic.t) == nil {
- return ic.c, nil
- }
- ic.c.Close()
- p.mu.Lock()
- p.release()
- }
-
- // Check for pool closed before dialing a new connection.
-
- if p.closed {
- p.mu.Unlock()
- return nil, errors.New("redigo: get on closed pool")
- }
-
- // Dial new connection if under limit.
-
- if p.MaxActive == 0 || p.active < p.MaxActive {
- dial := p.Dial
- p.active += 1
- p.mu.Unlock()
- c, err := dial()
- if err != nil {
- p.mu.Lock()
- p.release()
- p.mu.Unlock()
- c = nil
- }
- return c, err
- }
-
- if !p.Wait {
- p.mu.Unlock()
- return nil, ErrPoolExhausted
- }
-
- if p.cond == nil {
- p.cond = sync.NewCond(&p.mu)
- }
- p.cond.Wait()
- }
-}
-
-func (p *Pool) put(c Conn, forceClose bool) error {
- err := c.Err()
- p.mu.Lock()
- if !p.closed && err == nil && !forceClose {
- p.idle.PushFront(idleConn{t: nowFunc(), c: c})
- if p.idle.Len() > p.MaxIdle {
- c = p.idle.Remove(p.idle.Back()).(idleConn).c
- } else {
- c = nil
- }
- }
-
- if c == nil {
- if p.cond != nil {
- p.cond.Signal()
- }
- p.mu.Unlock()
- return nil
- }
-
- p.release()
- p.mu.Unlock()
- return c.Close()
-}
-
-type pooledConnection struct {
- p *Pool
- c Conn
- state int
-}
-
-var (
- sentinel []byte
- sentinelOnce sync.Once
-)
-
-func initSentinel() {
- p := make([]byte, 64)
- if _, err := rand.Read(p); err == nil {
- sentinel = p
- } else {
- h := sha1.New()
- io.WriteString(h, "Oops, rand failed. Use time instead.")
- io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10))
- sentinel = h.Sum(nil)
- }
-}
-
-func (pc *pooledConnection) Close() error {
- c := pc.c
- if _, ok := c.(errorConnection); ok {
- return nil
- }
- pc.c = errorConnection{errConnClosed}
-
- if pc.state&internal.MultiState != 0 {
- c.Send("DISCARD")
- pc.state &^= (internal.MultiState | internal.WatchState)
- } else if pc.state&internal.WatchState != 0 {
- c.Send("UNWATCH")
- pc.state &^= internal.WatchState
- }
- if pc.state&internal.SubscribeState != 0 {
- c.Send("UNSUBSCRIBE")
- c.Send("PUNSUBSCRIBE")
- // To detect the end of the message stream, ask the server to echo
- // a sentinel value and read until we see that value.
- sentinelOnce.Do(initSentinel)
- c.Send("ECHO", sentinel)
- c.Flush()
- for {
- p, err := c.Receive()
- if err != nil {
- break
- }
- if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) {
- pc.state &^= internal.SubscribeState
- break
- }
- }
- }
- c.Do("")
- pc.p.put(c, pc.state != 0)
- return nil
-}
-
-func (pc *pooledConnection) Err() error {
- return pc.c.Err()
-}
-
-func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
- ci := internal.LookupCommandInfo(commandName)
- pc.state = (pc.state | ci.Set) &^ ci.Clear
- return pc.c.Do(commandName, args...)
-}
-
-func (pc *pooledConnection) Send(commandName string, args ...interface{}) error {
- ci := internal.LookupCommandInfo(commandName)
- pc.state = (pc.state | ci.Set) &^ ci.Clear
- return pc.c.Send(commandName, args...)
-}
-
-func (pc *pooledConnection) Flush() error {
- return pc.c.Flush()
-}
-
-func (pc *pooledConnection) Receive() (reply interface{}, err error) {
- return pc.c.Receive()
-}
-
-type errorConnection struct{ err error }
-
-func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err }
-func (ec errorConnection) Send(string, ...interface{}) error { return ec.err }
-func (ec errorConnection) Err() error { return ec.err }
-func (ec errorConnection) Close() error { return ec.err }
-func (ec errorConnection) Flush() error { return ec.err }
-func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err }
diff --git a/vendor/github.com/garyburd/redigo/redis/pool_test.go b/vendor/github.com/garyburd/redigo/redis/pool_test.go
deleted file mode 100644
index 9419a128f..000000000
--- a/vendor/github.com/garyburd/redigo/redis/pool_test.go
+++ /dev/null
@@ -1,684 +0,0 @@
-// Copyright 2011 Gary Burd
-//
-// 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 redis_test
-
-import (
- "errors"
- "io"
- "reflect"
- "sync"
- "testing"
- "time"
-
- "github.com/garyburd/redigo/redis"
-)
-
-type poolTestConn struct {
- d *poolDialer
- err error
- redis.Conn
-}
-
-func (c *poolTestConn) Close() error {
- c.d.mu.Lock()
- c.d.open -= 1
- c.d.mu.Unlock()
- return c.Conn.Close()
-}
-
-func (c *poolTestConn) Err() error { return c.err }
-
-func (c *poolTestConn) Do(commandName string, args ...interface{}) (interface{}, error) {
- if commandName == "ERR" {
- c.err = args[0].(error)
- commandName = "PING"
- }
- if commandName != "" {
- c.d.commands = append(c.d.commands, commandName)
- }
- return c.Conn.Do(commandName, args...)
-}
-
-func (c *poolTestConn) Send(commandName string, args ...interface{}) error {
- c.d.commands = append(c.d.commands, commandName)
- return c.Conn.Send(commandName, args...)
-}
-
-type poolDialer struct {
- mu sync.Mutex
- t *testing.T
- dialed int
- open int
- commands []string
- dialErr error
-}
-
-func (d *poolDialer) dial() (redis.Conn, error) {
- d.mu.Lock()
- d.dialed += 1
- dialErr := d.dialErr
- d.mu.Unlock()
- if dialErr != nil {
- return nil, d.dialErr
- }
- c, err := redis.DialDefaultServer()
- if err != nil {
- return nil, err
- }
- d.mu.Lock()
- d.open += 1
- d.mu.Unlock()
- return &poolTestConn{d: d, Conn: c}, nil
-}
-
-func (d *poolDialer) check(message string, p *redis.Pool, dialed, open int) {
- d.mu.Lock()
- if d.dialed != dialed {
- d.t.Errorf("%s: dialed=%d, want %d", message, d.dialed, dialed)
- }
- if d.open != open {
- d.t.Errorf("%s: open=%d, want %d", message, d.open, open)
- }
- if active := p.ActiveCount(); active != open {
- d.t.Errorf("%s: active=%d, want %d", message, active, open)
- }
- d.mu.Unlock()
-}
-
-func TestPoolReuse(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- Dial: d.dial,
- }
-
- for i := 0; i < 10; i++ {
- c1 := p.Get()
- c1.Do("PING")
- c2 := p.Get()
- c2.Do("PING")
- c1.Close()
- c2.Close()
- }
-
- d.check("before close", p, 2, 2)
- p.Close()
- d.check("after close", p, 2, 0)
-}
-
-func TestPoolMaxIdle(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- Dial: d.dial,
- }
- defer p.Close()
-
- for i := 0; i < 10; i++ {
- c1 := p.Get()
- c1.Do("PING")
- c2 := p.Get()
- c2.Do("PING")
- c3 := p.Get()
- c3.Do("PING")
- c1.Close()
- c2.Close()
- c3.Close()
- }
- d.check("before close", p, 12, 2)
- p.Close()
- d.check("after close", p, 12, 0)
-}
-
-func TestPoolError(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- Dial: d.dial,
- }
- defer p.Close()
-
- c := p.Get()
- c.Do("ERR", io.EOF)
- if c.Err() == nil {
- t.Errorf("expected c.Err() != nil")
- }
- c.Close()
-
- c = p.Get()
- c.Do("ERR", io.EOF)
- c.Close()
-
- d.check(".", p, 2, 0)
-}
-
-func TestPoolClose(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- Dial: d.dial,
- }
- defer p.Close()
-
- c1 := p.Get()
- c1.Do("PING")
- c2 := p.Get()
- c2.Do("PING")
- c3 := p.Get()
- c3.Do("PING")
-
- c1.Close()
- if _, err := c1.Do("PING"); err == nil {
- t.Errorf("expected error after connection closed")
- }
-
- c2.Close()
- c2.Close()
-
- p.Close()
-
- d.check("after pool close", p, 3, 1)
-
- if _, err := c1.Do("PING"); err == nil {
- t.Errorf("expected error after connection and pool closed")
- }
-
- c3.Close()
-
- d.check("after conn close", p, 3, 0)
-
- c1 = p.Get()
- if _, err := c1.Do("PING"); err == nil {
- t.Errorf("expected error after pool closed")
- }
-}
-
-func TestPoolTimeout(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- IdleTimeout: 300 * time.Second,
- Dial: d.dial,
- }
- defer p.Close()
-
- now := time.Now()
- redis.SetNowFunc(func() time.Time { return now })
- defer redis.SetNowFunc(time.Now)
-
- c := p.Get()
- c.Do("PING")
- c.Close()
-
- d.check("1", p, 1, 1)
-
- now = now.Add(p.IdleTimeout)
-
- c = p.Get()
- c.Do("PING")
- c.Close()
-
- d.check("2", p, 2, 1)
-}
-
-func TestPoolConcurrenSendReceive(t *testing.T) {
- p := &redis.Pool{
- Dial: redis.DialDefaultServer,
- }
- defer p.Close()
-
- c := p.Get()
- done := make(chan error, 1)
- go func() {
- _, err := c.Receive()
- done <- err
- }()
- c.Send("PING")
- c.Flush()
- err := <-done
- if err != nil {
- t.Fatalf("Receive() returned error %v", err)
- }
- _, err = c.Do("")
- if err != nil {
- t.Fatalf("Do() returned error %v", err)
- }
- c.Close()
-}
-
-func TestPoolBorrowCheck(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- Dial: d.dial,
- TestOnBorrow: func(redis.Conn, time.Time) error { return redis.Error("BLAH") },
- }
- defer p.Close()
-
- for i := 0; i < 10; i++ {
- c := p.Get()
- c.Do("PING")
- c.Close()
- }
- d.check("1", p, 10, 1)
-}
-
-func TestPoolMaxActive(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- MaxActive: 2,
- Dial: d.dial,
- }
- defer p.Close()
-
- c1 := p.Get()
- c1.Do("PING")
- c2 := p.Get()
- c2.Do("PING")
-
- d.check("1", p, 2, 2)
-
- c3 := p.Get()
- if _, err := c3.Do("PING"); err != redis.ErrPoolExhausted {
- t.Errorf("expected pool exhausted")
- }
-
- c3.Close()
- d.check("2", p, 2, 2)
- c2.Close()
- d.check("3", p, 2, 2)
-
- c3 = p.Get()
- if _, err := c3.Do("PING"); err != nil {
- t.Errorf("expected good channel, err=%v", err)
- }
- c3.Close()
-
- d.check("4", p, 2, 2)
-}
-
-func TestPoolMonitorCleanup(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- MaxActive: 2,
- Dial: d.dial,
- }
- defer p.Close()
-
- c := p.Get()
- c.Send("MONITOR")
- c.Close()
-
- d.check("", p, 1, 0)
-}
-
-func TestPoolPubSubCleanup(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- MaxActive: 2,
- Dial: d.dial,
- }
- defer p.Close()
-
- c := p.Get()
- c.Send("SUBSCRIBE", "x")
- c.Close()
-
- want := []string{"SUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
- if !reflect.DeepEqual(d.commands, want) {
- t.Errorf("got commands %v, want %v", d.commands, want)
- }
- d.commands = nil
-
- c = p.Get()
- c.Send("PSUBSCRIBE", "x*")
- c.Close()
-
- want = []string{"PSUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
- if !reflect.DeepEqual(d.commands, want) {
- t.Errorf("got commands %v, want %v", d.commands, want)
- }
- d.commands = nil
-}
-
-func TestPoolTransactionCleanup(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 2,
- MaxActive: 2,
- Dial: d.dial,
- }
- defer p.Close()
-
- c := p.Get()
- c.Do("WATCH", "key")
- c.Do("PING")
- c.Close()
-
- want := []string{"WATCH", "PING", "UNWATCH"}
- if !reflect.DeepEqual(d.commands, want) {
- t.Errorf("got commands %v, want %v", d.commands, want)
- }
- d.commands = nil
-
- c = p.Get()
- c.Do("WATCH", "key")
- c.Do("UNWATCH")
- c.Do("PING")
- c.Close()
-
- want = []string{"WATCH", "UNWATCH", "PING"}
- if !reflect.DeepEqual(d.commands, want) {
- t.Errorf("got commands %v, want %v", d.commands, want)
- }
- d.commands = nil
-
- c = p.Get()
- c.Do("WATCH", "key")
- c.Do("MULTI")
- c.Do("PING")
- c.Close()
-
- want = []string{"WATCH", "MULTI", "PING", "DISCARD"}
- if !reflect.DeepEqual(d.commands, want) {
- t.Errorf("got commands %v, want %v", d.commands, want)
- }
- d.commands = nil
-
- c = p.Get()
- c.Do("WATCH", "key")
- c.Do("MULTI")
- c.Do("DISCARD")
- c.Do("PING")
- c.Close()
-
- want = []string{"WATCH", "MULTI", "DISCARD", "PING"}
- if !reflect.DeepEqual(d.commands, want) {
- t.Errorf("got commands %v, want %v", d.commands, want)
- }
- d.commands = nil
-
- c = p.Get()
- c.Do("WATCH", "key")
- c.Do("MULTI")
- c.Do("EXEC")
- c.Do("PING")
- c.Close()
-
- want = []string{"WATCH", "MULTI", "EXEC", "PING"}
- if !reflect.DeepEqual(d.commands, want) {
- t.Errorf("got commands %v, want %v", d.commands, want)
- }
- d.commands = nil
-}
-
-func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) chan error {
- errs := make(chan error, 10)
- for i := 0; i < cap(errs); i++ {
- go func() {
- c := p.Get()
- _, err := c.Do(cmd, args...)
- errs <- err
- c.Close()
- }()
- }
-
- // Wait for goroutines to block.
- time.Sleep(time.Second / 4)
-
- return errs
-}
-
-func TestWaitPool(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 1,
- MaxActive: 1,
- Dial: d.dial,
- Wait: true,
- }
- defer p.Close()
-
- c := p.Get()
- errs := startGoroutines(p, "PING")
- d.check("before close", p, 1, 1)
- c.Close()
- timeout := time.After(2 * time.Second)
- for i := 0; i < cap(errs); i++ {
- select {
- case err := <-errs:
- if err != nil {
- t.Fatal(err)
- }
- case <-timeout:
- t.Fatalf("timeout waiting for blocked goroutine %d", i)
- }
- }
- d.check("done", p, 1, 1)
-}
-
-func TestWaitPoolClose(t *testing.T) {
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 1,
- MaxActive: 1,
- Dial: d.dial,
- Wait: true,
- }
- defer p.Close()
-
- c := p.Get()
- if _, err := c.Do("PING"); err != nil {
- t.Fatal(err)
- }
- errs := startGoroutines(p, "PING")
- d.check("before close", p, 1, 1)
- p.Close()
- timeout := time.After(2 * time.Second)
- for i := 0; i < cap(errs); i++ {
- select {
- case err := <-errs:
- switch err {
- case nil:
- t.Fatal("blocked goroutine did not get error")
- case redis.ErrPoolExhausted:
- t.Fatal("blocked goroutine got pool exhausted error")
- }
- case <-timeout:
- t.Fatal("timeout waiting for blocked goroutine")
- }
- }
- c.Close()
- d.check("done", p, 1, 0)
-}
-
-func TestWaitPoolCommandError(t *testing.T) {
- testErr := errors.New("test")
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 1,
- MaxActive: 1,
- Dial: d.dial,
- Wait: true,
- }
- defer p.Close()
-
- c := p.Get()
- errs := startGoroutines(p, "ERR", testErr)
- d.check("before close", p, 1, 1)
- c.Close()
- timeout := time.After(2 * time.Second)
- for i := 0; i < cap(errs); i++ {
- select {
- case err := <-errs:
- if err != nil {
- t.Fatal(err)
- }
- case <-timeout:
- t.Fatalf("timeout waiting for blocked goroutine %d", i)
- }
- }
- d.check("done", p, cap(errs), 0)
-}
-
-func TestWaitPoolDialError(t *testing.T) {
- testErr := errors.New("test")
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: 1,
- MaxActive: 1,
- Dial: d.dial,
- Wait: true,
- }
- defer p.Close()
-
- c := p.Get()
- errs := startGoroutines(p, "ERR", testErr)
- d.check("before close", p, 1, 1)
-
- d.dialErr = errors.New("dial")
- c.Close()
-
- nilCount := 0
- errCount := 0
- timeout := time.After(2 * time.Second)
- for i := 0; i < cap(errs); i++ {
- select {
- case err := <-errs:
- switch err {
- case nil:
- nilCount++
- case d.dialErr:
- errCount++
- default:
- t.Fatalf("expected dial error or nil, got %v", err)
- }
- case <-timeout:
- t.Fatalf("timeout waiting for blocked goroutine %d", i)
- }
- }
- if nilCount != 1 {
- t.Errorf("expected one nil error, got %d", nilCount)
- }
- if errCount != cap(errs)-1 {
- t.Errorf("expected %d dial erors, got %d", cap(errs)-1, errCount)
- }
- d.check("done", p, cap(errs), 0)
-}
-
-// Borrowing requires us to iterate over the idle connections, unlock the pool,
-// and perform a blocking operation to check the connection still works. If
-// TestOnBorrow fails, we must reacquire the lock and continue iteration. This
-// test ensures that iteration will work correctly if multiple threads are
-// iterating simultaneously.
-func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) {
- const count = 100
-
- // First we'll Create a pool where the pilfering of idle connections fails.
- d := poolDialer{t: t}
- p := &redis.Pool{
- MaxIdle: count,
- MaxActive: count,
- Dial: d.dial,
- TestOnBorrow: func(c redis.Conn, t time.Time) error {
- return errors.New("No way back into the real world.")
- },
- }
- defer p.Close()
-
- // Fill the pool with idle connections.
- conns := make([]redis.Conn, count)
- for i := range conns {
- conns[i] = p.Get()
- }
- for i := range conns {
- conns[i].Close()
- }
-
- // Spawn a bunch of goroutines to thrash the pool.
- var wg sync.WaitGroup
- wg.Add(count)
- for i := 0; i < count; i++ {
- go func() {
- c := p.Get()
- if c.Err() != nil {
- t.Errorf("pool get failed: %v", c.Err())
- }
- c.Close()
- wg.Done()
- }()
- }
- wg.Wait()
- if d.dialed != count*2 {
- t.Errorf("Expected %d dials, got %d", count*2, d.dialed)
- }
-}
-
-func BenchmarkPoolGet(b *testing.B) {
- b.StopTimer()
- p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
- c := p.Get()
- if err := c.Err(); err != nil {
- b.Fatal(err)
- }
- c.Close()
- defer p.Close()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- c = p.Get()
- c.Close()
- }
-}
-
-func BenchmarkPoolGetErr(b *testing.B) {
- b.StopTimer()
- p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
- c := p.Get()
- if err := c.Err(); err != nil {
- b.Fatal(err)
- }
- c.Close()
- defer p.Close()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- c = p.Get()
- if err := c.Err(); err != nil {
- b.Fatal(err)
- }
- c.Close()
- }
-}
-
-func BenchmarkPoolGetPing(b *testing.B) {
- b.StopTimer()
- p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
- c := p.Get()
- if err := c.Err(); err != nil {
- b.Fatal(err)
- }
- c.Close()
- defer p.Close()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- c = p.Get()
- if _, err := c.Do("PING"); err != nil {
- b.Fatal(err)
- }
- c.Close()
- }
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/pubsub.go b/vendor/github.com/garyburd/redigo/redis/pubsub.go
deleted file mode 100644
index c0ecce824..000000000
--- a/vendor/github.com/garyburd/redigo/redis/pubsub.go
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import "errors"
-
-// Subscription represents a subscribe or unsubscribe notification.
-type Subscription struct {
-
- // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
- Kind string
-
- // The channel that was changed.
- Channel string
-
- // The current number of subscriptions for connection.
- Count int
-}
-
-// Message represents a message notification.
-type Message struct {
-
- // The originating channel.
- Channel string
-
- // The message data.
- Data []byte
-}
-
-// PMessage represents a pmessage notification.
-type PMessage struct {
-
- // The matched pattern.
- Pattern string
-
- // The originating channel.
- Channel string
-
- // The message data.
- Data []byte
-}
-
-// Pong represents a pubsub pong notification.
-type Pong struct {
- Data string
-}
-
-// PubSubConn wraps a Conn with convenience methods for subscribers.
-type PubSubConn struct {
- Conn Conn
-}
-
-// Close closes the connection.
-func (c PubSubConn) Close() error {
- return c.Conn.Close()
-}
-
-// Subscribe subscribes the connection to the specified channels.
-func (c PubSubConn) Subscribe(channel ...interface{}) error {
- c.Conn.Send("SUBSCRIBE", channel...)
- return c.Conn.Flush()
-}
-
-// PSubscribe subscribes the connection to the given patterns.
-func (c PubSubConn) PSubscribe(channel ...interface{}) error {
- c.Conn.Send("PSUBSCRIBE", channel...)
- return c.Conn.Flush()
-}
-
-// Unsubscribe unsubscribes the connection from the given channels, or from all
-// of them if none is given.
-func (c PubSubConn) Unsubscribe(channel ...interface{}) error {
- c.Conn.Send("UNSUBSCRIBE", channel...)
- return c.Conn.Flush()
-}
-
-// PUnsubscribe unsubscribes the connection from the given patterns, or from all
-// of them if none is given.
-func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
- c.Conn.Send("PUNSUBSCRIBE", channel...)
- return c.Conn.Flush()
-}
-
-// Ping sends a PING to the server with the specified data.
-func (c PubSubConn) Ping(data string) error {
- c.Conn.Send("PING", data)
- return c.Conn.Flush()
-}
-
-// Receive returns a pushed message as a Subscription, Message, PMessage, Pong
-// or error. The return value is intended to be used directly in a type switch
-// as illustrated in the PubSubConn example.
-func (c PubSubConn) Receive() interface{} {
- reply, err := Values(c.Conn.Receive())
- if err != nil {
- return err
- }
-
- var kind string
- reply, err = Scan(reply, &kind)
- if err != nil {
- return err
- }
-
- switch kind {
- case "message":
- var m Message
- if _, err := Scan(reply, &m.Channel, &m.Data); err != nil {
- return err
- }
- return m
- case "pmessage":
- var pm PMessage
- if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil {
- return err
- }
- return pm
- case "subscribe", "psubscribe", "unsubscribe", "punsubscribe":
- s := Subscription{Kind: kind}
- if _, err := Scan(reply, &s.Channel, &s.Count); err != nil {
- return err
- }
- return s
- case "pong":
- var p Pong
- if _, err := Scan(reply, &p.Data); err != nil {
- return err
- }
- return p
- }
- return errors.New("redigo: unknown pubsub notification")
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/pubsub_test.go b/vendor/github.com/garyburd/redigo/redis/pubsub_test.go
deleted file mode 100644
index b95513155..000000000
--- a/vendor/github.com/garyburd/redigo/redis/pubsub_test.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis_test
-
-import (
- "fmt"
- "reflect"
- "sync"
- "testing"
-
- "github.com/garyburd/redigo/redis"
-)
-
-func publish(channel, value interface{}) {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
- c.Do("PUBLISH", channel, value)
-}
-
-// Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine.
-func ExamplePubSubConn() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
- var wg sync.WaitGroup
- wg.Add(2)
-
- psc := redis.PubSubConn{Conn: c}
-
- // This goroutine receives and prints pushed notifications from the server.
- // The goroutine exits when the connection is unsubscribed from all
- // channels or there is an error.
- go func() {
- defer wg.Done()
- for {
- switch n := psc.Receive().(type) {
- case redis.Message:
- fmt.Printf("Message: %s %s\n", n.Channel, n.Data)
- case redis.PMessage:
- fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data)
- case redis.Subscription:
- fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count)
- if n.Count == 0 {
- return
- }
- case error:
- fmt.Printf("error: %v\n", n)
- return
- }
- }
- }()
-
- // This goroutine manages subscriptions for the connection.
- go func() {
- defer wg.Done()
-
- psc.Subscribe("example")
- psc.PSubscribe("p*")
-
- // The following function calls publish a message using another
- // connection to the Redis server.
- publish("example", "hello")
- publish("example", "world")
- publish("pexample", "foo")
- publish("pexample", "bar")
-
- // Unsubscribe from all connections. This will cause the receiving
- // goroutine to exit.
- psc.Unsubscribe()
- psc.PUnsubscribe()
- }()
-
- wg.Wait()
-
- // Output:
- // Subscription: subscribe example 1
- // Subscription: psubscribe p* 2
- // Message: example hello
- // Message: example world
- // PMessage: p* pexample foo
- // PMessage: p* pexample bar
- // Subscription: unsubscribe example 1
- // Subscription: punsubscribe p* 0
-}
-
-func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) {
- actual := c.Receive()
- if !reflect.DeepEqual(actual, expected) {
- t.Errorf("%s = %v, want %v", message, actual, expected)
- }
-}
-
-func TestPushed(t *testing.T) {
- pc, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer pc.Close()
-
- sc, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer sc.Close()
-
- c := redis.PubSubConn{Conn: sc}
-
- c.Subscribe("c1")
- expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1})
- c.Subscribe("c2")
- expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2})
- c.PSubscribe("p1")
- expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3})
- c.PSubscribe("p2")
- expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4})
- c.PUnsubscribe()
- expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3})
- expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2})
-
- pc.Do("PUBLISH", "c1", "hello")
- expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")})
-
- c.Ping("hello")
- expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"})
-
- c.Conn.Send("PING")
- c.Conn.Flush()
- expectPushed(t, c, `Send("PING")`, redis.Pong{})
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/redis.go b/vendor/github.com/garyburd/redigo/redis/redis.go
deleted file mode 100644
index c90a48ed4..000000000
--- a/vendor/github.com/garyburd/redigo/redis/redis.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-// Error represents an error returned in a command reply.
-type Error string
-
-func (err Error) Error() string { return string(err) }
-
-// Conn represents a connection to a Redis server.
-type Conn interface {
- // Close closes the connection.
- Close() error
-
- // Err returns a non-nil value if the connection is broken. The returned
- // value is either the first non-nil value returned from the underlying
- // network connection or a protocol parsing error. Applications should
- // close broken connections.
- Err() error
-
- // Do sends a command to the server and returns the received reply.
- Do(commandName string, args ...interface{}) (reply interface{}, err error)
-
- // Send writes the command to the client's output buffer.
- Send(commandName string, args ...interface{}) error
-
- // Flush flushes the output buffer to the Redis server.
- Flush() error
-
- // Receive receives a single reply from the Redis server
- Receive() (reply interface{}, err error)
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/reply.go b/vendor/github.com/garyburd/redigo/redis/reply.go
deleted file mode 100644
index 57896147f..000000000
--- a/vendor/github.com/garyburd/redigo/redis/reply.go
+++ /dev/null
@@ -1,393 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import (
- "errors"
- "fmt"
- "strconv"
-)
-
-// ErrNil indicates that a reply value is nil.
-var ErrNil = errors.New("redigo: nil returned")
-
-// Int is a helper that converts a command reply to an integer. If err is not
-// equal to nil, then Int returns 0, err. Otherwise, Int converts the
-// reply to an int as follows:
-//
-// Reply type Result
-// integer int(reply), nil
-// bulk string parsed reply, nil
-// nil 0, ErrNil
-// other 0, error
-func Int(reply interface{}, err error) (int, error) {
- if err != nil {
- return 0, err
- }
- switch reply := reply.(type) {
- case int64:
- x := int(reply)
- if int64(x) != reply {
- return 0, strconv.ErrRange
- }
- return x, nil
- case []byte:
- n, err := strconv.ParseInt(string(reply), 10, 0)
- return int(n), err
- case nil:
- return 0, ErrNil
- case Error:
- return 0, reply
- }
- return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply)
-}
-
-// Int64 is a helper that converts a command reply to 64 bit integer. If err is
-// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
-// reply to an int64 as follows:
-//
-// Reply type Result
-// integer reply, nil
-// bulk string parsed reply, nil
-// nil 0, ErrNil
-// other 0, error
-func Int64(reply interface{}, err error) (int64, error) {
- if err != nil {
- return 0, err
- }
- switch reply := reply.(type) {
- case int64:
- return reply, nil
- case []byte:
- n, err := strconv.ParseInt(string(reply), 10, 64)
- return n, err
- case nil:
- return 0, ErrNil
- case Error:
- return 0, reply
- }
- return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply)
-}
-
-var errNegativeInt = errors.New("redigo: unexpected value for Uint64")
-
-// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
-// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
-// reply to an int64 as follows:
-//
-// Reply type Result
-// integer reply, nil
-// bulk string parsed reply, nil
-// nil 0, ErrNil
-// other 0, error
-func Uint64(reply interface{}, err error) (uint64, error) {
- if err != nil {
- return 0, err
- }
- switch reply := reply.(type) {
- case int64:
- if reply < 0 {
- return 0, errNegativeInt
- }
- return uint64(reply), nil
- case []byte:
- n, err := strconv.ParseUint(string(reply), 10, 64)
- return n, err
- case nil:
- return 0, ErrNil
- case Error:
- return 0, reply
- }
- return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply)
-}
-
-// Float64 is a helper that converts a command reply to 64 bit float. If err is
-// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
-// the reply to an int as follows:
-//
-// Reply type Result
-// bulk string parsed reply, nil
-// nil 0, ErrNil
-// other 0, error
-func Float64(reply interface{}, err error) (float64, error) {
- if err != nil {
- return 0, err
- }
- switch reply := reply.(type) {
- case []byte:
- n, err := strconv.ParseFloat(string(reply), 64)
- return n, err
- case nil:
- return 0, ErrNil
- case Error:
- return 0, reply
- }
- return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply)
-}
-
-// String is a helper that converts a command reply to a string. If err is not
-// equal to nil, then String returns "", err. Otherwise String converts the
-// reply to a string as follows:
-//
-// Reply type Result
-// bulk string string(reply), nil
-// simple string reply, nil
-// nil "", ErrNil
-// other "", error
-func String(reply interface{}, err error) (string, error) {
- if err != nil {
- return "", err
- }
- switch reply := reply.(type) {
- case []byte:
- return string(reply), nil
- case string:
- return reply, nil
- case nil:
- return "", ErrNil
- case Error:
- return "", reply
- }
- return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply)
-}
-
-// Bytes is a helper that converts a command reply to a slice of bytes. If err
-// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts
-// the reply to a slice of bytes as follows:
-//
-// Reply type Result
-// bulk string reply, nil
-// simple string []byte(reply), nil
-// nil nil, ErrNil
-// other nil, error
-func Bytes(reply interface{}, err error) ([]byte, error) {
- if err != nil {
- return nil, err
- }
- switch reply := reply.(type) {
- case []byte:
- return reply, nil
- case string:
- return []byte(reply), nil
- case nil:
- return nil, ErrNil
- case Error:
- return nil, reply
- }
- return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply)
-}
-
-// Bool is a helper that converts a command reply to a boolean. If err is not
-// equal to nil, then Bool returns false, err. Otherwise Bool converts the
-// reply to boolean as follows:
-//
-// Reply type Result
-// integer value != 0, nil
-// bulk string strconv.ParseBool(reply)
-// nil false, ErrNil
-// other false, error
-func Bool(reply interface{}, err error) (bool, error) {
- if err != nil {
- return false, err
- }
- switch reply := reply.(type) {
- case int64:
- return reply != 0, nil
- case []byte:
- return strconv.ParseBool(string(reply))
- case nil:
- return false, ErrNil
- case Error:
- return false, reply
- }
- return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply)
-}
-
-// MultiBulk is a helper that converts an array command reply to a []interface{}.
-//
-// Deprecated: Use Values instead.
-func MultiBulk(reply interface{}, err error) ([]interface{}, error) { return Values(reply, err) }
-
-// Values is a helper that converts an array command reply to a []interface{}.
-// If err is not equal to nil, then Values returns nil, err. Otherwise, Values
-// converts the reply as follows:
-//
-// Reply type Result
-// array reply, nil
-// nil nil, ErrNil
-// other nil, error
-func Values(reply interface{}, err error) ([]interface{}, error) {
- if err != nil {
- return nil, err
- }
- switch reply := reply.(type) {
- case []interface{}:
- return reply, nil
- case nil:
- return nil, ErrNil
- case Error:
- return nil, reply
- }
- return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
-}
-
-// Strings is a helper that converts an array command reply to a []string. If
-// err is not equal to nil, then Strings returns nil, err. Nil array items are
-// converted to "" in the output slice. Strings returns an error if an array
-// item is not a bulk string or nil.
-func Strings(reply interface{}, err error) ([]string, error) {
- if err != nil {
- return nil, err
- }
- switch reply := reply.(type) {
- case []interface{}:
- result := make([]string, len(reply))
- for i := range reply {
- if reply[i] == nil {
- continue
- }
- p, ok := reply[i].([]byte)
- if !ok {
- return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i])
- }
- result[i] = string(p)
- }
- return result, nil
- case nil:
- return nil, ErrNil
- case Error:
- return nil, reply
- }
- return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply)
-}
-
-// ByteSlices is a helper that converts an array command reply to a [][]byte.
-// If err is not equal to nil, then ByteSlices returns nil, err. Nil array
-// items are stay nil. ByteSlices returns an error if an array item is not a
-// bulk string or nil.
-func ByteSlices(reply interface{}, err error) ([][]byte, error) {
- if err != nil {
- return nil, err
- }
- switch reply := reply.(type) {
- case []interface{}:
- result := make([][]byte, len(reply))
- for i := range reply {
- if reply[i] == nil {
- continue
- }
- p, ok := reply[i].([]byte)
- if !ok {
- return nil, fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", reply[i])
- }
- result[i] = p
- }
- return result, nil
- case nil:
- return nil, ErrNil
- case Error:
- return nil, reply
- }
- return nil, fmt.Errorf("redigo: unexpected type for ByteSlices, got type %T", reply)
-}
-
-// Ints is a helper that converts an array command reply to a []int. If
-// err is not equal to nil, then Ints returns nil, err.
-func Ints(reply interface{}, err error) ([]int, error) {
- var ints []int
- values, err := Values(reply, err)
- if err != nil {
- return ints, err
- }
- if err := ScanSlice(values, &ints); err != nil {
- return ints, err
- }
- return ints, nil
-}
-
-// StringMap is a helper that converts an array of strings (alternating key, value)
-// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format.
-// Requires an even number of values in result.
-func StringMap(result interface{}, err error) (map[string]string, error) {
- values, err := Values(result, err)
- if err != nil {
- return nil, err
- }
- if len(values)%2 != 0 {
- return nil, errors.New("redigo: StringMap expects even number of values result")
- }
- m := make(map[string]string, len(values)/2)
- for i := 0; i < len(values); i += 2 {
- key, okKey := values[i].([]byte)
- value, okValue := values[i+1].([]byte)
- if !okKey || !okValue {
- return nil, errors.New("redigo: ScanMap key not a bulk string value")
- }
- m[string(key)] = string(value)
- }
- return m, nil
-}
-
-// IntMap is a helper that converts an array of strings (alternating key, value)
-// into a map[string]int. The HGETALL commands return replies in this format.
-// Requires an even number of values in result.
-func IntMap(result interface{}, err error) (map[string]int, error) {
- values, err := Values(result, err)
- if err != nil {
- return nil, err
- }
- if len(values)%2 != 0 {
- return nil, errors.New("redigo: IntMap expects even number of values result")
- }
- m := make(map[string]int, len(values)/2)
- for i := 0; i < len(values); i += 2 {
- key, ok := values[i].([]byte)
- if !ok {
- return nil, errors.New("redigo: ScanMap key not a bulk string value")
- }
- value, err := Int(values[i+1], nil)
- if err != nil {
- return nil, err
- }
- m[string(key)] = value
- }
- return m, nil
-}
-
-// Int64Map is a helper that converts an array of strings (alternating key, value)
-// into a map[string]int64. The HGETALL commands return replies in this format.
-// Requires an even number of values in result.
-func Int64Map(result interface{}, err error) (map[string]int64, error) {
- values, err := Values(result, err)
- if err != nil {
- return nil, err
- }
- if len(values)%2 != 0 {
- return nil, errors.New("redigo: Int64Map expects even number of values result")
- }
- m := make(map[string]int64, len(values)/2)
- for i := 0; i < len(values); i += 2 {
- key, ok := values[i].([]byte)
- if !ok {
- return nil, errors.New("redigo: ScanMap key not a bulk string value")
- }
- value, err := Int64(values[i+1], nil)
- if err != nil {
- return nil, err
- }
- m[string(key)] = value
- }
- return m, nil
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/reply_test.go b/vendor/github.com/garyburd/redigo/redis/reply_test.go
deleted file mode 100644
index 2c774866d..000000000
--- a/vendor/github.com/garyburd/redigo/redis/reply_test.go
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis_test
-
-import (
- "fmt"
- "reflect"
- "testing"
-
- "github.com/garyburd/redigo/redis"
-)
-
-type valueError struct {
- v interface{}
- err error
-}
-
-func ve(v interface{}, err error) valueError {
- return valueError{v, err}
-}
-
-var replyTests = []struct {
- name interface{}
- actual valueError
- expected valueError
-}{
- {
- "ints([v1, v2])",
- ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)),
- ve([]int{4, 5}, nil),
- },
- {
- "ints(nil)",
- ve(redis.Ints(nil, nil)),
- ve([]int(nil), redis.ErrNil),
- },
- {
- "strings([v1, v2])",
- ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
- ve([]string{"v1", "v2"}, nil),
- },
- {
- "strings(nil)",
- ve(redis.Strings(nil, nil)),
- ve([]string(nil), redis.ErrNil),
- },
- {
- "byteslices([v1, v2])",
- ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
- ve([][]byte{[]byte("v1"), []byte("v2")}, nil),
- },
- {
- "byteslices(nil)",
- ve(redis.ByteSlices(nil, nil)),
- ve([][]byte(nil), redis.ErrNil),
- },
- {
- "values([v1, v2])",
- ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
- ve([]interface{}{[]byte("v1"), []byte("v2")}, nil),
- },
- {
- "values(nil)",
- ve(redis.Values(nil, nil)),
- ve([]interface{}(nil), redis.ErrNil),
- },
- {
- "float64(1.0)",
- ve(redis.Float64([]byte("1.0"), nil)),
- ve(float64(1.0), nil),
- },
- {
- "float64(nil)",
- ve(redis.Float64(nil, nil)),
- ve(float64(0.0), redis.ErrNil),
- },
- {
- "uint64(1)",
- ve(redis.Uint64(int64(1), nil)),
- ve(uint64(1), nil),
- },
- {
- "uint64(-1)",
- ve(redis.Uint64(int64(-1), nil)),
- ve(uint64(0), redis.ErrNegativeInt),
- },
-}
-
-func TestReply(t *testing.T) {
- for _, rt := range replyTests {
- if rt.actual.err != rt.expected.err {
- t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err)
- continue
- }
- if !reflect.DeepEqual(rt.actual.v, rt.expected.v) {
- t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v)
- }
- }
-}
-
-// dial wraps DialDefaultServer() with a more suitable function name for examples.
-func dial() (redis.Conn, error) {
- return redis.DialDefaultServer()
-}
-
-func ExampleBool() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- c.Do("SET", "foo", 1)
- exists, _ := redis.Bool(c.Do("EXISTS", "foo"))
- fmt.Printf("%#v\n", exists)
- // Output:
- // true
-}
-
-func ExampleInt() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- c.Do("SET", "k1", 1)
- n, _ := redis.Int(c.Do("GET", "k1"))
- fmt.Printf("%#v\n", n)
- n, _ = redis.Int(c.Do("INCR", "k1"))
- fmt.Printf("%#v\n", n)
- // Output:
- // 1
- // 2
-}
-
-func ExampleInts() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- c.Do("SADD", "set_with_integers", 4, 5, 6)
- ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers"))
- fmt.Printf("%#v\n", ints)
- // Output:
- // []int{4, 5, 6}
-}
-
-func ExampleString() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- c.Do("SET", "hello", "world")
- s, err := redis.String(c.Do("GET", "hello"))
- fmt.Printf("%#v\n", s)
- // Output:
- // "world"
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/scan.go b/vendor/github.com/garyburd/redigo/redis/scan.go
deleted file mode 100644
index 962e94bcc..000000000
--- a/vendor/github.com/garyburd/redigo/redis/scan.go
+++ /dev/null
@@ -1,555 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import (
- "errors"
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "sync"
-)
-
-func ensureLen(d reflect.Value, n int) {
- if n > d.Cap() {
- d.Set(reflect.MakeSlice(d.Type(), n, n))
- } else {
- d.SetLen(n)
- }
-}
-
-func cannotConvert(d reflect.Value, s interface{}) error {
- var sname string
- switch s.(type) {
- case string:
- sname = "Redis simple string"
- case Error:
- sname = "Redis error"
- case int64:
- sname = "Redis integer"
- case []byte:
- sname = "Redis bulk string"
- case []interface{}:
- sname = "Redis array"
- default:
- sname = reflect.TypeOf(s).String()
- }
- return fmt.Errorf("cannot convert from %s to %s", sname, d.Type())
-}
-
-func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
- switch d.Type().Kind() {
- case reflect.Float32, reflect.Float64:
- var x float64
- x, err = strconv.ParseFloat(string(s), d.Type().Bits())
- d.SetFloat(x)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- var x int64
- x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
- d.SetInt(x)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- var x uint64
- x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
- d.SetUint(x)
- case reflect.Bool:
- var x bool
- x, err = strconv.ParseBool(string(s))
- d.SetBool(x)
- case reflect.String:
- d.SetString(string(s))
- case reflect.Slice:
- if d.Type().Elem().Kind() != reflect.Uint8 {
- err = cannotConvert(d, s)
- } else {
- d.SetBytes(s)
- }
- default:
- err = cannotConvert(d, s)
- }
- return
-}
-
-func convertAssignInt(d reflect.Value, s int64) (err error) {
- switch d.Type().Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- d.SetInt(s)
- if d.Int() != s {
- err = strconv.ErrRange
- d.SetInt(0)
- }
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- if s < 0 {
- err = strconv.ErrRange
- } else {
- x := uint64(s)
- d.SetUint(x)
- if d.Uint() != x {
- err = strconv.ErrRange
- d.SetUint(0)
- }
- }
- case reflect.Bool:
- d.SetBool(s != 0)
- default:
- err = cannotConvert(d, s)
- }
- return
-}
-
-func convertAssignValue(d reflect.Value, s interface{}) (err error) {
- switch s := s.(type) {
- case []byte:
- err = convertAssignBulkString(d, s)
- case int64:
- err = convertAssignInt(d, s)
- default:
- err = cannotConvert(d, s)
- }
- return err
-}
-
-func convertAssignArray(d reflect.Value, s []interface{}) error {
- if d.Type().Kind() != reflect.Slice {
- return cannotConvert(d, s)
- }
- ensureLen(d, len(s))
- for i := 0; i < len(s); i++ {
- if err := convertAssignValue(d.Index(i), s[i]); err != nil {
- return err
- }
- }
- return nil
-}
-
-func convertAssign(d interface{}, s interface{}) (err error) {
- // Handle the most common destination types using type switches and
- // fall back to reflection for all other types.
- switch s := s.(type) {
- case nil:
- // ingore
- case []byte:
- switch d := d.(type) {
- case *string:
- *d = string(s)
- case *int:
- *d, err = strconv.Atoi(string(s))
- case *bool:
- *d, err = strconv.ParseBool(string(s))
- case *[]byte:
- *d = s
- case *interface{}:
- *d = s
- case nil:
- // skip value
- default:
- if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
- err = cannotConvert(d, s)
- } else {
- err = convertAssignBulkString(d.Elem(), s)
- }
- }
- case int64:
- switch d := d.(type) {
- case *int:
- x := int(s)
- if int64(x) != s {
- err = strconv.ErrRange
- x = 0
- }
- *d = x
- case *bool:
- *d = s != 0
- case *interface{}:
- *d = s
- case nil:
- // skip value
- default:
- if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
- err = cannotConvert(d, s)
- } else {
- err = convertAssignInt(d.Elem(), s)
- }
- }
- case string:
- switch d := d.(type) {
- case *string:
- *d = string(s)
- default:
- err = cannotConvert(reflect.ValueOf(d), s)
- }
- case []interface{}:
- switch d := d.(type) {
- case *[]interface{}:
- *d = s
- case *interface{}:
- *d = s
- case nil:
- // skip value
- default:
- if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
- err = cannotConvert(d, s)
- } else {
- err = convertAssignArray(d.Elem(), s)
- }
- }
- case Error:
- err = s
- default:
- err = cannotConvert(reflect.ValueOf(d), s)
- }
- return
-}
-
-// Scan copies from src to the values pointed at by dest.
-//
-// The values pointed at by dest must be an integer, float, boolean, string,
-// []byte, interface{} or slices of these types. Scan uses the standard strconv
-// package to convert bulk strings to numeric and boolean types.
-//
-// If a dest value is nil, then the corresponding src value is skipped.
-//
-// If a src element is nil, then the corresponding dest value is not modified.
-//
-// To enable easy use of Scan in a loop, Scan returns the slice of src
-// following the copied values.
-func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
- if len(src) < len(dest) {
- return nil, errors.New("redigo.Scan: array short")
- }
- var err error
- for i, d := range dest {
- err = convertAssign(d, src[i])
- if err != nil {
- err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err)
- break
- }
- }
- return src[len(dest):], err
-}
-
-type fieldSpec struct {
- name string
- index []int
- omitEmpty bool
-}
-
-type structSpec struct {
- m map[string]*fieldSpec
- l []*fieldSpec
-}
-
-func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
- return ss.m[string(name)]
-}
-
-func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
- switch {
- case f.PkgPath != "" && !f.Anonymous:
- // Ignore unexported fields.
- case f.Anonymous:
- // TODO: Handle pointers. Requires change to decoder and
- // protection against infinite recursion.
- if f.Type.Kind() == reflect.Struct {
- compileStructSpec(f.Type, depth, append(index, i), ss)
- }
- default:
- fs := &fieldSpec{name: f.Name}
- tag := f.Tag.Get("redis")
- p := strings.Split(tag, ",")
- if len(p) > 0 {
- if p[0] == "-" {
- continue
- }
- if len(p[0]) > 0 {
- fs.name = p[0]
- }
- for _, s := range p[1:] {
- switch s {
- case "omitempty":
- fs.omitEmpty = true
- default:
- panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name()))
- }
- }
- }
- d, found := depth[fs.name]
- if !found {
- d = 1 << 30
- }
- switch {
- case len(index) == d:
- // At same depth, remove from result.
- delete(ss.m, fs.name)
- j := 0
- for i := 0; i < len(ss.l); i++ {
- if fs.name != ss.l[i].name {
- ss.l[j] = ss.l[i]
- j += 1
- }
- }
- ss.l = ss.l[:j]
- case len(index) < d:
- fs.index = make([]int, len(index)+1)
- copy(fs.index, index)
- fs.index[len(index)] = i
- depth[fs.name] = len(index)
- ss.m[fs.name] = fs
- ss.l = append(ss.l, fs)
- }
- }
- }
-}
-
-var (
- structSpecMutex sync.RWMutex
- structSpecCache = make(map[reflect.Type]*structSpec)
- defaultFieldSpec = &fieldSpec{}
-)
-
-func structSpecForType(t reflect.Type) *structSpec {
-
- structSpecMutex.RLock()
- ss, found := structSpecCache[t]
- structSpecMutex.RUnlock()
- if found {
- return ss
- }
-
- structSpecMutex.Lock()
- defer structSpecMutex.Unlock()
- ss, found = structSpecCache[t]
- if found {
- return ss
- }
-
- ss = &structSpec{m: make(map[string]*fieldSpec)}
- compileStructSpec(t, make(map[string]int), nil, ss)
- structSpecCache[t] = ss
- return ss
-}
-
-var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct")
-
-// ScanStruct scans alternating names and values from src to a struct. The
-// HGETALL and CONFIG GET commands return replies in this format.
-//
-// ScanStruct uses exported field names to match values in the response. Use
-// 'redis' field tag to override the name:
-//
-// Field int `redis:"myName"`
-//
-// Fields with the tag redis:"-" are ignored.
-//
-// Integer, float, boolean, string and []byte fields are supported. Scan uses the
-// standard strconv package to convert bulk string values to numeric and
-// boolean types.
-//
-// If a src element is nil, then the corresponding field is not modified.
-func ScanStruct(src []interface{}, dest interface{}) error {
- d := reflect.ValueOf(dest)
- if d.Kind() != reflect.Ptr || d.IsNil() {
- return errScanStructValue
- }
- d = d.Elem()
- if d.Kind() != reflect.Struct {
- return errScanStructValue
- }
- ss := structSpecForType(d.Type())
-
- if len(src)%2 != 0 {
- return errors.New("redigo.ScanStruct: number of values not a multiple of 2")
- }
-
- for i := 0; i < len(src); i += 2 {
- s := src[i+1]
- if s == nil {
- continue
- }
- name, ok := src[i].([]byte)
- if !ok {
- return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
- }
- fs := ss.fieldSpec(name)
- if fs == nil {
- continue
- }
- if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
- return fmt.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err)
- }
- }
- return nil
-}
-
-var (
- errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
-)
-
-// ScanSlice scans src to the slice pointed to by dest. The elements the dest
-// slice must be integer, float, boolean, string, struct or pointer to struct
-// values.
-//
-// Struct fields must be integer, float, boolean or string values. All struct
-// fields are used unless a subset is specified using fieldNames.
-func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error {
- d := reflect.ValueOf(dest)
- if d.Kind() != reflect.Ptr || d.IsNil() {
- return errScanSliceValue
- }
- d = d.Elem()
- if d.Kind() != reflect.Slice {
- return errScanSliceValue
- }
-
- isPtr := false
- t := d.Type().Elem()
- if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
- isPtr = true
- t = t.Elem()
- }
-
- if t.Kind() != reflect.Struct {
- ensureLen(d, len(src))
- for i, s := range src {
- if s == nil {
- continue
- }
- if err := convertAssignValue(d.Index(i), s); err != nil {
- return fmt.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err)
- }
- }
- return nil
- }
-
- ss := structSpecForType(t)
- fss := ss.l
- if len(fieldNames) > 0 {
- fss = make([]*fieldSpec, len(fieldNames))
- for i, name := range fieldNames {
- fss[i] = ss.m[name]
- if fss[i] == nil {
- return fmt.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name)
- }
- }
- }
-
- if len(fss) == 0 {
- return errors.New("redigo.ScanSlice: no struct fields")
- }
-
- n := len(src) / len(fss)
- if n*len(fss) != len(src) {
- return errors.New("redigo.ScanSlice: length not a multiple of struct field count")
- }
-
- ensureLen(d, n)
- for i := 0; i < n; i++ {
- d := d.Index(i)
- if isPtr {
- if d.IsNil() {
- d.Set(reflect.New(t))
- }
- d = d.Elem()
- }
- for j, fs := range fss {
- s := src[i*len(fss)+j]
- if s == nil {
- continue
- }
- if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
- return fmt.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err)
- }
- }
- }
- return nil
-}
-
-// Args is a helper for constructing command arguments from structured values.
-type Args []interface{}
-
-// Add returns the result of appending value to args.
-func (args Args) Add(value ...interface{}) Args {
- return append(args, value...)
-}
-
-// AddFlat returns the result of appending the flattened value of v to args.
-//
-// Maps are flattened by appending the alternating keys and map values to args.
-//
-// Slices are flattened by appending the slice elements to args.
-//
-// Structs are flattened by appending the alternating names and values of
-// exported fields to args. If v is a nil struct pointer, then nothing is
-// appended. The 'redis' field tag overrides struct field names. See ScanStruct
-// for more information on the use of the 'redis' field tag.
-//
-// Other types are appended to args as is.
-func (args Args) AddFlat(v interface{}) Args {
- rv := reflect.ValueOf(v)
- switch rv.Kind() {
- case reflect.Struct:
- args = flattenStruct(args, rv)
- case reflect.Slice:
- for i := 0; i < rv.Len(); i++ {
- args = append(args, rv.Index(i).Interface())
- }
- case reflect.Map:
- for _, k := range rv.MapKeys() {
- args = append(args, k.Interface(), rv.MapIndex(k).Interface())
- }
- case reflect.Ptr:
- if rv.Type().Elem().Kind() == reflect.Struct {
- if !rv.IsNil() {
- args = flattenStruct(args, rv.Elem())
- }
- } else {
- args = append(args, v)
- }
- default:
- args = append(args, v)
- }
- return args
-}
-
-func flattenStruct(args Args, v reflect.Value) Args {
- ss := structSpecForType(v.Type())
- for _, fs := range ss.l {
- fv := v.FieldByIndex(fs.index)
- if fs.omitEmpty {
- var empty = false
- switch fv.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
- empty = fv.Len() == 0
- case reflect.Bool:
- empty = !fv.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- empty = fv.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- empty = fv.Uint() == 0
- case reflect.Float32, reflect.Float64:
- empty = fv.Float() == 0
- case reflect.Interface, reflect.Ptr:
- empty = fv.IsNil()
- }
- if empty {
- continue
- }
- }
- args = append(args, fs.name, fv.Interface())
- }
- return args
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/scan_test.go b/vendor/github.com/garyburd/redigo/redis/scan_test.go
deleted file mode 100644
index d364dff42..000000000
--- a/vendor/github.com/garyburd/redigo/redis/scan_test.go
+++ /dev/null
@@ -1,440 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis_test
-
-import (
- "fmt"
- "math"
- "reflect"
- "testing"
-
- "github.com/garyburd/redigo/redis"
-)
-
-var scanConversionTests = []struct {
- src interface{}
- dest interface{}
-}{
- {[]byte("-inf"), math.Inf(-1)},
- {[]byte("+inf"), math.Inf(1)},
- {[]byte("0"), float64(0)},
- {[]byte("3.14159"), float64(3.14159)},
- {[]byte("3.14"), float32(3.14)},
- {[]byte("-100"), int(-100)},
- {[]byte("101"), int(101)},
- {int64(102), int(102)},
- {[]byte("103"), uint(103)},
- {int64(104), uint(104)},
- {[]byte("105"), int8(105)},
- {int64(106), int8(106)},
- {[]byte("107"), uint8(107)},
- {int64(108), uint8(108)},
- {[]byte("0"), false},
- {int64(0), false},
- {[]byte("f"), false},
- {[]byte("1"), true},
- {int64(1), true},
- {[]byte("t"), true},
- {"hello", "hello"},
- {[]byte("hello"), "hello"},
- {[]byte("world"), []byte("world")},
- {[]interface{}{[]byte("foo")}, []interface{}{[]byte("foo")}},
- {[]interface{}{[]byte("foo")}, []string{"foo"}},
- {[]interface{}{[]byte("hello"), []byte("world")}, []string{"hello", "world"}},
- {[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}},
- {[]interface{}{[]byte("1")}, []int{1}},
- {[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}},
- {[]interface{}{[]byte("1"), []byte("2")}, []float64{1, 2}},
- {[]interface{}{[]byte("1")}, []byte{1}},
- {[]interface{}{[]byte("1")}, []bool{true}},
-}
-
-func TestScanConversion(t *testing.T) {
- for _, tt := range scanConversionTests {
- values := []interface{}{tt.src}
- dest := reflect.New(reflect.TypeOf(tt.dest))
- values, err := redis.Scan(values, dest.Interface())
- if err != nil {
- t.Errorf("Scan(%v) returned error %v", tt, err)
- continue
- }
- if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
- t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
- }
- }
-}
-
-var scanConversionErrorTests = []struct {
- src interface{}
- dest interface{}
-}{
- {[]byte("1234"), byte(0)},
- {int64(1234), byte(0)},
- {[]byte("-1"), byte(0)},
- {int64(-1), byte(0)},
- {[]byte("junk"), false},
- {redis.Error("blah"), false},
-}
-
-func TestScanConversionError(t *testing.T) {
- for _, tt := range scanConversionErrorTests {
- values := []interface{}{tt.src}
- dest := reflect.New(reflect.TypeOf(tt.dest))
- values, err := redis.Scan(values, dest.Interface())
- if err == nil {
- t.Errorf("Scan(%v) did not return error", tt)
- }
- }
-}
-
-func ExampleScan() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
- c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
- c.Send("HMSET", "album:3", "title", "Beat")
- c.Send("LPUSH", "albums", "1")
- c.Send("LPUSH", "albums", "2")
- c.Send("LPUSH", "albums", "3")
- values, err := redis.Values(c.Do("SORT", "albums",
- "BY", "album:*->rating",
- "GET", "album:*->title",
- "GET", "album:*->rating"))
- if err != nil {
- fmt.Println(err)
- return
- }
-
- for len(values) > 0 {
- var title string
- rating := -1 // initialize to illegal value to detect nil.
- values, err = redis.Scan(values, &title, &rating)
- if err != nil {
- fmt.Println(err)
- return
- }
- if rating == -1 {
- fmt.Println(title, "not-rated")
- } else {
- fmt.Println(title, rating)
- }
- }
- // Output:
- // Beat not-rated
- // Earthbound 1
- // Red 5
-}
-
-type s0 struct {
- X int
- Y int `redis:"y"`
- Bt bool
-}
-
-type s1 struct {
- X int `redis:"-"`
- I int `redis:"i"`
- U uint `redis:"u"`
- S string `redis:"s"`
- P []byte `redis:"p"`
- B bool `redis:"b"`
- Bt bool
- Bf bool
- s0
-}
-
-var scanStructTests = []struct {
- title string
- reply []string
- value interface{}
-}{
- {"basic",
- []string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"},
- &s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}},
- },
-}
-
-func TestScanStruct(t *testing.T) {
- for _, tt := range scanStructTests {
-
- var reply []interface{}
- for _, v := range tt.reply {
- reply = append(reply, []byte(v))
- }
-
- value := reflect.New(reflect.ValueOf(tt.value).Type().Elem())
-
- if err := redis.ScanStruct(reply, value.Interface()); err != nil {
- t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err)
- }
-
- if !reflect.DeepEqual(value.Interface(), tt.value) {
- t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value)
- }
- }
-}
-
-func TestBadScanStructArgs(t *testing.T) {
- x := []interface{}{"A", "b"}
- test := func(v interface{}) {
- if err := redis.ScanStruct(x, v); err == nil {
- t.Errorf("Expect error for ScanStruct(%T, %T)", x, v)
- }
- }
-
- test(nil)
-
- var v0 *struct{}
- test(v0)
-
- var v1 int
- test(&v1)
-
- x = x[:1]
- v2 := struct{ A string }{}
- test(&v2)
-}
-
-var scanSliceTests = []struct {
- src []interface{}
- fieldNames []string
- ok bool
- dest interface{}
-}{
- {
- []interface{}{[]byte("1"), nil, []byte("-1")},
- nil,
- true,
- []int{1, 0, -1},
- },
- {
- []interface{}{[]byte("1"), nil, []byte("2")},
- nil,
- true,
- []uint{1, 0, 2},
- },
- {
- []interface{}{[]byte("-1")},
- nil,
- false,
- []uint{1},
- },
- {
- []interface{}{[]byte("hello"), nil, []byte("world")},
- nil,
- true,
- [][]byte{[]byte("hello"), nil, []byte("world")},
- },
- {
- []interface{}{[]byte("hello"), nil, []byte("world")},
- nil,
- true,
- []string{"hello", "", "world"},
- },
- {
- []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
- nil,
- true,
- []struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
- },
- {
- []interface{}{[]byte("a1"), []byte("b1")},
- nil,
- false,
- []struct{ A, B, C string }{{"a1", "b1", ""}},
- },
- {
- []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
- nil,
- true,
- []*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
- },
- {
- []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
- []string{"A", "B"},
- true,
- []struct{ A, C, B string }{{"a1", "", "b1"}, {"a2", "", "b2"}},
- },
- {
- []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
- nil,
- false,
- []struct{}{},
- },
-}
-
-func TestScanSlice(t *testing.T) {
- for _, tt := range scanSliceTests {
-
- typ := reflect.ValueOf(tt.dest).Type()
- dest := reflect.New(typ)
-
- err := redis.ScanSlice(tt.src, dest.Interface(), tt.fieldNames...)
- if tt.ok != (err == nil) {
- t.Errorf("ScanSlice(%v, []%s, %v) returned error %v", tt.src, typ, tt.fieldNames, err)
- continue
- }
- if tt.ok && !reflect.DeepEqual(dest.Elem().Interface(), tt.dest) {
- t.Errorf("ScanSlice(src, []%s) returned %#v, want %#v", typ, dest.Elem().Interface(), tt.dest)
- }
- }
-}
-
-func ExampleScanSlice() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
- c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
- c.Send("HMSET", "album:3", "title", "Beat", "rating", 4)
- c.Send("LPUSH", "albums", "1")
- c.Send("LPUSH", "albums", "2")
- c.Send("LPUSH", "albums", "3")
- values, err := redis.Values(c.Do("SORT", "albums",
- "BY", "album:*->rating",
- "GET", "album:*->title",
- "GET", "album:*->rating"))
- if err != nil {
- fmt.Println(err)
- return
- }
-
- var albums []struct {
- Title string
- Rating int
- }
- if err := redis.ScanSlice(values, &albums); err != nil {
- fmt.Println(err)
- return
- }
- fmt.Printf("%v\n", albums)
- // Output:
- // [{Earthbound 1} {Beat 4} {Red 5}]
-}
-
-var argsTests = []struct {
- title string
- actual redis.Args
- expected redis.Args
-}{
- {"struct ptr",
- redis.Args{}.AddFlat(&struct {
- I int `redis:"i"`
- U uint `redis:"u"`
- S string `redis:"s"`
- P []byte `redis:"p"`
- M map[string]string `redis:"m"`
- Bt bool
- Bf bool
- }{
- -1234, 5678, "hello", []byte("world"), map[string]string{"hello": "world"}, true, false,
- }),
- redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "m", map[string]string{"hello": "world"}, "Bt", true, "Bf", false},
- },
- {"struct",
- redis.Args{}.AddFlat(struct{ I int }{123}),
- redis.Args{"I", 123},
- },
- {"slice",
- redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
- redis.Args{1, "a", "b", "c", 2},
- },
- {"struct omitempty",
- redis.Args{}.AddFlat(&struct {
- I int `redis:"i,omitempty"`
- U uint `redis:"u,omitempty"`
- S string `redis:"s,omitempty"`
- P []byte `redis:"p,omitempty"`
- M map[string]string `redis:"m,omitempty"`
- Bt bool `redis:"Bt,omitempty"`
- Bf bool `redis:"Bf,omitempty"`
- }{
- 0, 0, "", []byte{}, map[string]string{}, true, false,
- }),
- redis.Args{"Bt", true},
- },
-}
-
-func TestArgs(t *testing.T) {
- for _, tt := range argsTests {
- if !reflect.DeepEqual(tt.actual, tt.expected) {
- t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
- }
- }
-}
-
-func ExampleArgs() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- var p1, p2 struct {
- Title string `redis:"title"`
- Author string `redis:"author"`
- Body string `redis:"body"`
- }
-
- p1.Title = "Example"
- p1.Author = "Gary"
- p1.Body = "Hello"
-
- if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
- fmt.Println(err)
- return
- }
-
- m := map[string]string{
- "title": "Example2",
- "author": "Steve",
- "body": "Map",
- }
-
- if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
- fmt.Println(err)
- return
- }
-
- for _, id := range []string{"id1", "id2"} {
-
- v, err := redis.Values(c.Do("HGETALL", id))
- if err != nil {
- fmt.Println(err)
- return
- }
-
- if err := redis.ScanStruct(v, &p2); err != nil {
- fmt.Println(err)
- return
- }
-
- fmt.Printf("%+v\n", p2)
- }
-
- // Output:
- // {Title:Example Author:Gary Body:Hello}
- // {Title:Example2 Author:Steve Body:Map}
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/script.go b/vendor/github.com/garyburd/redigo/redis/script.go
deleted file mode 100644
index 78605a90a..000000000
--- a/vendor/github.com/garyburd/redigo/redis/script.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import (
- "crypto/sha1"
- "encoding/hex"
- "io"
- "strings"
-)
-
-// Script encapsulates the source, hash and key count for a Lua script. See
-// http://redis.io/commands/eval for information on scripts in Redis.
-type Script struct {
- keyCount int
- src string
- hash string
-}
-
-// NewScript returns a new script object. If keyCount is greater than or equal
-// to zero, then the count is automatically inserted in the EVAL command
-// argument list. If keyCount is less than zero, then the application supplies
-// the count as the first value in the keysAndArgs argument to the Do, Send and
-// SendHash methods.
-func NewScript(keyCount int, src string) *Script {
- h := sha1.New()
- io.WriteString(h, src)
- return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))}
-}
-
-func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} {
- var args []interface{}
- if s.keyCount < 0 {
- args = make([]interface{}, 1+len(keysAndArgs))
- args[0] = spec
- copy(args[1:], keysAndArgs)
- } else {
- args = make([]interface{}, 2+len(keysAndArgs))
- args[0] = spec
- args[1] = s.keyCount
- copy(args[2:], keysAndArgs)
- }
- return args
-}
-
-// Do evaluates the script. Under the covers, Do optimistically evaluates the
-// script using the EVALSHA command. If the command fails because the script is
-// not loaded, then Do evaluates the script using the EVAL command (thus
-// causing the script to load).
-func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) {
- v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...)
- if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") {
- v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...)
- }
- return v, err
-}
-
-// SendHash evaluates the script without waiting for the reply. The script is
-// evaluated with the EVALSHA command. The application must ensure that the
-// script is loaded by a previous call to Send, Do or Load methods.
-func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error {
- return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...)
-}
-
-// Send evaluates the script without waiting for the reply.
-func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error {
- return c.Send("EVAL", s.args(s.src, keysAndArgs)...)
-}
-
-// Load loads the script without evaluating it.
-func (s *Script) Load(c Conn) error {
- _, err := c.Do("SCRIPT", "LOAD", s.src)
- return err
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/script_test.go b/vendor/github.com/garyburd/redigo/redis/script_test.go
deleted file mode 100644
index af282415c..000000000
--- a/vendor/github.com/garyburd/redigo/redis/script_test.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis_test
-
-import (
- "fmt"
- "reflect"
- "testing"
- "time"
-
- "github.com/garyburd/redigo/redis"
-)
-
-var (
- // These variables are declared at package level to remove distracting
- // details from the examples.
- c redis.Conn
- reply interface{}
- err error
-)
-
-func ExampleScript() {
- // Initialize a package-level variable with a script.
- var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`)
-
- // In a function, use the script Do method to evaluate the script. The Do
- // method optimistically uses the EVALSHA command. If the script is not
- // loaded, then the Do method falls back to the EVAL command.
- reply, err = getScript.Do(c, "foo")
-}
-
-func TestScript(t *testing.T) {
- c, err := redis.DialDefaultServer()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- // To test fall back in Do, we make script unique by adding comment with current time.
- script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano())
- s := redis.NewScript(2, script)
- reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")}
-
- v, err := s.Do(c, "key1", "key2", "arg1", "arg2")
- if err != nil {
- t.Errorf("s.Do(c, ...) returned %v", err)
- }
-
- if !reflect.DeepEqual(v, reply) {
- t.Errorf("s.Do(c, ..); = %v, want %v", v, reply)
- }
-
- err = s.Load(c)
- if err != nil {
- t.Errorf("s.Load(c) returned %v", err)
- }
-
- err = s.SendHash(c, "key1", "key2", "arg1", "arg2")
- if err != nil {
- t.Errorf("s.SendHash(c, ...) returned %v", err)
- }
-
- err = c.Flush()
- if err != nil {
- t.Errorf("c.Flush() returned %v", err)
- }
-
- v, err = c.Receive()
- if !reflect.DeepEqual(v, reply) {
- t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply)
- }
-
- err = s.Send(c, "key1", "key2", "arg1", "arg2")
- if err != nil {
- t.Errorf("s.Send(c, ...) returned %v", err)
- }
-
- err = c.Flush()
- if err != nil {
- t.Errorf("c.Flush() returned %v", err)
- }
-
- v, err = c.Receive()
- if !reflect.DeepEqual(v, reply) {
- t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply)
- }
-
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/test_test.go b/vendor/github.com/garyburd/redigo/redis/test_test.go
deleted file mode 100644
index 7240fa1f3..000000000
--- a/vendor/github.com/garyburd/redigo/redis/test_test.go
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redis
-
-import (
- "bufio"
- "errors"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "strconv"
- "strings"
- "sync"
- "testing"
- "time"
-)
-
-func SetNowFunc(f func() time.Time) {
- nowFunc = f
-}
-
-var (
- ErrNegativeInt = errNegativeInt
-
- serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary")
- serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
- serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`")
- serverLog = ioutil.Discard
-
- defaultServerMu sync.Mutex
- defaultServer *Server
- defaultServerErr error
-)
-
-type Server struct {
- name string
- cmd *exec.Cmd
- done chan struct{}
-}
-
-func NewServer(name string, args ...string) (*Server, error) {
- s := &Server{
- name: name,
- cmd: exec.Command(*serverPath, args...),
- done: make(chan struct{}),
- }
-
- r, err := s.cmd.StdoutPipe()
- if err != nil {
- return nil, err
- }
-
- err = s.cmd.Start()
- if err != nil {
- return nil, err
- }
-
- ready := make(chan error, 1)
- go s.watch(r, ready)
-
- select {
- case err = <-ready:
- case <-time.After(time.Second * 10):
- err = errors.New("timeout waiting for server to start")
- }
-
- if err != nil {
- s.Stop()
- return nil, err
- }
-
- return s, nil
-}
-
-func (s *Server) watch(r io.Reader, ready chan error) {
- fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name)
- var listening bool
- var text string
- scn := bufio.NewScanner(r)
- for scn.Scan() {
- text = scn.Text()
- fmt.Fprintf(serverLog, "%s\n", text)
- if !listening {
- if strings.Contains(text, "The server is now ready to accept connections on port") {
- listening = true
- ready <- nil
- }
- }
- }
- if !listening {
- ready <- fmt.Errorf("server exited: %s", text)
- }
- s.cmd.Wait()
- fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name)
- close(s.done)
-}
-
-func (s *Server) Stop() {
- s.cmd.Process.Signal(os.Interrupt)
- <-s.done
-}
-
-// stopDefaultServer stops the server created by DialDefaultServer.
-func stopDefaultServer() {
- defaultServerMu.Lock()
- defer defaultServerMu.Unlock()
- if defaultServer != nil {
- defaultServer.Stop()
- defaultServer = nil
- }
-}
-
-// startDefaultServer starts the default server if not already running.
-func startDefaultServer() error {
- defaultServerMu.Lock()
- defer defaultServerMu.Unlock()
- if defaultServer != nil || defaultServerErr != nil {
- return defaultServerErr
- }
- defaultServer, defaultServerErr = NewServer(
- "default",
- "--port", strconv.Itoa(*serverBasePort),
- "--save", "",
- "--appendonly", "no")
- return defaultServerErr
-}
-
-// DialDefaultServer starts the test server if not already started and dials a
-// connection to the server.
-func DialDefaultServer() (Conn, error) {
- if err := startDefaultServer(); err != nil {
- return nil, err
- }
- c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
- if err != nil {
- return nil, err
- }
- c.Do("FLUSHDB")
- return c, nil
-}
-
-func TestMain(m *testing.M) {
- os.Exit(func() int {
- flag.Parse()
-
- var f *os.File
- if *serverLogName != "" {
- var err error
- f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err)
- return 1
- }
- defer f.Close()
- serverLog = f
- }
-
- defer stopDefaultServer()
-
- return m.Run()
- }())
-}
diff --git a/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go b/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
deleted file mode 100644
index 1d86ee6ce..000000000
--- a/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2013 Gary Burd
-//
-// 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 redis_test
-
-import (
- "fmt"
- "github.com/garyburd/redigo/redis"
-)
-
-// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands.
-func zpop(c redis.Conn, key string) (result string, err error) {
-
- defer func() {
- // Return connection to normal state on error.
- if err != nil {
- c.Do("DISCARD")
- }
- }()
-
- // Loop until transaction is successful.
- for {
- if _, err := c.Do("WATCH", key); err != nil {
- return "", err
- }
-
- members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0))
- if err != nil {
- return "", err
- }
- if len(members) != 1 {
- return "", redis.ErrNil
- }
-
- c.Send("MULTI")
- c.Send("ZREM", key, members[0])
- queued, err := c.Do("EXEC")
- if err != nil {
- return "", err
- }
-
- if queued != nil {
- result = members[0]
- break
- }
- }
-
- return result, nil
-}
-
-// zpopScript pops a value from a ZSET.
-var zpopScript = redis.NewScript(1, `
- local r = redis.call('ZRANGE', KEYS[1], 0, 0)
- if r ~= nil then
- r = r[1]
- redis.call('ZREM', KEYS[1], r)
- end
- return r
-`)
-
-// This example implements ZPOP as described at
-// http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting.
-func Example_zpop() {
- c, err := dial()
- if err != nil {
- fmt.Println(err)
- return
- }
- defer c.Close()
-
- // Add test data using a pipeline.
-
- for i, member := range []string{"red", "blue", "green"} {
- c.Send("ZADD", "zset", i, member)
- }
- if _, err := c.Do(""); err != nil {
- fmt.Println(err)
- return
- }
-
- // Pop using WATCH/MULTI/EXEC
-
- v, err := zpop(c, "zset")
- if err != nil {
- fmt.Println(err)
- return
- }
- fmt.Println(v)
-
- // Pop using a script.
-
- v, err = redis.String(zpopScript.Do(c, "zset"))
- if err != nil {
- fmt.Println(err)
- return
- }
- fmt.Println(v)
-
- // Output:
- // red
- // blue
-}
diff --git a/vendor/github.com/garyburd/redigo/redisx/connmux.go b/vendor/github.com/garyburd/redigo/redisx/connmux.go
deleted file mode 100644
index af2cced3f..000000000
--- a/vendor/github.com/garyburd/redigo/redisx/connmux.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 Gary Burd
-//
-// 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 redisx
-
-import (
- "errors"
- "sync"
-
- "github.com/garyburd/redigo/internal"
- "github.com/garyburd/redigo/redis"
-)
-
-// ConnMux multiplexes one or more connections to a single underlying
-// connection. The ConnMux connections do not support concurrency, commands
-// that associate server side state with the connection or commands that put
-// the connection in a special mode.
-type ConnMux struct {
- c redis.Conn
-
- sendMu sync.Mutex
- sendID uint
-
- recvMu sync.Mutex
- recvID uint
- recvWait map[uint]chan struct{}
-}
-
-func NewConnMux(c redis.Conn) *ConnMux {
- return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})}
-}
-
-// Get gets a connection. The application must close the returned connection.
-func (p *ConnMux) Get() redis.Conn {
- c := &muxConn{p: p}
- c.ids = c.buf[:0]
- return c
-}
-
-// Close closes the underlying connection.
-func (p *ConnMux) Close() error {
- return p.c.Close()
-}
-
-type muxConn struct {
- p *ConnMux
- ids []uint
- buf [8]uint
-}
-
-func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error {
- if internal.LookupCommandInfo(cmd).Set != 0 {
- return errors.New("command not supported by mux pool")
- }
- p := c.p
- p.sendMu.Lock()
- id := p.sendID
- c.ids = append(c.ids, id)
- p.sendID++
- err := p.c.Send(cmd, args...)
- if flush {
- err = p.c.Flush()
- }
- p.sendMu.Unlock()
- return err
-}
-
-func (c *muxConn) Send(cmd string, args ...interface{}) error {
- return c.send(false, cmd, args...)
-}
-
-func (c *muxConn) Flush() error {
- p := c.p
- p.sendMu.Lock()
- err := p.c.Flush()
- p.sendMu.Unlock()
- return err
-}
-
-func (c *muxConn) Receive() (interface{}, error) {
- if len(c.ids) == 0 {
- return nil, errors.New("mux pool underflow")
- }
-
- id := c.ids[0]
- c.ids = c.ids[1:]
- if len(c.ids) == 0 {
- c.ids = c.buf[:0]
- }
-
- p := c.p
- p.recvMu.Lock()
- if p.recvID != id {
- ch := make(chan struct{})
- p.recvWait[id] = ch
- p.recvMu.Unlock()
- <-ch
- p.recvMu.Lock()
- if p.recvID != id {
- panic("out of sync")
- }
- }
-
- v, err := p.c.Receive()
-
- id++
- p.recvID = id
- ch, ok := p.recvWait[id]
- if ok {
- delete(p.recvWait, id)
- }
- p.recvMu.Unlock()
- if ok {
- ch <- struct{}{}
- }
-
- return v, err
-}
-
-func (c *muxConn) Close() error {
- var err error
- if len(c.ids) == 0 {
- return nil
- }
- c.Flush()
- for _ = range c.ids {
- _, err = c.Receive()
- }
- return err
-}
-
-func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) {
- if err := c.send(true, cmd, args...); err != nil {
- return nil, err
- }
- return c.Receive()
-}
-
-func (c *muxConn) Err() error {
- return c.p.c.Err()
-}
diff --git a/vendor/github.com/garyburd/redigo/redisx/connmux_test.go b/vendor/github.com/garyburd/redigo/redisx/connmux_test.go
deleted file mode 100644
index 9c3c8b162..000000000
--- a/vendor/github.com/garyburd/redigo/redisx/connmux_test.go
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2014 Gary Burd
-//
-// 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 redisx_test
-
-import (
- "net/textproto"
- "sync"
- "testing"
-
- "github.com/garyburd/redigo/internal/redistest"
- "github.com/garyburd/redigo/redis"
- "github.com/garyburd/redigo/redisx"
-)
-
-func TestConnMux(t *testing.T) {
- c, err := redistest.Dial()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- m := redisx.NewConnMux(c)
- defer m.Close()
-
- c1 := m.Get()
- c2 := m.Get()
- c1.Send("ECHO", "hello")
- c2.Send("ECHO", "world")
- c1.Flush()
- c2.Flush()
- s, err := redis.String(c1.Receive())
- if err != nil {
- t.Fatal(err)
- }
- if s != "hello" {
- t.Fatalf("echo returned %q, want %q", s, "hello")
- }
- s, err = redis.String(c2.Receive())
- if err != nil {
- t.Fatal(err)
- }
- if s != "world" {
- t.Fatalf("echo returned %q, want %q", s, "world")
- }
- c1.Close()
- c2.Close()
-}
-
-func TestConnMuxClose(t *testing.T) {
- c, err := redistest.Dial()
- if err != nil {
- t.Fatalf("error connection to database, %v", err)
- }
- m := redisx.NewConnMux(c)
- defer m.Close()
-
- c1 := m.Get()
- c2 := m.Get()
-
- if err := c1.Send("ECHO", "hello"); err != nil {
- t.Fatal(err)
- }
- if err := c1.Close(); err != nil {
- t.Fatal(err)
- }
-
- if err := c2.Send("ECHO", "world"); err != nil {
- t.Fatal(err)
- }
- if err := c2.Flush(); err != nil {
- t.Fatal(err)
- }
-
- s, err := redis.String(c2.Receive())
- if err != nil {
- t.Fatal(err)
- }
- if s != "world" {
- t.Fatalf("echo returned %q, want %q", s, "world")
- }
- c2.Close()
-}
-
-func BenchmarkConn(b *testing.B) {
- b.StopTimer()
- c, err := redistest.Dial()
- if err != nil {
- b.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
- b.StartTimer()
-
- for i := 0; i < b.N; i++ {
- if _, err := c.Do("PING"); err != nil {
- b.Fatal(err)
- }
- }
-}
-
-func BenchmarkConnMux(b *testing.B) {
- b.StopTimer()
- c, err := redistest.Dial()
- if err != nil {
- b.Fatalf("error connection to database, %v", err)
- }
- m := redisx.NewConnMux(c)
- defer m.Close()
-
- b.StartTimer()
-
- for i := 0; i < b.N; i++ {
- c := m.Get()
- if _, err := c.Do("PING"); err != nil {
- b.Fatal(err)
- }
- c.Close()
- }
-}
-
-func BenchmarkPool(b *testing.B) {
- b.StopTimer()
-
- p := redis.Pool{Dial: redistest.Dial, MaxIdle: 1}
- defer p.Close()
-
- // Fill the pool.
- c := p.Get()
- if err := c.Err(); err != nil {
- b.Fatal(err)
- }
- c.Close()
-
- b.StartTimer()
-
- for i := 0; i < b.N; i++ {
- c := p.Get()
- if _, err := c.Do("PING"); err != nil {
- b.Fatal(err)
- }
- c.Close()
- }
-}
-
-const numConcurrent = 10
-
-func BenchmarkConnMuxConcurrent(b *testing.B) {
- b.StopTimer()
- c, err := redistest.Dial()
- if err != nil {
- b.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- m := redisx.NewConnMux(c)
-
- var wg sync.WaitGroup
- wg.Add(numConcurrent)
-
- b.StartTimer()
-
- for i := 0; i < numConcurrent; i++ {
- go func() {
- defer wg.Done()
- for i := 0; i < b.N; i++ {
- c := m.Get()
- if _, err := c.Do("PING"); err != nil {
- b.Fatal(err)
- }
- c.Close()
- }
- }()
- }
- wg.Wait()
-}
-
-func BenchmarkPoolConcurrent(b *testing.B) {
- b.StopTimer()
-
- p := redis.Pool{Dial: redistest.Dial, MaxIdle: numConcurrent}
- defer p.Close()
-
- // Fill the pool.
- conns := make([]redis.Conn, numConcurrent)
- for i := range conns {
- c := p.Get()
- if err := c.Err(); err != nil {
- b.Fatal(err)
- }
- conns[i] = c
- }
- for _, c := range conns {
- c.Close()
- }
-
- var wg sync.WaitGroup
- wg.Add(numConcurrent)
-
- b.StartTimer()
-
- for i := 0; i < numConcurrent; i++ {
- go func() {
- defer wg.Done()
- for i := 0; i < b.N; i++ {
- c := p.Get()
- if _, err := c.Do("PING"); err != nil {
- b.Fatal(err)
- }
- c.Close()
- }
- }()
- }
- wg.Wait()
-}
-
-func BenchmarkPipelineConcurrency(b *testing.B) {
- b.StopTimer()
- c, err := redistest.Dial()
- if err != nil {
- b.Fatalf("error connection to database, %v", err)
- }
- defer c.Close()
-
- var wg sync.WaitGroup
- wg.Add(numConcurrent)
-
- var pipeline textproto.Pipeline
-
- b.StartTimer()
-
- for i := 0; i < numConcurrent; i++ {
- go func() {
- defer wg.Done()
- for i := 0; i < b.N; i++ {
- id := pipeline.Next()
- pipeline.StartRequest(id)
- c.Send("PING")
- c.Flush()
- pipeline.EndRequest(id)
- pipeline.StartResponse(id)
- _, err := c.Receive()
- if err != nil {
- b.Fatal(err)
- }
- pipeline.EndResponse(id)
- }
- }()
- }
- wg.Wait()
-}
diff --git a/vendor/github.com/garyburd/redigo/redisx/doc.go b/vendor/github.com/garyburd/redigo/redisx/doc.go
deleted file mode 100644
index 91653dbe2..000000000
--- a/vendor/github.com/garyburd/redigo/redisx/doc.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2012 Gary Burd
-//
-// 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 redisx contains experimental features for Redigo. Features in this
-// package may be modified or deleted at any time.
-package redisx // import "github.com/garyburd/redigo/redisx"
diff --git a/vendor/github.com/go-ldap/ldap/.githooks/pre-push b/vendor/github.com/go-ldap/ldap/.githooks/pre-push
new file mode 100755
index 000000000..4325ee31c
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/.githooks/pre-push
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+# install from the root of the repo with:
+# ln -s ../../.githooks/pre-push .git/hooks/pre-push
+
+make vet fmt lint \ No newline at end of file
diff --git a/vendor/github.com/go-ldap/ldap/.travis.yml b/vendor/github.com/go-ldap/ldap/.travis.yml
index a7a38951b..7e2f641e7 100644
--- a/vendor/github.com/go-ldap/ldap/.travis.yml
+++ b/vendor/github.com/go-ldap/ldap/.travis.yml
@@ -1,15 +1,24 @@
language: go
+env:
+ global:
+ - VET_VERSIONS="1.5 1.6 tip"
+ - LINT_VERSIONS="1.5 1.6 tip"
go:
- 1.2
- 1.3
- 1.4
- 1.5
+ - 1.6
- tip
go_import_path: gopkg.in/ldap.v2
install:
- go get gopkg.in/asn1-ber.v1
- go get gopkg.in/ldap.v2
- go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
+ - go get github.com/golang/lint/golint || true
- go build -v ./...
script:
- - go test -v -cover ./...
+ - make test
+ - make fmt
+ - if [[ "$VET_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make vet; fi
+ - if [[ "$LINT_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make lint; fi
diff --git a/vendor/github.com/go-ldap/ldap/Makefile b/vendor/github.com/go-ldap/ldap/Makefile
new file mode 100644
index 000000000..c1fc96657
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/Makefile
@@ -0,0 +1,42 @@
+.PHONY: default install build test quicktest fmt vet lint
+
+default: fmt vet lint build quicktest
+
+install:
+ go get -t -v ./...
+
+build:
+ go build -v ./...
+
+test:
+ go test -v -cover ./...
+
+quicktest:
+ go test ./...
+
+# Capture output and force failure when there is non-empty output
+fmt:
+ @echo gofmt -l .
+ @OUTPUT=`gofmt -l . 2>&1`; \
+ if [ "$$OUTPUT" ]; then \
+ echo "gofmt must be run on the following files:"; \
+ echo "$$OUTPUT"; \
+ exit 1; \
+ fi
+
+# Only run on go1.5+
+vet:
+ go tool vet -atomic -bool -copylocks -nilfunc -printf -shadow -rangeloops -unreachable -unsafeptr -unusedresult .
+
+# https://github.com/golang/lint
+# go get github.com/golang/lint/golint
+# Capture output and force failure when there is non-empty output
+# Only run on go1.5+
+lint:
+ @echo golint ./...
+ @OUTPUT=`golint ./... 2>&1`; \
+ if [ "$$OUTPUT" ]; then \
+ echo "golint errors:"; \
+ echo "$$OUTPUT"; \
+ exit 1; \
+ fi
diff --git a/vendor/github.com/go-ldap/ldap/README.md b/vendor/github.com/go-ldap/ldap/README.md
index f49b4d6a1..a26ed2d82 100644
--- a/vendor/github.com/go-ldap/ldap/README.md
+++ b/vendor/github.com/go-ldap/ldap/README.md
@@ -13,41 +13,39 @@ Import the latest version with:
import "gopkg.in/ldap.v2"
-
## Required Libraries:
- gopkg.in/asn1-ber.v1
-## Working:
+## Features:
- - Connecting to LDAP server
+ - Connecting to LDAP server (non-TLS, TLS, STARTTLS)
- Binding to LDAP server
- Searching for entries
- - Compiling string filters to LDAP filters
+ - Filter Compile / Decompile
- Paging Search Results
- Modify Requests / Responses
- Add Requests / Responses
- Delete Requests / Responses
- - Better Unicode support
## Examples:
- search
- modify
-## Tests Implemented:
-
- - Filter Compile / Decompile
-
-## TODO:
+## Contributing:
- - [x] Add Requests / Responses
- - [x] Delete Requests / Responses
- - [x] Modify DN Requests / Responses
- - [ ] Compare Requests / Responses
- - [ ] Implement Tests / Benchmarks
+Bug reports and pull requests are welcome!
+Before submitting a pull request, please make sure tests and verification scripts pass:
+```
+make all
+```
+To set up a pre-push hook to run the tests and verify scripts before pushing:
+```
+ln -s ../../.githooks/pre-push .git/hooks/pre-push
+```
---
The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
diff --git a/vendor/github.com/go-ldap/ldap/add.go b/vendor/github.com/go-ldap/ldap/add.go
index 61b795e0e..0e5f6cdba 100644
--- a/vendor/github.com/go-ldap/ldap/add.go
+++ b/vendor/github.com/go-ldap/ldap/add.go
@@ -16,73 +16,78 @@ import (
"gopkg.in/asn1-ber.v1"
)
+// Attribute represents an LDAP attribute
type Attribute struct {
- attrType string
- attrVals []string
+ // Type is the name of the LDAP attribute
+ Type string
+ // Vals are the LDAP attribute values
+ Vals []string
}
func (a *Attribute) encode() *ber.Packet {
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
- seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.attrType, "Type"))
+ seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type"))
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
- for _, value := range a.attrVals {
+ for _, value := range a.Vals {
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
}
seq.AppendChild(set)
return seq
}
+// AddRequest represents an LDAP AddRequest operation
type AddRequest struct {
- dn string
- attributes []Attribute
+ // DN identifies the entry being added
+ DN string
+ // Attributes list the attributes of the new entry
+ Attributes []Attribute
}
func (a AddRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
- request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.dn, "DN"))
+ request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN"))
attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
- for _, attribute := range a.attributes {
+ for _, attribute := range a.Attributes {
attributes.AppendChild(attribute.encode())
}
request.AppendChild(attributes)
return request
}
+// Attribute adds an attribute with the given type and values
func (a *AddRequest) Attribute(attrType string, attrVals []string) {
- a.attributes = append(a.attributes, Attribute{attrType: attrType, attrVals: attrVals})
+ a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals})
}
+// NewAddRequest returns an AddRequest for the given DN, with no attributes
func NewAddRequest(dn string) *AddRequest {
return &AddRequest{
- dn: dn,
+ DN: dn,
}
}
+// Add performs the given AddRequest
func (l *Conn) Add(addRequest *AddRequest) error {
- messageID := l.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
packet.AppendChild(addRequest.encode())
l.Debug.PrintPacket(packet)
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
- if channel == nil {
- return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
- l.Debug.Printf("%d: waiting for response", messageID)
- packetResponse, ok := <-channel
+ l.Debug.Printf("%d: waiting for response", msgCtx.id)
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
@@ -103,6 +108,6 @@ func (l *Conn) Add(addRequest *AddRequest) error {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
- l.Debug.Printf("%d: returning", messageID)
+ l.Debug.Printf("%d: returning", msgCtx.id)
return nil
}
diff --git a/vendor/github.com/go-ldap/ldap/bind.go b/vendor/github.com/go-ldap/ldap/bind.go
index ae68eb481..26b3cc727 100644
--- a/vendor/github.com/go-ldap/ldap/bind.go
+++ b/vendor/github.com/go-ldap/ldap/bind.go
@@ -10,16 +10,22 @@ import (
"gopkg.in/asn1-ber.v1"
)
+// SimpleBindRequest represents a username/password bind operation
type SimpleBindRequest struct {
+ // Username is the name of the Directory object that the client wishes to bind as
Username string
+ // Password is the credentials to bind with
Password string
+ // Controls are optional controls to send with the bind request
Controls []Control
}
+// SimpleBindResult contains the response from the server
type SimpleBindResult struct {
Controls []Control
}
+// NewSimpleBindRequest returns a bind request
func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
return &SimpleBindRequest{
Username: username,
@@ -39,11 +45,10 @@ func (bindRequest *SimpleBindRequest) encode() *ber.Packet {
return request
}
+// SimpleBind performs the simple bind operation defined in the given request
func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
- messageID := l.nextMessageID()
-
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
encodedBindRequest := simpleBindRequest.encode()
packet.AppendChild(encodedBindRequest)
@@ -51,21 +56,18 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu
ber.PrintPacket(packet)
}
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return nil, err
}
- if channel == nil {
- return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
- packetResponse, ok := <-channel
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return nil, err
}
@@ -95,11 +97,10 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu
return result, nil
}
+// Bind performs a bind with the given username and password
func (l *Conn) Bind(username, password string) error {
- messageID := l.nextMessageID()
-
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name"))
@@ -110,21 +111,18 @@ func (l *Conn) Bind(username, password string) error {
ber.PrintPacket(packet)
}
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
- if channel == nil {
- return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
- packetResponse, ok := <-channel
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
diff --git a/vendor/github.com/go-ldap/ldap/compare.go b/vendor/github.com/go-ldap/ldap/compare.go
index dfe728bad..cc6d2af5e 100644
--- a/vendor/github.com/go-ldap/ldap/compare.go
+++ b/vendor/github.com/go-ldap/ldap/compare.go
@@ -33,9 +33,8 @@ import (
// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
// false with any error that occurs if any.
func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
- messageID := l.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN"))
@@ -48,22 +47,19 @@ func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
l.Debug.PrintPacket(packet)
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return false, err
}
- if channel == nil {
- return false, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
- l.Debug.Printf("%d: waiting for response", messageID)
- packetResponse, ok := <-channel
+ l.Debug.Printf("%d: waiting for response", msgCtx.id)
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return false, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return false, err
}
diff --git a/vendor/github.com/go-ldap/ldap/conn.go b/vendor/github.com/go-ldap/ldap/conn.go
index 6aad628be..b5bd99adb 100644
--- a/vendor/github.com/go-ldap/ldap/conn.go
+++ b/vendor/github.com/go-ldap/ldap/conn.go
@@ -17,18 +17,27 @@ import (
)
const (
- MessageQuit = 0
- MessageRequest = 1
+ // MessageQuit causes the processMessages loop to exit
+ MessageQuit = 0
+ // MessageRequest sends a request to the server
+ MessageRequest = 1
+ // MessageResponse receives a response from the server
MessageResponse = 2
- MessageFinish = 3
- MessageTimeout = 4
+ // MessageFinish indicates the client considers a particular message ID to be finished
+ MessageFinish = 3
+ // MessageTimeout indicates the client-specified timeout for a particular message ID has been reached
+ MessageTimeout = 4
)
+// PacketResponse contains the packet or error encountered reading a response
type PacketResponse struct {
+ // Packet is the packet read from the server
Packet *ber.Packet
- Error error
+ // Error is an error encountered while reading
+ Error error
}
+// ReadPacket returns the packet or an error
func (pr *PacketResponse) ReadPacket() (*ber.Packet, error) {
if (pr == nil) || (pr.Packet == nil && pr.Error == nil) {
return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve response"))
@@ -36,11 +45,31 @@ func (pr *PacketResponse) ReadPacket() (*ber.Packet, error) {
return pr.Packet, pr.Error
}
+type messageContext struct {
+ id int64
+ // close(done) should only be called from finishMessage()
+ done chan struct{}
+ // close(responses) should only be called from processMessages(), and only sent to from sendResponse()
+ responses chan *PacketResponse
+}
+
+// sendResponse should only be called within the processMessages() loop which
+// is also responsible for closing the responses channel.
+func (msgCtx *messageContext) sendResponse(packet *PacketResponse) {
+ select {
+ case msgCtx.responses <- packet:
+ // Successfully sent packet to message handler.
+ case <-msgCtx.done:
+ // The request handler is done and will not receive more
+ // packets.
+ }
+}
+
type messagePacket struct {
Op int
MessageID int64
Packet *ber.Packet
- Channel chan *PacketResponse
+ Context *messageContext
}
type sendMessageFlags uint
@@ -54,10 +83,11 @@ type Conn struct {
conn net.Conn
isTLS bool
isClosing bool
+ closeErr error
isStartingTLS bool
Debug debugging
chanConfirm chan bool
- chanResults map[int64]chan *PacketResponse
+ messageContexts map[int64]*messageContext
chanMessage chan *messagePacket
chanMessageID chan int64
wgSender sync.WaitGroup
@@ -111,16 +141,17 @@ func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
// NewConn returns a new Conn using conn for network I/O.
func NewConn(conn net.Conn, isTLS bool) *Conn {
return &Conn{
- conn: conn,
- chanConfirm: make(chan bool),
- chanMessageID: make(chan int64),
- chanMessage: make(chan *messagePacket, 10),
- chanResults: map[int64]chan *PacketResponse{},
- requestTimeout: 0,
- isTLS: isTLS,
+ conn: conn,
+ chanConfirm: make(chan bool),
+ chanMessageID: make(chan int64),
+ chanMessage: make(chan *messagePacket, 10),
+ messageContexts: map[int64]*messageContext{},
+ requestTimeout: 0,
+ isTLS: isTLS,
}
}
+// Start initializes goroutines to read responses and process messages
func (l *Conn) Start() {
go l.reader()
go l.processMessages()
@@ -148,7 +179,7 @@ func (l *Conn) Close() {
l.wgClose.Wait()
}
-// Sets the time after a request is sent that a MessageTimeout triggers
+// SetTimeout sets the time after a request is sent that a MessageTimeout triggers
func (l *Conn) SetTimeout(timeout time.Duration) {
if timeout > 0 {
l.requestTimeout = timeout
@@ -167,35 +198,31 @@ func (l *Conn) nextMessageID() int64 {
// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *Conn) StartTLS(config *tls.Config) error {
- messageID := l.nextMessageID()
-
if l.isTLS {
return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
}
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
packet.AppendChild(request)
l.Debug.PrintPacket(packet)
- channel, err := l.sendMessageWithFlags(packet, startTLS)
+ msgCtx, err := l.sendMessageWithFlags(packet, startTLS)
if err != nil {
return err
}
- if channel == nil {
- return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
+ defer l.finishMessage(msgCtx)
- l.Debug.Printf("%d: waiting for response", messageID)
- defer l.finishMessage(messageID)
- packetResponse, ok := <-channel
+ l.Debug.Printf("%d: waiting for response", msgCtx.id)
+
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
@@ -226,11 +253,11 @@ func (l *Conn) StartTLS(config *tls.Config) error {
return nil
}
-func (l *Conn) sendMessage(packet *ber.Packet) (chan *PacketResponse, error) {
+func (l *Conn) sendMessage(packet *ber.Packet) (*messageContext, error) {
return l.sendMessageWithFlags(packet, 0)
}
-func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (chan *PacketResponse, error) {
+func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (*messageContext, error) {
if l.isClosing {
return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
}
@@ -238,32 +265,38 @@ func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags)
l.Debug.Printf("flags&startTLS = %d", flags&startTLS)
if l.isStartingTLS {
l.messageMutex.Unlock()
- return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase."))
+ return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase"))
}
if flags&startTLS != 0 {
if l.outstandingRequests != 0 {
l.messageMutex.Unlock()
return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests"))
- } else {
- l.isStartingTLS = true
}
+ l.isStartingTLS = true
}
l.outstandingRequests++
l.messageMutex.Unlock()
- out := make(chan *PacketResponse)
+ responses := make(chan *PacketResponse)
+ messageID := packet.Children[0].Value.(int64)
message := &messagePacket{
Op: MessageRequest,
- MessageID: packet.Children[0].Value.(int64),
+ MessageID: messageID,
Packet: packet,
- Channel: out,
+ Context: &messageContext{
+ id: messageID,
+ done: make(chan struct{}),
+ responses: responses,
+ },
}
l.sendProcessMessage(message)
- return out, nil
+ return message.Context, nil
}
-func (l *Conn) finishMessage(messageID int64) {
+func (l *Conn) finishMessage(msgCtx *messageContext) {
+ close(msgCtx.done)
+
if l.isClosing {
return
}
@@ -277,7 +310,7 @@ func (l *Conn) finishMessage(messageID int64) {
message := &messagePacket{
Op: MessageFinish,
- MessageID: messageID,
+ MessageID: msgCtx.id,
}
l.sendProcessMessage(message)
}
@@ -297,10 +330,15 @@ func (l *Conn) processMessages() {
if err := recover(); err != nil {
log.Printf("ldap: recovered panic in processMessages: %v", err)
}
- for messageID, channel := range l.chanResults {
+ for messageID, msgCtx := range l.messageContexts {
+ // If we are closing due to an error, inform anyone who
+ // is waiting about the error.
+ if l.isClosing && l.closeErr != nil {
+ msgCtx.sendResponse(&PacketResponse{Error: l.closeErr})
+ }
l.Debug.Printf("Closing channel for MessageID %d", messageID)
- close(channel)
- delete(l.chanResults, messageID)
+ close(msgCtx.responses)
+ delete(l.messageContexts, messageID)
}
close(l.chanMessageID)
l.chanConfirm <- true
@@ -324,15 +362,20 @@ func (l *Conn) processMessages() {
case MessageRequest:
// Add to message list and write to network
l.Debug.Printf("Sending message %d", message.MessageID)
- l.chanResults[message.MessageID] = message.Channel
buf := message.Packet.Bytes()
_, err := l.conn.Write(buf)
if err != nil {
l.Debug.Printf("Error Sending Message: %s", err.Error())
+ message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)})
+ close(message.Context.responses)
break
}
+ // Only add to messageContexts if we were able to
+ // successfully write the message.
+ l.messageContexts[message.MessageID] = message.Context
+
// Add timeout if defined
if l.requestTimeout > 0 {
go func() {
@@ -351,8 +394,8 @@ func (l *Conn) processMessages() {
}
case MessageResponse:
l.Debug.Printf("Receiving message %d", message.MessageID)
- if chanResult, ok := l.chanResults[message.MessageID]; ok {
- chanResult <- &PacketResponse{message.Packet, nil}
+ if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
+ msgCtx.sendResponse(&PacketResponse{message.Packet, nil})
} else {
log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing)
ber.PrintPacket(message.Packet)
@@ -360,17 +403,17 @@ func (l *Conn) processMessages() {
case MessageTimeout:
// Handle the timeout by closing the channel
// All reads will return immediately
- if chanResult, ok := l.chanResults[message.MessageID]; ok {
- chanResult <- &PacketResponse{message.Packet, errors.New("ldap: connection timed out")}
+ if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
l.Debug.Printf("Receiving message timeout for %d", message.MessageID)
- delete(l.chanResults, message.MessageID)
- close(chanResult)
+ msgCtx.sendResponse(&PacketResponse{message.Packet, errors.New("ldap: connection timed out")})
+ delete(l.messageContexts, message.MessageID)
+ close(msgCtx.responses)
}
case MessageFinish:
l.Debug.Printf("Finished message %d", message.MessageID)
- if chanResult, ok := l.chanResults[message.MessageID]; ok {
- close(chanResult)
- delete(l.chanResults, message.MessageID)
+ if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
+ delete(l.messageContexts, message.MessageID)
+ close(msgCtx.responses)
}
}
}
@@ -397,6 +440,7 @@ func (l *Conn) reader() {
if err != nil {
// A read error is expected here if we are closing the connection...
if !l.isClosing {
+ l.closeErr = fmt.Errorf("unable to read LDAP response packet: %s", err)
l.Debug.Printf("reader error: %s", err.Error())
}
return
@@ -419,6 +463,5 @@ func (l *Conn) reader() {
if !l.sendProcessMessage(message) {
return
}
-
}
}
diff --git a/vendor/github.com/go-ldap/ldap/conn_test.go b/vendor/github.com/go-ldap/ldap/conn_test.go
index 8394e5339..10766bbd4 100644
--- a/vendor/github.com/go-ldap/ldap/conn_test.go
+++ b/vendor/github.com/go-ldap/ldap/conn_test.go
@@ -1,9 +1,14 @@
package ldap
import (
+ "bytes"
+ "errors"
+ "io"
"net"
"net/http"
"net/http/httptest"
+ "runtime"
+ "sync"
"testing"
"time"
@@ -27,19 +32,20 @@ func TestUnresponsiveConnection(t *testing.T) {
defer conn.Close()
// Mock a packet
- messageID := conn.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, conn.nextMessageID(), "MessageID"))
bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
packet.AppendChild(bindRequest)
// Send packet and test response
- channel, err := conn.sendMessage(packet)
+ msgCtx, err := conn.sendMessage(packet)
if err != nil {
t.Fatalf("error sending message: %v", err)
}
- packetResponse, ok := <-channel
+ defer conn.finishMessage(msgCtx)
+
+ packetResponse, ok := <-msgCtx.responses
if !ok {
t.Fatalf("no PacketResponse in response channel")
}
@@ -51,3 +57,284 @@ func TestUnresponsiveConnection(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
}
+
+// TestFinishMessage tests that we do not enter deadlock when a goroutine makes
+// a request but does not handle all responses from the server.
+func TestConn(t *testing.T) {
+ ptc := newPacketTranslatorConn()
+ defer ptc.Close()
+
+ conn := NewConn(ptc, false)
+ conn.Start()
+
+ // Test sending 5 different requests in series. Ensure that we can
+ // get a response packet from the underlying connection and also
+ // ensure that we can gracefully ignore unhandled responses.
+ for i := 0; i < 5; i++ {
+ t.Logf("serial request %d", i)
+ // Create a message and make sure we can receive responses.
+ msgCtx := testSendRequest(t, ptc, conn)
+ testReceiveResponse(t, ptc, msgCtx)
+
+ // Send a few unhandled responses and finish the message.
+ testSendUnhandledResponsesAndFinish(t, ptc, conn, msgCtx, 5)
+ t.Logf("serial request %d done", i)
+ }
+
+ // Test sending 5 different requests in parallel.
+ var wg sync.WaitGroup
+ for i := 0; i < 5; i++ {
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+ t.Logf("parallel request %d", i)
+ // Create a message and make sure we can receive responses.
+ msgCtx := testSendRequest(t, ptc, conn)
+ testReceiveResponse(t, ptc, msgCtx)
+
+ // Send a few unhandled responses and finish the message.
+ testSendUnhandledResponsesAndFinish(t, ptc, conn, msgCtx, 5)
+ t.Logf("parallel request %d done", i)
+ }(i)
+ }
+ wg.Wait()
+
+ // We cannot run Close() in a defer because t.FailNow() will run it and
+ // it will block if the processMessage Loop is in a deadlock.
+ conn.Close()
+}
+
+func testSendRequest(t *testing.T, ptc *packetTranslatorConn, conn *Conn) (msgCtx *messageContext) {
+ var msgID int64
+ runWithTimeout(t, time.Second, func() {
+ msgID = conn.nextMessageID()
+ })
+
+ requestPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ requestPacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, msgID, "MessageID"))
+
+ var err error
+
+ runWithTimeout(t, time.Second, func() {
+ msgCtx, err = conn.sendMessage(requestPacket)
+ if err != nil {
+ t.Fatalf("unable to send request message: %s", err)
+ }
+ })
+
+ // We should now be able to get this request packet out from the other
+ // side.
+ runWithTimeout(t, time.Second, func() {
+ if _, err = ptc.ReceiveRequest(); err != nil {
+ t.Fatalf("unable to receive request packet: %s", err)
+ }
+ })
+
+ return msgCtx
+}
+
+func testReceiveResponse(t *testing.T, ptc *packetTranslatorConn, msgCtx *messageContext) {
+ // Send a mock response packet.
+ responsePacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Response")
+ responsePacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, msgCtx.id, "MessageID"))
+
+ runWithTimeout(t, time.Second, func() {
+ if err := ptc.SendResponse(responsePacket); err != nil {
+ t.Fatalf("unable to send response packet: %s", err)
+ }
+ })
+
+ // We should be able to receive the packet from the connection.
+ runWithTimeout(t, time.Second, func() {
+ if _, ok := <-msgCtx.responses; !ok {
+ t.Fatal("response channel closed")
+ }
+ })
+}
+
+func testSendUnhandledResponsesAndFinish(t *testing.T, ptc *packetTranslatorConn, conn *Conn, msgCtx *messageContext, numResponses int) {
+ // Send a mock response packet.
+ responsePacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Response")
+ responsePacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, msgCtx.id, "MessageID"))
+
+ // Send extra responses but do not attempt to receive them on the
+ // client side.
+ for i := 0; i < numResponses; i++ {
+ runWithTimeout(t, time.Second, func() {
+ if err := ptc.SendResponse(responsePacket); err != nil {
+ t.Fatalf("unable to send response packet: %s", err)
+ }
+ })
+ }
+
+ // Finally, attempt to finish this message.
+ runWithTimeout(t, time.Second, func() {
+ conn.finishMessage(msgCtx)
+ })
+}
+
+func runWithTimeout(t *testing.T, timeout time.Duration, f func()) {
+ runtime.Gosched()
+
+ done := make(chan struct{})
+ go func() {
+ f()
+ close(done)
+ }()
+
+ runtime.Gosched()
+
+ select {
+ case <-done: // Success!
+ case <-time.After(timeout):
+ _, file, line, _ := runtime.Caller(1)
+ t.Fatalf("%s:%d timed out", file, line)
+ }
+}
+
+// packetTranslatorConn is a helful type which can be used with various tests
+// in this package. It implements the net.Conn interface to be used as an
+// underlying connection for a *ldap.Conn. Most methods are no-ops but the
+// Read() and Write() methods are able to translate ber-encoded packets for
+// testing LDAP requests and responses.
+//
+// Test cases can simulate an LDAP server sending a response by calling the
+// SendResponse() method with a ber-encoded LDAP response packet. Test cases
+// can simulate an LDAP server receiving a request from a client by calling the
+// ReceiveRequest() method which returns a ber-encoded LDAP request packet.
+type packetTranslatorConn struct {
+ lock sync.Mutex
+ isClosed bool
+
+ responseCond sync.Cond
+ requestCond sync.Cond
+
+ responseBuf bytes.Buffer
+ requestBuf bytes.Buffer
+}
+
+var errPacketTranslatorConnClosed = errors.New("connection closed")
+
+func newPacketTranslatorConn() *packetTranslatorConn {
+ conn := &packetTranslatorConn{}
+ conn.responseCond = sync.Cond{L: &conn.lock}
+ conn.requestCond = sync.Cond{L: &conn.lock}
+
+ return conn
+}
+
+// Read is called by the reader() loop to receive response packets. It will
+// block until there are more packet bytes available or this connection is
+// closed.
+func (c *packetTranslatorConn) Read(b []byte) (n int, err error) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ for !c.isClosed {
+ // Attempt to read data from the response buffer. If it fails
+ // with an EOF, wait and try again.
+ n, err = c.responseBuf.Read(b)
+ if err != io.EOF {
+ return n, err
+ }
+
+ c.responseCond.Wait()
+ }
+
+ return 0, errPacketTranslatorConnClosed
+}
+
+// SendResponse writes the given response packet to the response buffer for
+// this conection, signalling any goroutine waiting to read a response.
+func (c *packetTranslatorConn) SendResponse(packet *ber.Packet) error {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if c.isClosed {
+ return errPacketTranslatorConnClosed
+ }
+
+ // Signal any goroutine waiting to read a response.
+ defer c.responseCond.Broadcast()
+
+ // Writes to the buffer should always succeed.
+ c.responseBuf.Write(packet.Bytes())
+
+ return nil
+}
+
+// Write is called by the processMessages() loop to send request packets.
+func (c *packetTranslatorConn) Write(b []byte) (n int, err error) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if c.isClosed {
+ return 0, errPacketTranslatorConnClosed
+ }
+
+ // Signal any goroutine waiting to read a request.
+ defer c.requestCond.Broadcast()
+
+ // Writes to the buffer should always succeed.
+ return c.requestBuf.Write(b)
+}
+
+// ReceiveRequest attempts to read a request packet from this connection. It
+// will block until it is able to read a full request packet or until this
+// connection is closed.
+func (c *packetTranslatorConn) ReceiveRequest() (*ber.Packet, error) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ for !c.isClosed {
+ // Attempt to parse a request packet from the request buffer.
+ // If it fails with an unexpected EOF, wait and try again.
+ requestReader := bytes.NewReader(c.requestBuf.Bytes())
+ packet, err := ber.ReadPacket(requestReader)
+ switch err {
+ case io.EOF, io.ErrUnexpectedEOF:
+ c.requestCond.Wait()
+ case nil:
+ // Advance the request buffer by the number of bytes
+ // read to decode the request packet.
+ c.requestBuf.Next(c.requestBuf.Len() - requestReader.Len())
+ return packet, nil
+ default:
+ return nil, err
+ }
+ }
+
+ return nil, errPacketTranslatorConnClosed
+}
+
+// Close closes this connection causing Read() and Write() calls to fail.
+func (c *packetTranslatorConn) Close() error {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ c.isClosed = true
+ c.responseCond.Broadcast()
+ c.requestCond.Broadcast()
+
+ return nil
+}
+
+func (c *packetTranslatorConn) LocalAddr() net.Addr {
+ return (*net.TCPAddr)(nil)
+}
+
+func (c *packetTranslatorConn) RemoteAddr() net.Addr {
+ return (*net.TCPAddr)(nil)
+}
+
+func (c *packetTranslatorConn) SetDeadline(t time.Time) error {
+ return nil
+}
+
+func (c *packetTranslatorConn) SetReadDeadline(t time.Time) error {
+ return nil
+}
+
+func (c *packetTranslatorConn) SetWriteDeadline(t time.Time) error {
+ return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/control.go b/vendor/github.com/go-ldap/ldap/control.go
index 4d8298093..5c62118d4 100644
--- a/vendor/github.com/go-ldap/ldap/control.go
+++ b/vendor/github.com/go-ldap/ldap/control.go
@@ -12,35 +12,48 @@ import (
)
const (
- ControlTypePaging = "1.2.840.113556.1.4.319"
- ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
+ // ControlTypePaging - https://www.ietf.org/rfc/rfc2696.txt
+ ControlTypePaging = "1.2.840.113556.1.4.319"
+ // ControlTypeBeheraPasswordPolicy - https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
+ ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
+ // ControlTypeVChuPasswordMustChange - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
- ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
- ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
+ // ControlTypeVChuPasswordWarning - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
+ ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
+ // ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296
+ ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
)
+// ControlTypeMap maps controls to text descriptions
var ControlTypeMap = map[string]string{
ControlTypePaging: "Paging",
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
ControlTypeManageDsaIT: "Manage DSA IT",
}
+// Control defines an interface controls provide to encode and describe themselves
type Control interface {
+ // GetControlType returns the OID
GetControlType() string
+ // Encode returns the ber packet representation
Encode() *ber.Packet
+ // String returns a human-readable description
String() string
}
+// ControlString implements the Control interface for simple controls
type ControlString struct {
ControlType string
Criticality bool
ControlValue string
}
+// GetControlType returns the OID
func (c *ControlString) GetControlType() string {
return c.ControlType
}
+// Encode returns the ber packet representation
func (c *ControlString) Encode() *ber.Packet {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))
@@ -51,26 +64,32 @@ func (c *ControlString) Encode() *ber.Packet {
return packet
}
+// String returns a human-readable description
func (c *ControlString) String() string {
return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue)
}
+// ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt
type ControlPaging struct {
+ // PagingSize indicates the page size
PagingSize uint32
- Cookie []byte
+ // Cookie is an opaque value returned by the server to track a paging cursor
+ Cookie []byte
}
+// GetControlType returns the OID
func (c *ControlPaging) GetControlType() string {
return ControlTypePaging
}
+// Encode returns the ber packet representation
func (c *ControlPaging) Encode() *ber.Packet {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")"))
p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)")
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value")
- seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(c.PagingSize), "Paging Size"))
+ seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.PagingSize), "Paging Size"))
cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
cookie.Value = c.Cookie
cookie.Data.Write(c.Cookie)
@@ -81,6 +100,7 @@ func (c *ControlPaging) Encode() *ber.Packet {
return packet
}
+// String returns a human-readable description
func (c *ControlPaging) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q",
@@ -91,21 +111,29 @@ func (c *ControlPaging) String() string {
c.Cookie)
}
+// SetCookie stores the given cookie in the paging control
func (c *ControlPaging) SetCookie(cookie []byte) {
c.Cookie = cookie
}
+// ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
type ControlBeheraPasswordPolicy struct {
- Expire int64
- Grace int64
- Error int8
+ // Expire contains the number of seconds before a password will expire
+ Expire int64
+ // Grace indicates the remaining number of times a user will be allowed to authenticate with an expired password
+ Grace int64
+ // Error indicates the error code
+ Error int8
+ // ErrorString is a human readable error
ErrorString string
}
+// GetControlType returns the OID
func (c *ControlBeheraPasswordPolicy) GetControlType() string {
return ControlTypeBeheraPasswordPolicy
}
+// Encode returns the ber packet representation
func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")"))
@@ -113,6 +141,7 @@ func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
return packet
}
+// String returns a human-readable description
func (c *ControlBeheraPasswordPolicy) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t Expire: %d Grace: %d Error: %d, ErrorString: %s",
@@ -125,39 +154,49 @@ func (c *ControlBeheraPasswordPolicy) String() string {
c.ErrorString)
}
+// ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
type ControlVChuPasswordMustChange struct {
+ // MustChange indicates if the password is required to be changed
MustChange bool
}
+// GetControlType returns the OID
func (c *ControlVChuPasswordMustChange) GetControlType() string {
return ControlTypeVChuPasswordMustChange
}
+// Encode returns the ber packet representation
func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
return nil
}
+// String returns a human-readable description
func (c *ControlVChuPasswordMustChange) String() string {
return fmt.Sprintf(
- "Control Type: %s (%q) Criticality: %t MustChange: %b",
+ "Control Type: %s (%q) Criticality: %t MustChange: %v",
ControlTypeMap[ControlTypeVChuPasswordMustChange],
ControlTypeVChuPasswordMustChange,
false,
c.MustChange)
}
+// ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
type ControlVChuPasswordWarning struct {
+ // Expire indicates the time in seconds until the password expires
Expire int64
}
+// GetControlType returns the OID
func (c *ControlVChuPasswordWarning) GetControlType() string {
return ControlTypeVChuPasswordWarning
}
+// Encode returns the ber packet representation
func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
return nil
}
+// String returns a human-readable description
func (c *ControlVChuPasswordWarning) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t Expire: %b",
@@ -167,14 +206,18 @@ func (c *ControlVChuPasswordWarning) String() string {
c.Expire)
}
+// ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296
type ControlManageDsaIT struct {
+ // Criticality indicates if this control is required
Criticality bool
}
+// GetControlType returns the OID
func (c *ControlManageDsaIT) GetControlType() string {
return ControlTypeManageDsaIT
}
+// Encode returns the ber packet representation
func (c *ControlManageDsaIT) Encode() *ber.Packet {
//FIXME
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
@@ -185,6 +228,7 @@ func (c *ControlManageDsaIT) Encode() *ber.Packet {
return packet
}
+// String returns a human-readable description
func (c *ControlManageDsaIT) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t",
@@ -193,10 +237,12 @@ func (c *ControlManageDsaIT) String() string {
c.Criticality)
}
+// NewControlManageDsaIT returns a ControlManageDsaIT control
func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
return &ControlManageDsaIT{Criticality: Criticality}
}
+// FindControl returns the first control of the given type in the list, or nil
func FindControl(controls []Control, controlType string) Control {
for _, c := range controls {
if c.GetControlType() == controlType {
@@ -206,20 +252,56 @@ func FindControl(controls []Control, controlType string) Control {
return nil
}
+// DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
func DecodeControl(packet *ber.Packet) Control {
- ControlType := packet.Children[0].Value.(string)
- Criticality := false
+ var (
+ ControlType = ""
+ Criticality = false
+ value *ber.Packet
+ )
+
+ switch len(packet.Children) {
+ case 0:
+ // at least one child is required for control type
+ return nil
+
+ case 1:
+ // just type, no criticality or value
+ packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
+ ControlType = packet.Children[0].Value.(string)
+
+ case 2:
+ packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
+ ControlType = packet.Children[0].Value.(string)
+
+ // Children[1] could be criticality or value (both are optional)
+ // duck-type on whether this is a boolean
+ if _, ok := packet.Children[1].Value.(bool); ok {
+ packet.Children[1].Description = "Criticality"
+ Criticality = packet.Children[1].Value.(bool)
+ } else {
+ packet.Children[1].Description = "Control Value"
+ value = packet.Children[1]
+ }
+
+ case 3:
+ packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
+ ControlType = packet.Children[0].Value.(string)
- packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
- value := packet.Children[1]
- if len(packet.Children) == 3 {
- value = packet.Children[2]
packet.Children[1].Description = "Criticality"
Criticality = packet.Children[1].Value.(bool)
+
+ packet.Children[2].Description = "Control Value"
+ value = packet.Children[2]
+
+ default:
+ // more than 3 children is invalid
+ return nil
}
- value.Description = "Control Value"
switch ControlType {
+ case ControlTypeManageDsaIT:
+ return NewControlManageDsaIT(Criticality)
case ControlTypePaging:
value.Description += " (Paging)"
c := new(ControlPaging)
@@ -295,14 +377,18 @@ func DecodeControl(packet *ber.Packet) Control {
value.Value = c.Expire
return c
+ default:
+ c := new(ControlString)
+ c.ControlType = ControlType
+ c.Criticality = Criticality
+ if value != nil {
+ c.ControlValue = value.Value.(string)
+ }
+ return c
}
- c := new(ControlString)
- c.ControlType = ControlType
- c.Criticality = Criticality
- c.ControlValue = value.Value.(string)
- return c
}
+// NewControlString returns a generic control
func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
return &ControlString{
ControlType: controlType,
@@ -311,10 +397,12 @@ func NewControlString(controlType string, criticality bool, controlValue string)
}
}
+// NewControlPaging returns a paging control
func NewControlPaging(pagingSize uint32) *ControlPaging {
return &ControlPaging{PagingSize: pagingSize}
}
+// NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy
func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
return &ControlBeheraPasswordPolicy{
Expire: -1,
diff --git a/vendor/github.com/go-ldap/ldap/control_test.go b/vendor/github.com/go-ldap/ldap/control_test.go
new file mode 100644
index 000000000..3fcdab0d7
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/control_test.go
@@ -0,0 +1,58 @@
+package ldap
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "runtime"
+ "testing"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+func TestControlPaging(t *testing.T) {
+ runControlTest(t, NewControlPaging(0))
+ runControlTest(t, NewControlPaging(100))
+}
+
+func TestControlManageDsaIT(t *testing.T) {
+ runControlTest(t, NewControlManageDsaIT(true))
+ runControlTest(t, NewControlManageDsaIT(false))
+}
+
+func TestControlString(t *testing.T) {
+ runControlTest(t, NewControlString("x", true, "y"))
+ runControlTest(t, NewControlString("x", true, ""))
+ runControlTest(t, NewControlString("x", false, "y"))
+ runControlTest(t, NewControlString("x", false, ""))
+}
+
+func runControlTest(t *testing.T, originalControl Control) {
+ header := ""
+ if callerpc, _, line, ok := runtime.Caller(1); ok {
+ if caller := runtime.FuncForPC(callerpc); caller != nil {
+ header = fmt.Sprintf("%s:%d: ", caller.Name(), line)
+ }
+ }
+
+ encodedPacket := originalControl.Encode()
+ encodedBytes := encodedPacket.Bytes()
+
+ // Decode directly from the encoded packet (ensures Value is correct)
+ fromPacket := DecodeControl(encodedPacket)
+ if !bytes.Equal(encodedBytes, fromPacket.Encode().Bytes()) {
+ t.Errorf("%sround-trip from encoded packet failed", header)
+ }
+ if reflect.TypeOf(originalControl) != reflect.TypeOf(fromPacket) {
+ t.Errorf("%sgot different type decoding from encoded packet: %T vs %T", header, fromPacket, originalControl)
+ }
+
+ // Decode from the wire bytes (ensures ber-encoding is correct)
+ fromBytes := DecodeControl(ber.DecodePacket(encodedBytes))
+ if !bytes.Equal(encodedBytes, fromBytes.Encode().Bytes()) {
+ t.Errorf("%sround-trip from encoded bytes failed", header)
+ }
+ if reflect.TypeOf(originalControl) != reflect.TypeOf(fromPacket) {
+ t.Errorf("%sgot different type decoding from encoded bytes: %T vs %T", header, fromBytes, originalControl)
+ }
+}
diff --git a/vendor/github.com/go-ldap/ldap/del.go b/vendor/github.com/go-ldap/ldap/del.go
index 5bb5a25d7..4fd63dc3f 100644
--- a/vendor/github.com/go-ldap/ldap/del.go
+++ b/vendor/github.com/go-ldap/ldap/del.go
@@ -12,8 +12,11 @@ import (
"gopkg.in/asn1-ber.v1"
)
+// DelRequest implements an LDAP deletion request
type DelRequest struct {
- DN string
+ // DN is the name of the directory entry to delete
+ DN string
+ // Controls hold optional controls to send with the request
Controls []Control
}
@@ -23,6 +26,7 @@ func (d DelRequest) encode() *ber.Packet {
return request
}
+// NewDelRequest creates a delete request for the given DN and controls
func NewDelRequest(DN string,
Controls []Control) *DelRequest {
return &DelRequest{
@@ -31,10 +35,10 @@ func NewDelRequest(DN string,
}
}
+// Del executes the given delete request
func (l *Conn) Del(delRequest *DelRequest) error {
- messageID := l.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
packet.AppendChild(delRequest.encode())
if delRequest.Controls != nil {
packet.AppendChild(encodeControls(delRequest.Controls))
@@ -42,22 +46,19 @@ func (l *Conn) Del(delRequest *DelRequest) error {
l.Debug.PrintPacket(packet)
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
- if channel == nil {
- return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
- l.Debug.Printf("%d: waiting for response", messageID)
- packetResponse, ok := <-channel
+ l.Debug.Printf("%d: waiting for response", msgCtx.id)
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
@@ -78,6 +79,6 @@ func (l *Conn) Del(delRequest *DelRequest) error {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
- l.Debug.Printf("%d: returning", messageID)
+ l.Debug.Printf("%d: returning", msgCtx.id)
return nil
}
diff --git a/vendor/github.com/go-ldap/ldap/dn.go b/vendor/github.com/go-ldap/ldap/dn.go
index 5d83c5e9a..cc70c894c 100644
--- a/vendor/github.com/go-ldap/ldap/dn.go
+++ b/vendor/github.com/go-ldap/ldap/dn.go
@@ -55,19 +55,25 @@ import (
ber "gopkg.in/asn1-ber.v1"
)
+// AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514
type AttributeTypeAndValue struct {
- Type string
+ // Type is the attribute type
+ Type string
+ // Value is the attribute value
Value string
}
+// RelativeDN represents a relativeDistinguishedName from https://tools.ietf.org/html/rfc4514
type RelativeDN struct {
Attributes []*AttributeTypeAndValue
}
+// DN represents a distinguishedName from https://tools.ietf.org/html/rfc4514
type DN struct {
RDNs []*RelativeDN
}
+// ParseDN returns a distinguishedName or an error
func ParseDN(str string) (*DN, error) {
dn := new(DN)
dn.RDNs = make([]*RelativeDN, 0)
@@ -94,11 +100,9 @@ func ParseDN(str string) (*DN, error) {
dst := []byte{0}
n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2]))
if err != nil {
- return nil, errors.New(
- fmt.Sprintf("Failed to decode escaped character: %s", err))
+ return nil, fmt.Errorf("Failed to decode escaped character: %s", err)
} else if n != 1 {
- return nil, errors.New(
- fmt.Sprintf("Expected 1 byte when un-escaping, got %d", n))
+ return nil, fmt.Errorf("Expected 1 byte when un-escaping, got %d", n)
}
buffer.WriteByte(dst[0])
i++
@@ -119,12 +123,11 @@ func ParseDN(str string) (*DN, error) {
} else {
data = str[i:]
}
- raw_ber, err := enchex.DecodeString(data)
+ rawBER, err := enchex.DecodeString(data)
if err != nil {
- return nil, errors.New(
- fmt.Sprintf("Failed to decode BER encoding: %s", err))
+ return nil, fmt.Errorf("Failed to decode BER encoding: %s", err)
}
- packet := ber.DecodePacket(raw_ber)
+ packet := ber.DecodePacket(rawBER)
buffer.WriteString(packet.Data.String())
i += len(data) - 1
}
diff --git a/vendor/github.com/go-ldap/ldap/error.go b/vendor/github.com/go-ldap/ldap/error.go
index 97404eb65..ff697873d 100644
--- a/vendor/github.com/go-ldap/ldap/error.go
+++ b/vendor/github.com/go-ldap/ldap/error.go
@@ -56,6 +56,7 @@ const (
ErrorUnexpectedResponse = 205
)
+// LDAPResultCodeMap contains string descriptions for LDAP error codes
var LDAPResultCodeMap = map[uint8]string{
LDAPResultSuccess: "Success",
LDAPResultOperationsError: "Operations Error",
@@ -115,8 +116,11 @@ func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
return ErrorNetwork, "Invalid packet format"
}
+// Error holds LDAP error information
type Error struct {
- Err error
+ // Err is the underlying error
+ Err error
+ // ResultCode is the LDAP error code
ResultCode uint8
}
@@ -124,10 +128,12 @@ func (e *Error) Error() string {
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
}
+// NewError creates an LDAP error with the given code and underlying error
func NewError(resultCode uint8, err error) error {
return &Error{ResultCode: resultCode, Err: err}
}
+// IsErrorWithCode returns true if the given error is an LDAP error with the given result code
func IsErrorWithCode(err error, desiredResultCode uint8) bool {
if err == nil {
return false
diff --git a/vendor/github.com/go-ldap/ldap/error_test.go b/vendor/github.com/go-ldap/ldap/error_test.go
index 4ec720d9f..c010ebe3e 100644
--- a/vendor/github.com/go-ldap/ldap/error_test.go
+++ b/vendor/github.com/go-ldap/ldap/error_test.go
@@ -1,7 +1,11 @@
package ldap
import (
+ "errors"
+ "net"
+ "strings"
"testing"
+ "time"
"gopkg.in/asn1-ber.v1"
)
@@ -16,8 +20,8 @@ func TestNilPacket(t *testing.T) {
// Test for nil result
kids := []*ber.Packet{
- &ber.Packet{}, // Unused
- nil, // Can't be nil
+ {}, // Unused
+ nil, // Can't be nil
}
pack := &ber.Packet{Children: kids}
code, _ = getLDAPResultCode(pack)
@@ -25,5 +29,74 @@ func TestNilPacket(t *testing.T) {
if code != ErrorUnexpectedResponse {
t.Errorf("Should have an 'ErrorUnexpectedResponse' error in nil packets, got: %v", code)
}
+}
+
+// TestConnReadErr tests that an unexpected error reading from underlying
+// connection bubbles up to the goroutine which makes a request.
+func TestConnReadErr(t *testing.T) {
+ conn := &signalErrConn{
+ signals: make(chan error),
+ }
+
+ ldapConn := NewConn(conn, false)
+ ldapConn.Start()
+
+ // Make a dummy search request.
+ searchReq := NewSearchRequest("dc=example,dc=com", ScopeWholeSubtree, DerefAlways, 0, 0, false, "(objectClass=*)", nil, nil)
+
+ expectedError := errors.New("this is the error you are looking for")
+
+ // Send the signal after a short amount of time.
+ time.AfterFunc(10*time.Millisecond, func() { conn.signals <- expectedError })
+
+ // This should block until the underlyiny conn gets the error signal
+ // which should bubble up through the reader() goroutine, close the
+ // connection, and
+ _, err := ldapConn.Search(searchReq)
+ if err == nil || !strings.Contains(err.Error(), expectedError.Error()) {
+ t.Errorf("not the expected error: %s", err)
+ }
+}
+
+// signalErrConn is a helful type used with TestConnReadErr. It implements the
+// net.Conn interface to be used as a connection for the test. Most methods are
+// no-ops but the Read() method blocks until it receives a signal which it
+// returns as an error.
+type signalErrConn struct {
+ signals chan error
+}
+
+// Read blocks until an error is sent on the internal signals channel. That
+// error is returned.
+func (c *signalErrConn) Read(b []byte) (n int, err error) {
+ return 0, <-c.signals
+}
+
+func (c *signalErrConn) Write(b []byte) (n int, err error) {
+ return len(b), nil
+}
+
+func (c *signalErrConn) Close() error {
+ close(c.signals)
+ return nil
+}
+
+func (c *signalErrConn) LocalAddr() net.Addr {
+ return (*net.TCPAddr)(nil)
+}
+
+func (c *signalErrConn) RemoteAddr() net.Addr {
+ return (*net.TCPAddr)(nil)
+}
+
+func (c *signalErrConn) SetDeadline(t time.Time) error {
+ return nil
+}
+
+func (c *signalErrConn) SetReadDeadline(t time.Time) error {
+ return nil
+}
+func (c *signalErrConn) SetWriteDeadline(t time.Time) error {
+ return nil
}
diff --git a/vendor/github.com/go-ldap/ldap/filter.go b/vendor/github.com/go-ldap/ldap/filter.go
index 63bcec1e3..7eae310f1 100644
--- a/vendor/github.com/go-ldap/ldap/filter.go
+++ b/vendor/github.com/go-ldap/ldap/filter.go
@@ -15,6 +15,7 @@ import (
"gopkg.in/asn1-ber.v1"
)
+// Filter choices
const (
FilterAnd = 0
FilterOr = 1
@@ -28,6 +29,7 @@ const (
FilterExtensibleMatch = 9
)
+// FilterMap contains human readable descriptions of Filter choices
var FilterMap = map[uint64]string{
FilterAnd: "And",
FilterOr: "Or",
@@ -41,18 +43,21 @@ var FilterMap = map[uint64]string{
FilterExtensibleMatch: "Extensible Match",
}
+// SubstringFilter options
const (
FilterSubstringsInitial = 0
FilterSubstringsAny = 1
FilterSubstringsFinal = 2
)
+// FilterSubstringsMap contains human readable descriptions of SubstringFilter choices
var FilterSubstringsMap = map[uint64]string{
FilterSubstringsInitial: "Substrings Initial",
FilterSubstringsAny: "Substrings Any",
FilterSubstringsFinal: "Substrings Final",
}
+// MatchingRuleAssertion choices
const (
MatchingRuleAssertionMatchingRule = 1
MatchingRuleAssertionType = 2
@@ -60,6 +65,7 @@ const (
MatchingRuleAssertionDNAttributes = 4
)
+// MatchingRuleAssertionMap contains human readable descriptions of MatchingRuleAssertion choices
var MatchingRuleAssertionMap = map[uint64]string{
MatchingRuleAssertionMatchingRule: "Matching Rule Assertion Matching Rule",
MatchingRuleAssertionType: "Matching Rule Assertion Type",
@@ -67,6 +73,7 @@ var MatchingRuleAssertionMap = map[uint64]string{
MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes",
}
+// CompileFilter converts a string representation of a filter into a BER-encoded packet
func CompileFilter(filter string) (*ber.Packet, error) {
if len(filter) == 0 || filter[0] != '(' {
return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('"))
@@ -81,6 +88,7 @@ func CompileFilter(filter string) (*ber.Packet, error) {
return packet, nil
}
+// DecompileFilter converts a packet representation of a filter into a string representation
func DecompileFilter(packet *ber.Packet) (ret string, err error) {
defer func() {
if r := recover(); r != nil {
@@ -239,11 +247,13 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
packet.AppendChild(child)
return packet, newPos, err
default:
- READING_ATTR := 0
- READING_EXTENSIBLE_MATCHING_RULE := 1
- READING_CONDITION := 2
+ const (
+ stateReadingAttr = 0
+ stateReadingExtensibleMatchingRule = 1
+ stateReadingCondition = 2
+ )
- state := READING_ATTR
+ state := stateReadingAttr
attribute := ""
extensibleDNAttributes := false
@@ -261,56 +271,56 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
}
switch state {
- case READING_ATTR:
+ case stateReadingAttr:
switch {
// Extensible rule, with only DN-matching
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
extensibleDNAttributes = true
- state = READING_CONDITION
+ state = stateReadingCondition
newPos += 5
// Extensible rule, with DN-matching and a matching OID
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:"):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
extensibleDNAttributes = true
- state = READING_EXTENSIBLE_MATCHING_RULE
+ state = stateReadingExtensibleMatchingRule
newPos += 4
// Extensible rule, with attr only
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
- state = READING_CONDITION
+ state = stateReadingCondition
newPos += 2
// Extensible rule, with no DN attribute matching
case currentRune == ':':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
- state = READING_EXTENSIBLE_MATCHING_RULE
- newPos += 1
+ state = stateReadingExtensibleMatchingRule
+ newPos++
// Equality condition
case currentRune == '=':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
- state = READING_CONDITION
- newPos += 1
+ state = stateReadingCondition
+ newPos++
// Greater-than or equal
case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
- state = READING_CONDITION
+ state = stateReadingCondition
newPos += 2
// Less-than or equal
case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
- state = READING_CONDITION
+ state = stateReadingCondition
newPos += 2
// Approx
case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch])
- state = READING_CONDITION
+ state = stateReadingCondition
newPos += 2
// Still reading the attribute name
@@ -319,12 +329,12 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
newPos += currentWidth
}
- case READING_EXTENSIBLE_MATCHING_RULE:
+ case stateReadingExtensibleMatchingRule:
switch {
// Matching rule OID is done
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
- state = READING_CONDITION
+ state = stateReadingCondition
newPos += 2
// Still reading the matching rule oid
@@ -333,7 +343,7 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
newPos += currentWidth
}
- case READING_CONDITION:
+ case stateReadingCondition:
// append to the condition
condition += fmt.Sprintf("%c", currentRune)
newPos += currentWidth
@@ -369,9 +379,9 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
}
// Add the value (only required child)
- encodedString, err := escapedStringToEncodedBytes(condition)
- if err != nil {
- return packet, newPos, err
+ encodedString, encodeErr := escapedStringToEncodedBytes(condition)
+ if encodeErr != nil {
+ return packet, newPos, encodeErr
}
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue]))
@@ -401,17 +411,17 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
default:
tag = FilterSubstringsAny
}
- encodedString, err := escapedStringToEncodedBytes(part)
- if err != nil {
- return packet, newPos, err
+ encodedString, encodeErr := escapedStringToEncodedBytes(part)
+ if encodeErr != nil {
+ return packet, newPos, encodeErr
}
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)]))
}
packet.AppendChild(seq)
default:
- encodedString, err := escapedStringToEncodedBytes(condition)
- if err != nil {
- return packet, newPos, err
+ encodedString, encodeErr := escapedStringToEncodedBytes(condition)
+ if encodeErr != nil {
+ return packet, newPos, encodeErr
}
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition"))
@@ -440,12 +450,12 @@ func escapedStringToEncodedBytes(escapedString string) (string, error) {
if i+2 > len(escapedString) {
return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter"))
}
- if escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]); decodeErr != nil {
+ escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3])
+ if decodeErr != nil {
return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter"))
- } else {
- buffer.WriteByte(escByte[0])
- i += 2 // +1 from end of loop, so 3 total for \xx.
}
+ buffer.WriteByte(escByte[0])
+ i += 2 // +1 from end of loop, so 3 total for \xx.
} else {
buffer.WriteRune(currentRune)
}
diff --git a/vendor/github.com/go-ldap/ldap/ldap.go b/vendor/github.com/go-ldap/ldap/ldap.go
index 1620aaea6..90018be83 100644
--- a/vendor/github.com/go-ldap/ldap/ldap.go
+++ b/vendor/github.com/go-ldap/ldap/ldap.go
@@ -36,6 +36,7 @@ const (
ApplicationExtendedResponse = 24
)
+// ApplicationMap contains human readable descriptions of LDAP Application Codes
var ApplicationMap = map[uint8]string{
ApplicationBindRequest: "Bind Request",
ApplicationBindResponse: "Bind Response",
@@ -72,6 +73,7 @@ const (
BeheraPasswordInHistory = 8
)
+// BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes
var BeheraPasswordPolicyErrorMap = map[int8]string{
BeheraPasswordExpired: "Password expired",
BeheraAccountLocked: "Account locked",
@@ -237,6 +239,7 @@ func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
}
}
+// DebugBinaryFile reads and prints packets from the given filename
func DebugBinaryFile(fileName string) error {
file, err := ioutil.ReadFile(fileName)
if err != nil {
diff --git a/vendor/github.com/go-ldap/ldap/modify.go b/vendor/github.com/go-ldap/ldap/modify.go
index 5c042af79..e4ab6cefc 100644
--- a/vendor/github.com/go-ldap/ldap/modify.go
+++ b/vendor/github.com/go-ldap/ldap/modify.go
@@ -36,64 +36,76 @@ import (
"gopkg.in/asn1-ber.v1"
)
+// Change operation choices
const (
AddAttribute = 0
DeleteAttribute = 1
ReplaceAttribute = 2
)
+// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
type PartialAttribute struct {
- attrType string
- attrVals []string
+ // Type is the type of the partial attribute
+ Type string
+ // Vals are the values of the partial attribute
+ Vals []string
}
func (p *PartialAttribute) encode() *ber.Packet {
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
- seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.attrType, "Type"))
+ seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type"))
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
- for _, value := range p.attrVals {
+ for _, value := range p.Vals {
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
}
seq.AppendChild(set)
return seq
}
+// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
type ModifyRequest struct {
- dn string
- addAttributes []PartialAttribute
- deleteAttributes []PartialAttribute
- replaceAttributes []PartialAttribute
+ // DN is the distinguishedName of the directory entry to modify
+ DN string
+ // AddAttributes contain the attributes to add
+ AddAttributes []PartialAttribute
+ // DeleteAttributes contain the attributes to delete
+ DeleteAttributes []PartialAttribute
+ // ReplaceAttributes contain the attributes to replace
+ ReplaceAttributes []PartialAttribute
}
+// Add inserts the given attribute to the list of attributes to add
func (m *ModifyRequest) Add(attrType string, attrVals []string) {
- m.addAttributes = append(m.addAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
+ m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
}
+// Delete inserts the given attribute to the list of attributes to delete
func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
- m.deleteAttributes = append(m.deleteAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
+ m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
}
+// Replace inserts the given attribute to the list of attributes to replace
func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
- m.replaceAttributes = append(m.replaceAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
+ m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
}
func (m ModifyRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
- request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.dn, "DN"))
+ request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN"))
changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
- for _, attribute := range m.addAttributes {
+ for _, attribute := range m.AddAttributes {
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation"))
change.AppendChild(attribute.encode())
changes.AppendChild(change)
}
- for _, attribute := range m.deleteAttributes {
+ for _, attribute := range m.DeleteAttributes {
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation"))
change.AppendChild(attribute.encode())
changes.AppendChild(change)
}
- for _, attribute := range m.replaceAttributes {
+ for _, attribute := range m.ReplaceAttributes {
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation"))
change.AppendChild(attribute.encode())
@@ -103,38 +115,36 @@ func (m ModifyRequest) encode() *ber.Packet {
return request
}
+// NewModifyRequest creates a modify request for the given DN
func NewModifyRequest(
dn string,
) *ModifyRequest {
return &ModifyRequest{
- dn: dn,
+ DN: dn,
}
}
+// Modify performs the ModifyRequest
func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
- messageID := l.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
packet.AppendChild(modifyRequest.encode())
l.Debug.PrintPacket(packet)
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
- if channel == nil {
- return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
- l.Debug.Printf("%d: waiting for response", messageID)
- packetResponse, ok := <-channel
+ l.Debug.Printf("%d: waiting for response", msgCtx.id)
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
@@ -155,6 +165,6 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
- l.Debug.Printf("%d: returning", messageID)
+ l.Debug.Printf("%d: returning", msgCtx.id)
return nil
}
diff --git a/vendor/github.com/go-ldap/ldap/passwdmodify.go b/vendor/github.com/go-ldap/ldap/passwdmodify.go
index 6d5ca975a..26110ccf4 100644
--- a/vendor/github.com/go-ldap/ldap/passwdmodify.go
+++ b/vendor/github.com/go-ldap/ldap/passwdmodify.go
@@ -16,13 +16,21 @@ const (
passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1"
)
+// PasswordModifyRequest implements the Password Modify Extended Operation as defined in https://www.ietf.org/rfc/rfc3062.txt
type PasswordModifyRequest struct {
+ // UserIdentity is an optional string representation of the user associated with the request.
+ // This string may or may not be an LDAPDN [RFC2253].
+ // If no UserIdentity field is present, the request acts up upon the password of the user currently associated with the LDAP session
UserIdentity string
- OldPassword string
- NewPassword string
+ // OldPassword, if present, contains the user's current password
+ OldPassword string
+ // NewPassword, if present, contains the desired password for this user
+ NewPassword string
}
+// PasswordModifyResult holds the server response to a PasswordModifyRequest
type PasswordModifyResult struct {
+ // GeneratedPassword holds a password generated by the server, if present
GeneratedPassword string
}
@@ -47,7 +55,7 @@ func (r *PasswordModifyRequest) encode() (*ber.Packet, error) {
return request, nil
}
-// Create a new PasswordModifyRequest
+// NewPasswordModifyRequest creates a new PasswordModifyRequest
//
// According to the RFC 3602:
// userIdentity is a string representing the user associated with the request.
@@ -72,11 +80,10 @@ func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPasswo
}
}
+// PasswordModify performs the modification request
func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) {
- messageID := l.nextMessageID()
-
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
encodedPasswordModifyRequest, err := passwordModifyRequest.encode()
if err != nil {
@@ -86,24 +93,21 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa
l.Debug.PrintPacket(packet)
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return nil, err
}
- if channel == nil {
- return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
result := &PasswordModifyResult{}
- l.Debug.Printf("%d: waiting for response", messageID)
- packetResponse, ok := <-channel
+ l.Debug.Printf("%d: waiting for response", msgCtx.id)
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/go-ldap/ldap/search.go b/vendor/github.com/go-ldap/ldap/search.go
index 7e9495bdc..2a99894c9 100644
--- a/vendor/github.com/go-ldap/ldap/search.go
+++ b/vendor/github.com/go-ldap/ldap/search.go
@@ -68,18 +68,21 @@ import (
"gopkg.in/asn1-ber.v1"
)
+// scope choices
const (
ScopeBaseObject = 0
ScopeSingleLevel = 1
ScopeWholeSubtree = 2
)
+// ScopeMap contains human readable descriptions of scope choices
var ScopeMap = map[int]string{
ScopeBaseObject: "Base Object",
ScopeSingleLevel: "Single Level",
ScopeWholeSubtree: "Whole Subtree",
}
+// derefAliases
const (
NeverDerefAliases = 0
DerefInSearching = 1
@@ -87,6 +90,7 @@ const (
DerefAlways = 3
)
+// DerefMap contains human readable descriptions of derefAliases choices
var DerefMap = map[int]string{
NeverDerefAliases: "NeverDerefAliases",
DerefInSearching: "DerefInSearching",
@@ -114,11 +118,15 @@ func NewEntry(dn string, attributes map[string][]string) *Entry {
}
}
+// Entry represents a single search result entry
type Entry struct {
- DN string
+ // DN is the distinguished name of the entry
+ DN string
+ // Attributes are the returned attributes for the entry
Attributes []*EntryAttribute
}
+// GetAttributeValues returns the values for the named attribute, or an empty list
func (e *Entry) GetAttributeValues(attribute string) []string {
for _, attr := range e.Attributes {
if attr.Name == attribute {
@@ -128,6 +136,7 @@ func (e *Entry) GetAttributeValues(attribute string) []string {
return []string{}
}
+// GetRawAttributeValues returns the byte values for the named attribute, or an empty list
func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
for _, attr := range e.Attributes {
if attr.Name == attribute {
@@ -137,6 +146,7 @@ func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
return [][]byte{}
}
+// GetAttributeValue returns the first value for the named attribute, or ""
func (e *Entry) GetAttributeValue(attribute string) string {
values := e.GetAttributeValues(attribute)
if len(values) == 0 {
@@ -145,6 +155,7 @@ func (e *Entry) GetAttributeValue(attribute string) string {
return values[0]
}
+// GetRawAttributeValue returns the first value for the named attribute, or an empty slice
func (e *Entry) GetRawAttributeValue(attribute string) []byte {
values := e.GetRawAttributeValues(attribute)
if len(values) == 0 {
@@ -153,6 +164,7 @@ func (e *Entry) GetRawAttributeValue(attribute string) []byte {
return values[0]
}
+// Print outputs a human-readable description
func (e *Entry) Print() {
fmt.Printf("DN: %s\n", e.DN)
for _, attr := range e.Attributes {
@@ -160,6 +172,7 @@ func (e *Entry) Print() {
}
}
+// PrettyPrint outputs a human-readable description indenting
func (e *Entry) PrettyPrint(indent int) {
fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN)
for _, attr := range e.Attributes {
@@ -180,38 +193,51 @@ func NewEntryAttribute(name string, values []string) *EntryAttribute {
}
}
+// EntryAttribute holds a single attribute
type EntryAttribute struct {
- Name string
- Values []string
+ // Name is the name of the attribute
+ Name string
+ // Values contain the string values of the attribute
+ Values []string
+ // ByteValues contain the raw values of the attribute
ByteValues [][]byte
}
+// Print outputs a human-readable description
func (e *EntryAttribute) Print() {
fmt.Printf("%s: %s\n", e.Name, e.Values)
}
+// PrettyPrint outputs a human-readable description with indenting
func (e *EntryAttribute) PrettyPrint(indent int) {
fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
}
+// SearchResult holds the server's response to a search request
type SearchResult struct {
- Entries []*Entry
+ // Entries are the returned entries
+ Entries []*Entry
+ // Referrals are the returned referrals
Referrals []string
- Controls []Control
+ // Controls are the returned controls
+ Controls []Control
}
+// Print outputs a human-readable description
func (s *SearchResult) Print() {
for _, entry := range s.Entries {
entry.Print()
}
}
+// PrettyPrint outputs a human-readable description with indenting
func (s *SearchResult) PrettyPrint(indent int) {
for _, entry := range s.Entries {
entry.PrettyPrint(indent)
}
}
+// SearchRequest represents a search request to send to the server
type SearchRequest struct {
BaseDN string
Scope int
@@ -247,6 +273,7 @@ func (s *SearchRequest) encode() (*ber.Packet, error) {
return request, nil
}
+// NewSearchRequest creates a new search request
func NewSearchRequest(
BaseDN string,
Scope, DerefAliases, SizeLimit, TimeLimit int,
@@ -341,10 +368,10 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32)
return searchResult, nil
}
+// Search performs the given search request
func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
- messageID := l.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
- packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
// encode search request
encodedSearchRequest, err := searchRequest.encode()
if err != nil {
@@ -358,14 +385,11 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
l.Debug.PrintPacket(packet)
- channel, err := l.sendMessage(packet)
+ msgCtx, err := l.sendMessage(packet)
if err != nil {
return nil, err
}
- if channel == nil {
- return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
- }
- defer l.finishMessage(messageID)
+ defer l.finishMessage(msgCtx)
result := &SearchResult{
Entries: make([]*Entry, 0),
@@ -374,13 +398,13 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
foundSearchResultDone := false
for !foundSearchResultDone {
- l.Debug.Printf("%d: waiting for response", messageID)
- packetResponse, ok := <-channel
+ l.Debug.Printf("%d: waiting for response", msgCtx.id)
+ packetResponse, ok := <-msgCtx.responses
if !ok {
- return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
- l.Debug.Printf("%d: got response %p", messageID, packet)
+ l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return nil, err
}
@@ -421,6 +445,6 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
}
}
- l.Debug.Printf("%d: returning", messageID)
+ l.Debug.Printf("%d: returning", msgCtx.id)
return result, nil
}
diff --git a/vendor/github.com/go-sql-driver/mysql/.travis.yml b/vendor/github.com/go-sql-driver/mysql/.travis.yml
index 211969dfa..cc97c31e7 100644
--- a/vendor/github.com/go-sql-driver/mysql/.travis.yml
+++ b/vendor/github.com/go-sql-driver/mysql/.travis.yml
@@ -1,11 +1,6 @@
-sudo: false
language: go
go:
- - 1.2
- - 1.3
- - 1.4
- - 1.5
- - 1.6
+ - 1.1
- tip
before_script:
diff --git a/vendor/github.com/go-sql-driver/mysql/AUTHORS b/vendor/github.com/go-sql-driver/mysql/AUTHORS
index 3774919d7..f3c62e178 100644
--- a/vendor/github.com/go-sql-driver/mysql/AUTHORS
+++ b/vendor/github.com/go-sql-driver/mysql/AUTHORS
@@ -14,34 +14,18 @@
Aaron Hopkins <go-sql-driver at die.net>
Arne Hormann <arnehormann at gmail.com>
Carlos Nieto <jose.carlos at menteslibres.net>
-Chris Moos <chris at tech9computers.com>
-Daniel Nichter <nil at codenode.com>
-Daniël van Eeden <git at myname.nl>
DisposaBoy <disposaboy at dby.me>
Frederick Mayle <frederickmayle at gmail.com>
Gustavo Kristic <gkristic at gmail.com>
Hanno Braun <mail at hannobraun.com>
-Henri Yandell <flamefew at gmail.com>
-Hirotaka Yamamoto <ymmt2005 at gmail.com>
-INADA Naoki <songofacandy at gmail.com>
James Harr <james.harr at gmail.com>
Jian Zhen <zhenjl at gmail.com>
-Joshua Prunier <joshua.prunier at gmail.com>
-Julien Lefevre <julien.lefevr at gmail.com>
Julien Schmidt <go-sql-driver at julienschmidt.com>
-Kamil Dziedzic <kamil at klecza.pl>
-Kevin Malachowski <kevin at chowski.com>
Leonardo YongUk Kim <dalinaum at gmail.com>
-Luca Looz <luca.looz92 at gmail.com>
Lucas Liu <extrafliu at gmail.com>
Luke Scott <luke at webconnex.com>
Michael Woolnough <michael.woolnough at gmail.com>
Nicola Peduzzi <thenikso at gmail.com>
-Paul Bonser <misterpib at gmail.com>
-Runrioter Wung <runrioter at gmail.com>
-Soroush Pour <me at soroushjp.com>
-Stan Putrya <root.vagner at gmail.com>
-Stanley Gunawan <gunawan.stanley at gmail.com>
Xiaobing Jiang <s7v7nislands at gmail.com>
Xiuming Chen <cc at cxm.cc>
@@ -49,4 +33,3 @@ Xiuming Chen <cc at cxm.cc>
Barracuda Networks, Inc.
Google Inc.
-Stripe Inc.
diff --git a/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
index 381d91825..feb53a1e8 100644
--- a/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
+++ b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
@@ -1,34 +1,3 @@
-## HEAD
-
-Changes:
-
- - Go 1.1 is no longer supported
- - Use decimals field from MySQL to format time types (#249)
- - Buffer optimizations (#269)
- - TLS ServerName defaults to the host (#283)
-
-Bugfixes:
-
- - Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249)
- - Fixed handling of queries without columns and rows (#255)
- - Fixed a panic when SetKeepAlive() failed (#298)
- - Support receiving ERR packet while reading rows (#321)
- - Fixed reading NULL length-encoded integers in MySQL 5.6+ (#349)
- - Fixed absolute paths support in LOAD LOCAL DATA INFILE (#356)
- - Actually zero out bytes in handshake response (#378)
- - Fixed race condition in registering LOAD DATA INFILE handler (#383)
- - Fixed tests with MySQL 5.7.9+ (#380)
- - QueryUnescape TLS config names (#397)
- - Fixed "broken pipe" error by writing to closed socket (#390)
-
-New Features:
- - Support for returning table alias on Columns() (#289, #359, #382)
- - Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318)
- - Support for uint64 parameters with high bit set (#332, #345)
- - Cleartext authentication plugin support (#327)
-
-
-
## Version 1.2 (2014-06-03)
Changes:
diff --git a/vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md b/vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md
index 8fe16bcb4..f87c19824 100644
--- a/vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md
+++ b/vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md
@@ -4,11 +4,28 @@
Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed).
+Please provide the following minimum information:
+* Your Go-MySQL-Driver version (or git SHA)
+* Your Go version (run `go version` in your console)
+* A detailed issue description
+* Error Log if present
+* If possible, a short example
+
+
## Contributing Code
By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file.
Don't forget to add yourself to the AUTHORS file.
+### Pull Requests Checklist
+
+Please check the following points before submitting your pull request:
+- [x] Code compiles correctly
+- [x] Created tests, if possible
+- [x] All tests pass
+- [x] Extended the README / documentation, if necessary
+- [x] Added yourself to the AUTHORS file
+
### Code Review
Everyone is invited to review and comment on pull requests.
diff --git a/vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md b/vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md
deleted file mode 100644
index d9771f1dd..000000000
--- a/vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,21 +0,0 @@
-### Issue description
-Tell us what should happen and what happens instead
-
-### Example code
-```go
-If possible, please enter some example code here to reproduce the issue.
-```
-
-### Error log
-```
-If you have an error log, please paste it here.
-```
-
-### Configuration
-*Driver version (or git SHA):*
-
-*Go version:* run `go version` in your console
-
-*Server version:* E.g. MySQL 5.6, MariaDB 10.0.20
-
-*Server OS:* E.g. Debian 8.1 (Jessie), Windows 10
diff --git a/vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index 6f5c7ebeb..000000000
--- a/vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,9 +0,0 @@
-### Description
-Please explain the changes you made here.
-
-### Checklist
-- [ ] Code compiles correctly
-- [ ] Created tests which fail without the change (if possible)
-- [ ] All tests passing
-- [ ] Extended the README / documentation, if necessary
-- [ ] Added myself / the copyright holder to the AUTHORS file
diff --git a/vendor/github.com/go-sql-driver/mysql/README.md b/vendor/github.com/go-sql-driver/mysql/README.md
index c64aae264..6b3475b37 100644
--- a/vendor/github.com/go-sql-driver/mysql/README.md
+++ b/vendor/github.com/go-sql-driver/mysql/README.md
@@ -30,7 +30,7 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
## Features
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
* Native Go implementation. No C-bindings, just pure Go
- * Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](http://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
+ * Connections over TCP/IPv4, TCP/IPv6 or Unix domain sockets
* Automatic handling of broken connections
* Automatic Connection Pooling *(by database/sql package)*
* Supports queries larger than 16MB
@@ -38,10 +38,9 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
* Intelligent `LONG DATA` handling in prepared statements
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
* Optional `time.Time` parsing
- * Optional placeholder interpolation
## Requirements
- * Go 1.2 or higher
+ * Go 1.1 or higher
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
---------------------------------------
@@ -93,8 +92,6 @@ This has the same effect as an empty DSN string:
```
-Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mysql#Config.FormatDSN) can be used to create a DSN string by filling a struct.
-
#### Password
Passwords can consist of any character. Escaping is **not** necessary.
@@ -125,16 +122,6 @@ Default: false
`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files.
[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
-##### `allowCleartextPasswords`
-
-```
-Type: bool
-Valid Values: true, false
-Default: false
-```
-
-`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
-
##### `allowOldPasswords`
```
@@ -179,33 +166,6 @@ Default: false
`clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed.
-##### `columnsWithAlias`
-
-```
-Type: bool
-Valid Values: true, false
-Default: false
-```
-
-When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example:
-
-```
-SELECT u.id FROM users as u
-```
-
-will return `u.id` instead of just `id` if `columnsWithAlias=true`.
-
-##### `interpolateParams`
-
-```
-Type: bool
-Valid Values: true, false
-Default: false
-```
-
-If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
-
-*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
##### `loc`
@@ -217,22 +177,8 @@ Default: UTC
Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details.
-Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter.
-
Please keep in mind, that param values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
-##### `multiStatements`
-
-```
-Type: bool
-Valid Values: true, false
-Default: false
-```
-
-Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded.
-
-When `multiStatements` is used, `?` parameters must only be used in the first statement.
-
##### `parseTime`
@@ -245,16 +191,6 @@ Default: false
`parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string`
-##### `readTimeout`
-
-```
-Type: decimal number
-Default: 0
-```
-
-I/O read timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
-
-
##### `strict`
```
@@ -275,7 +211,7 @@ Type: decimal number
Default: OS default
```
-*Driver* side connection timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout).
+*Driver* side connection timeout. The value must be a string of decimal numbers, each with optional fraction and a unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout).
##### `tls`
@@ -289,21 +225,11 @@ Default: false
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
-##### `writeTimeout`
-
-```
-Type: decimal number
-Default: 0
-```
-
-I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
-
-
##### System Variables
All other parameters are interpreted as system variables:
* `autocommit`: `"SET autocommit=<value>"`
- * [`time_zone`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `"SET time_zone=<value>"`
+ * `time_zone`: `"SET time_zone=<value>"`
* [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"`
* `param`: `"SET <param>=<value>"`
@@ -365,7 +291,7 @@ import "github.com/go-sql-driver/mysql"
Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
-To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
+To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then.
See the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
@@ -408,9 +334,9 @@ Mozilla summarizes the license scope as follows:
That means:
- * You can **use** the **unchanged** source code both in private and commercially
- * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0)
- * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**
+ * You can **use** the **unchanged** source code both in private as also commercial
+ * You **needn't publish** the source code of your library as long the files licensed under the MPL 2.0 are **unchanged**
+ * You **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0)
Please read the [MPL 2.0 FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license.
diff --git a/vendor/github.com/go-sql-driver/mysql/benchmark_test.go b/vendor/github.com/go-sql-driver/mysql/benchmark_test.go
index 8f721139b..d72a4183f 100644
--- a/vendor/github.com/go-sql-driver/mysql/benchmark_test.go
+++ b/vendor/github.com/go-sql-driver/mysql/benchmark_test.go
@@ -11,13 +11,10 @@ package mysql
import (
"bytes"
"database/sql"
- "database/sql/driver"
- "math"
"strings"
"sync"
"sync/atomic"
"testing"
- "time"
)
type TB testing.B
@@ -48,11 +45,7 @@ func initDB(b *testing.B, queries ...string) *sql.DB {
db := tb.checkDB(sql.Open("mysql", dsn))
for _, query := range queries {
if _, err := db.Exec(query); err != nil {
- if w, ok := err.(MySQLWarnings); ok {
- b.Logf("warning on %q: %v", query, w)
- } else {
- b.Fatalf("error on %q: %v", query, err)
- }
+ b.Fatalf("Error on %q: %v", query, err)
}
}
return db
@@ -213,34 +206,3 @@ func BenchmarkRoundtripBin(b *testing.B) {
rows.Close()
}
}
-
-func BenchmarkInterpolation(b *testing.B) {
- mc := &mysqlConn{
- cfg: &Config{
- InterpolateParams: true,
- Loc: time.UTC,
- },
- maxPacketAllowed: maxPacketSize,
- maxWriteSize: maxPacketSize - 1,
- buf: newBuffer(nil),
- }
-
- args := []driver.Value{
- int64(42424242),
- float64(math.Pi),
- false,
- time.Unix(1423411542, 807015000),
- []byte("bytes containing special chars ' \" \a \x00"),
- "string containing special chars ' \" \a \x00",
- }
- q := "SELECT ?, ?, ?, ?, ?, ?"
-
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- _, err := mc.interpolateParams(q, args)
- if err != nil {
- b.Fatal(err)
- }
- }
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/buffer.go b/vendor/github.com/go-sql-driver/mysql/buffer.go
index 2001feacd..794ac3099 100644
--- a/vendor/github.com/go-sql-driver/mysql/buffer.go
+++ b/vendor/github.com/go-sql-driver/mysql/buffer.go
@@ -8,11 +8,7 @@
package mysql
-import (
- "io"
- "net"
- "time"
-)
+import "io"
const defaultBufSize = 4096
@@ -22,28 +18,25 @@ const defaultBufSize = 4096
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
// Also highly optimized for this particular use case.
type buffer struct {
- buf []byte
- nc net.Conn
- idx int
- length int
- timeout time.Duration
+ buf []byte
+ rd io.Reader
+ idx int
+ length int
}
-func newBuffer(nc net.Conn) buffer {
+func newBuffer(rd io.Reader) buffer {
var b [defaultBufSize]byte
return buffer{
buf: b[:],
- nc: nc,
+ rd: rd,
}
}
// fill reads into the buffer until at least _need_ bytes are in it
func (b *buffer) fill(need int) error {
- n := b.length
-
// move existing data to the beginning
- if n > 0 && b.idx > 0 {
- copy(b.buf[0:n], b.buf[b.idx:])
+ if b.length > 0 && b.idx > 0 {
+ copy(b.buf[0:b.length], b.buf[b.idx:])
}
// grow buffer if necessary
@@ -59,33 +52,19 @@ func (b *buffer) fill(need int) error {
b.idx = 0
for {
- if b.timeout > 0 {
- if err := b.nc.SetReadDeadline(time.Now().Add(b.timeout)); err != nil {
- return err
- }
- }
+ n, err := b.rd.Read(b.buf[b.length:])
+ b.length += n
- nn, err := b.nc.Read(b.buf[n:])
- n += nn
-
- switch err {
- case nil:
- if n < need {
+ if err == nil {
+ if b.length < need {
continue
}
- b.length = n
return nil
-
- case io.EOF:
- if n >= need {
- b.length = n
- return nil
- }
- return io.ErrUnexpectedEOF
-
- default:
- return err
}
+ if b.length >= need && err == io.EOF {
+ return nil
+ }
+ return err
}
}
diff --git a/vendor/github.com/go-sql-driver/mysql/collations.go b/vendor/github.com/go-sql-driver/mysql/collations.go
index 82079cfb9..aabe0055d 100644
--- a/vendor/github.com/go-sql-driver/mysql/collations.go
+++ b/vendor/github.com/go-sql-driver/mysql/collations.go
@@ -8,7 +8,7 @@
package mysql
-const defaultCollation = "utf8_general_ci"
+const defaultCollation byte = 33 // utf8_general_ci
// A list of available collations mapped to the internal ID.
// To update this map use the following MySQL query:
@@ -234,17 +234,3 @@ var collations = map[string]byte{
"utf8mb4_unicode_520_ci": 246,
"utf8mb4_vietnamese_ci": 247,
}
-
-// A blacklist of collations which is unsafe to interpolate parameters.
-// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
-var unsafeCollations = map[string]bool{
- "big5_chinese_ci": true,
- "sjis_japanese_ci": true,
- "gbk_chinese_ci": true,
- "big5_bin": true,
- "gb2312_bin": true,
- "gbk_bin": true,
- "sjis_bin": true,
- "cp932_japanese_ci": true,
- "cp932_bin": true,
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/connection.go b/vendor/github.com/go-sql-driver/mysql/connection.go
index c3899de0e..04607296e 100644
--- a/vendor/github.com/go-sql-driver/mysql/connection.go
+++ b/vendor/github.com/go-sql-driver/mysql/connection.go
@@ -9,9 +9,10 @@
package mysql
import (
+ "crypto/tls"
"database/sql/driver"
+ "errors"
"net"
- "strconv"
"strings"
"time"
)
@@ -21,20 +22,34 @@ type mysqlConn struct {
netConn net.Conn
affectedRows uint64
insertId uint64
- cfg *Config
+ cfg *config
maxPacketAllowed int
maxWriteSize int
- writeTimeout time.Duration
flags clientFlag
- status statusFlag
sequence uint8
parseTime bool
strict bool
}
+type config struct {
+ user string
+ passwd string
+ net string
+ addr string
+ dbname string
+ params map[string]string
+ loc *time.Location
+ tls *tls.Config
+ timeout time.Duration
+ collation uint8
+ allowAllFiles bool
+ allowOldPasswords bool
+ clientFoundRows bool
+}
+
// Handles parameters set in DSN after the connection is established
func (mc *mysqlConn) handleParams() (err error) {
- for param, val := range mc.cfg.Params {
+ for param, val := range mc.cfg.params {
switch param {
// Charset
case "charset":
@@ -50,6 +65,27 @@ func (mc *mysqlConn) handleParams() (err error) {
return
}
+ // time.Time parsing
+ case "parseTime":
+ var isBool bool
+ mc.parseTime, isBool = readBool(val)
+ if !isBool {
+ return errors.New("Invalid Bool value: " + val)
+ }
+
+ // Strict mode
+ case "strict":
+ var isBool bool
+ mc.strict, isBool = readBool(val)
+ if !isBool {
+ return errors.New("Invalid Bool value: " + val)
+ }
+
+ // Compression
+ case "compress":
+ err = errors.New("Compression not implemented yet")
+ return
+
// System Vars
default:
err = mc.exec("SET " + param + "=" + val + "")
@@ -79,27 +115,18 @@ func (mc *mysqlConn) Close() (err error) {
// Makes Close idempotent
if mc.netConn != nil {
err = mc.writeCommandPacket(comQuit)
- }
-
- mc.cleanup()
-
- return
-}
-
-// Closes the network connection and unsets internal variables. Do not call this
-// function after successfully authentication, call Close instead. This function
-// is called before auth or on auth failure because MySQL will have already
-// closed the network connection.
-func (mc *mysqlConn) cleanup() {
- // Makes cleanup idempotent
- if mc.netConn != nil {
- if err := mc.netConn.Close(); err != nil {
- errLog.Print(err)
+ if err == nil {
+ err = mc.netConn.Close()
+ } else {
+ mc.netConn.Close()
}
mc.netConn = nil
}
+
mc.cfg = nil
- mc.buf.nc = nil
+ mc.buf.rd = nil
+
+ return
}
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
@@ -134,151 +161,28 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
return stmt, err
}
-func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
- buf := mc.buf.takeCompleteBuffer()
- if buf == nil {
- // can not take the buffer. Something must be wrong with the connection
- errLog.Print(ErrBusyBuffer)
- return "", driver.ErrBadConn
- }
- buf = buf[:0]
- argPos := 0
-
- for i := 0; i < len(query); i++ {
- q := strings.IndexByte(query[i:], '?')
- if q == -1 {
- buf = append(buf, query[i:]...)
- break
- }
- buf = append(buf, query[i:i+q]...)
- i += q
-
- arg := args[argPos]
- argPos++
-
- if arg == nil {
- buf = append(buf, "NULL"...)
- continue
- }
-
- switch v := arg.(type) {
- case int64:
- buf = strconv.AppendInt(buf, v, 10)
- case float64:
- buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
- case bool:
- if v {
- buf = append(buf, '1')
- } else {
- buf = append(buf, '0')
- }
- case time.Time:
- if v.IsZero() {
- buf = append(buf, "'0000-00-00'"...)
- } else {
- v := v.In(mc.cfg.Loc)
- v = v.Add(time.Nanosecond * 500) // To round under microsecond
- year := v.Year()
- year100 := year / 100
- year1 := year % 100
- month := v.Month()
- day := v.Day()
- hour := v.Hour()
- minute := v.Minute()
- second := v.Second()
- micro := v.Nanosecond() / 1000
-
- buf = append(buf, []byte{
- '\'',
- digits10[year100], digits01[year100],
- digits10[year1], digits01[year1],
- '-',
- digits10[month], digits01[month],
- '-',
- digits10[day], digits01[day],
- ' ',
- digits10[hour], digits01[hour],
- ':',
- digits10[minute], digits01[minute],
- ':',
- digits10[second], digits01[second],
- }...)
-
- if micro != 0 {
- micro10000 := micro / 10000
- micro100 := micro / 100 % 100
- micro1 := micro % 100
- buf = append(buf, []byte{
- '.',
- digits10[micro10000], digits01[micro10000],
- digits10[micro100], digits01[micro100],
- digits10[micro1], digits01[micro1],
- }...)
- }
- buf = append(buf, '\'')
- }
- case []byte:
- if v == nil {
- buf = append(buf, "NULL"...)
- } else {
- buf = append(buf, "_binary'"...)
- if mc.status&statusNoBackslashEscapes == 0 {
- buf = escapeBytesBackslash(buf, v)
- } else {
- buf = escapeBytesQuotes(buf, v)
- }
- buf = append(buf, '\'')
- }
- case string:
- buf = append(buf, '\'')
- if mc.status&statusNoBackslashEscapes == 0 {
- buf = escapeStringBackslash(buf, v)
- } else {
- buf = escapeStringQuotes(buf, v)
- }
- buf = append(buf, '\'')
- default:
- return "", driver.ErrSkip
- }
-
- if len(buf)+4 > mc.maxPacketAllowed {
- return "", driver.ErrSkip
- }
- }
- if argPos != len(args) {
- return "", driver.ErrSkip
- }
- return string(buf), nil
-}
-
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
if mc.netConn == nil {
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}
- if len(args) != 0 {
- if !mc.cfg.InterpolateParams {
- return nil, driver.ErrSkip
- }
- // try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
- prepared, err := mc.interpolateParams(query, args)
- if err != nil {
- return nil, err
+ if len(args) == 0 { // no args, fastpath
+ mc.affectedRows = 0
+ mc.insertId = 0
+
+ err := mc.exec(query)
+ if err == nil {
+ return &mysqlResult{
+ affectedRows: int64(mc.affectedRows),
+ insertId: int64(mc.insertId),
+ }, err
}
- query = prepared
- args = nil
+ return nil, err
}
- mc.affectedRows = 0
- mc.insertId = 0
- err := mc.exec(query)
- if err == nil {
- return &mysqlResult{
- affectedRows: int64(mc.affectedRows),
- insertId: int64(mc.insertId),
- }, err
- }
- return nil, err
+ // with args, must use prepared stmt
+ return nil, driver.ErrSkip
+
}
// Internal function to execute commands
@@ -307,38 +211,29 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}
- if len(args) != 0 {
- if !mc.cfg.InterpolateParams {
- return nil, driver.ErrSkip
- }
- // try client-side prepare to reduce roundtrip
- prepared, err := mc.interpolateParams(query, args)
- if err != nil {
- return nil, err
- }
- query = prepared
- args = nil
- }
- // Send command
- err := mc.writeCommandPacketStr(comQuery, query)
- if err == nil {
- // Read Result
- var resLen int
- resLen, err = mc.readResultSetHeaderPacket()
+ if len(args) == 0 { // no args, fastpath
+ // Send command
+ err := mc.writeCommandPacketStr(comQuery, query)
if err == nil {
- rows := new(textRows)
- rows.mc = mc
-
- if resLen == 0 {
- // no columns, no more data
- return emptyRows{}, nil
+ // Read Result
+ var resLen int
+ resLen, err = mc.readResultSetHeaderPacket()
+ if err == nil {
+ rows := new(textRows)
+ rows.mc = mc
+
+ if resLen > 0 {
+ // Columns
+ rows.columns, err = mc.readColumns(resLen)
+ }
+ return rows, err
}
- // Columns
- rows.columns, err = mc.readColumns(resLen)
- return rows, err
}
+ return nil, err
}
- return nil, err
+
+ // with args, must use prepared stmt
+ return nil, driver.ErrSkip
}
// Gets the value of the given MySQL System Variable
@@ -354,7 +249,6 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
if err == nil {
rows := new(textRows)
rows.mc = mc
- rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
if resLen > 0 {
// Columns
diff --git a/vendor/github.com/go-sql-driver/mysql/const.go b/vendor/github.com/go-sql-driver/mysql/const.go
index 88cfff3fd..379eabec1 100644
--- a/vendor/github.com/go-sql-driver/mysql/const.go
+++ b/vendor/github.com/go-sql-driver/mysql/const.go
@@ -11,7 +11,7 @@ package mysql
const (
minProtocolVersion byte = 10
maxPacketSize = 1<<24 - 1
- timeFormat = "2006-01-02 15:04:05.999999"
+ timeFormat = "2006-01-02 15:04:05"
)
// MySQL constants documentation:
@@ -24,7 +24,6 @@ const (
iERR byte = 0xff
)
-// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
type clientFlag uint32
const (
@@ -46,13 +45,6 @@ const (
clientSecureConn
clientMultiStatements
clientMultiResults
- clientPSMultiResults
- clientPluginAuth
- clientConnectAttrs
- clientPluginAuthLenEncClientData
- clientCanHandleExpiredPasswords
- clientSessionTrack
- clientDeprecateEOF
)
const (
@@ -76,7 +68,7 @@ const (
comBinlogDump
comTableDump
comConnectOut
- comRegisterSlave
+ comRegiserSlave
comStmtPrepare
comStmtExecute
comStmtSendLongData
@@ -86,7 +78,6 @@ const (
comStmtFetch
)
-// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
const (
fieldTypeDecimal byte = iota
fieldTypeTiny
@@ -107,8 +98,7 @@ const (
fieldTypeBit
)
const (
- fieldTypeJSON byte = iota + 0xf5
- fieldTypeNewDecimal
+ fieldTypeNewDecimal byte = iota + 0xf6
fieldTypeEnum
fieldTypeSet
fieldTypeTinyBLOB
@@ -140,24 +130,3 @@ const (
flagUnknown3
flagUnknown4
)
-
-// http://dev.mysql.com/doc/internals/en/status-flags.html
-type statusFlag uint16
-
-const (
- statusInTrans statusFlag = 1 << iota
- statusInAutocommit
- statusReserved // Not in documentation
- statusMoreResultsExists
- statusNoGoodIndexUsed
- statusNoIndexUsed
- statusCursorExists
- statusLastRowSent
- statusDbDropped
- statusNoBackslashEscapes
- statusMetadataChanged
- statusQueryWasSlow
- statusPsOutParams
- statusInTransReadonly
- statusSessionStateChanged
-)
diff --git a/vendor/github.com/go-sql-driver/mysql/driver.go b/vendor/github.com/go-sql-driver/mysql/driver.go
index 899f955fb..c0375fe18 100644
--- a/vendor/github.com/go-sql-driver/mysql/driver.go
+++ b/vendor/github.com/go-sql-driver/mysql/driver.go
@@ -4,7 +4,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
-// Package mysql provides a MySQL driver for Go's database/sql package
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// The driver should be used via the database/sql package:
//
@@ -22,7 +22,7 @@ import (
"net"
)
-// MySQLDriver is exported to make the driver directly accessible.
+// This struct is exported to make the driver directly accessible.
// In general the driver is used via the database/sql package.
type MySQLDriver struct{}
@@ -53,19 +53,17 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
maxPacketAllowed: maxPacketSize,
maxWriteSize: maxPacketSize - 1,
}
- mc.cfg, err = ParseDSN(dsn)
+ mc.cfg, err = parseDSN(dsn)
if err != nil {
return nil, err
}
- mc.parseTime = mc.cfg.ParseTime
- mc.strict = mc.cfg.Strict
// Connect to Server
- if dial, ok := dials[mc.cfg.Net]; ok {
- mc.netConn, err = dial(mc.cfg.Addr)
+ if dial, ok := dials[mc.cfg.net]; ok {
+ mc.netConn, err = dial(mc.cfg.addr)
} else {
- nd := net.Dialer{Timeout: mc.cfg.Timeout}
- mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr)
+ nd := net.Dialer{Timeout: mc.cfg.timeout}
+ mc.netConn, err = nd.Dial(mc.cfg.net, mc.cfg.addr)
}
if err != nil {
return nil, err
@@ -74,39 +72,44 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
// Enable TCP Keepalives on TCP connections
if tc, ok := mc.netConn.(*net.TCPConn); ok {
if err := tc.SetKeepAlive(true); err != nil {
- // Don't send COM_QUIT before handshake.
- mc.netConn.Close()
- mc.netConn = nil
+ mc.Close()
return nil, err
}
}
mc.buf = newBuffer(mc.netConn)
- // Set I/O timeouts
- mc.buf.timeout = mc.cfg.ReadTimeout
- mc.writeTimeout = mc.cfg.WriteTimeout
-
// Reading Handshake Initialization Packet
cipher, err := mc.readInitPacket()
if err != nil {
- mc.cleanup()
+ mc.Close()
return nil, err
}
// Send Client Authentication Packet
if err = mc.writeAuthPacket(cipher); err != nil {
- mc.cleanup()
+ mc.Close()
return nil, err
}
- // Handle response to auth packet, switch methods if possible
- if err = handleAuthResult(mc, cipher); err != nil {
- // Authentication failed and MySQL has already closed the connection
- // (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
- // Do not send COM_QUIT, just cleanup and return the error.
- mc.cleanup()
- return nil, err
+ // Read Result Packet
+ err = mc.readResultOK()
+ if err != nil {
+ // Retry with old authentication method, if allowed
+ if mc.cfg != nil && mc.cfg.allowOldPasswords && err == ErrOldPassword {
+ if err = mc.writeOldAuthPacket(cipher); err != nil {
+ mc.Close()
+ return nil, err
+ }
+ if err = mc.readResultOK(); err != nil {
+ mc.Close()
+ return nil, err
+ }
+ } else {
+ mc.Close()
+ return nil, err
+ }
+
}
// Get max allowed packet size
@@ -130,38 +133,6 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
return mc, nil
}
-func handleAuthResult(mc *mysqlConn, cipher []byte) error {
- // Read Result Packet
- err := mc.readResultOK()
- if err == nil {
- return nil // auth successful
- }
-
- if mc.cfg == nil {
- return err // auth failed and retry not possible
- }
-
- // Retry auth if configured to do so.
- if mc.cfg.AllowOldPasswords && err == ErrOldPassword {
- // Retry with old authentication method. Note: there are edge cases
- // where this should work but doesn't; this is currently "wontfix":
- // https://github.com/go-sql-driver/mysql/issues/184
- if err = mc.writeOldAuthPacket(cipher); err != nil {
- return err
- }
- err = mc.readResultOK()
- } else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword {
- // Retry with clear text password for
- // http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
- // http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
- if err = mc.writeClearAuthPacket(); err != nil {
- return err
- }
- err = mc.readResultOK()
- }
- return err
-}
-
func init() {
sql.Register("mysql", &MySQLDriver{})
}
diff --git a/vendor/github.com/go-sql-driver/mysql/driver_test.go b/vendor/github.com/go-sql-driver/mysql/driver_test.go
index efbc4925c..ef5b371cf 100644
--- a/vendor/github.com/go-sql-driver/mysql/driver_test.go
+++ b/vendor/github.com/go-sql-driver/mysql/driver_test.go
@@ -9,14 +9,12 @@
package mysql
import (
- "bytes"
"crypto/tls"
"database/sql"
"database/sql/driver"
"fmt"
"io"
"io/ioutil"
- "log"
"net"
"net/url"
"os"
@@ -76,75 +74,23 @@ type DBTest struct {
db *sql.DB
}
-func runTestsWithMultiStatement(t *testing.T, dsn string, tests ...func(dbt *DBTest)) {
- if !available {
- t.Skipf("MySQL server not running on %s", netAddr)
- }
-
- dsn += "&multiStatements=true"
- var db *sql.DB
- if _, err := ParseDSN(dsn); err != errInvalidDSNUnsafeCollation {
- db, err = sql.Open("mysql", dsn)
- if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
- }
- defer db.Close()
- }
-
- dbt := &DBTest{t, db}
- for _, test := range tests {
- test(dbt)
- dbt.db.Exec("DROP TABLE IF EXISTS test")
- }
-}
-
func runTests(t *testing.T, dsn string, tests ...func(dbt *DBTest)) {
if !available {
- t.Skipf("MySQL server not running on %s", netAddr)
+ t.Skipf("MySQL-Server not running on %s", netAddr)
}
db, err := sql.Open("mysql", dsn)
if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
+ t.Fatalf("Error connecting: %s", err.Error())
}
defer db.Close()
db.Exec("DROP TABLE IF EXISTS test")
- dsn2 := dsn + "&interpolateParams=true"
- var db2 *sql.DB
- if _, err := ParseDSN(dsn2); err != errInvalidDSNUnsafeCollation {
- db2, err = sql.Open("mysql", dsn2)
- if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
- }
- defer db2.Close()
- }
-
- dsn3 := dsn + "&multiStatements=true"
- var db3 *sql.DB
- if _, err := ParseDSN(dsn3); err != errInvalidDSNUnsafeCollation {
- db3, err = sql.Open("mysql", dsn3)
- if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
- }
- defer db3.Close()
- }
-
dbt := &DBTest{t, db}
- dbt2 := &DBTest{t, db2}
- dbt3 := &DBTest{t, db3}
for _, test := range tests {
test(dbt)
dbt.db.Exec("DROP TABLE IF EXISTS test")
- if db2 != nil {
- test(dbt2)
- dbt2.db.Exec("DROP TABLE IF EXISTS test")
- }
- if db3 != nil {
- test(dbt3)
- dbt3.db.Exec("DROP TABLE IF EXISTS test")
- }
}
}
@@ -152,13 +98,13 @@ func (dbt *DBTest) fail(method, query string, err error) {
if len(query) > 300 {
query = "[query too large to print]"
}
- dbt.Fatalf("error on %s %s: %s", method, query, err.Error())
+ dbt.Fatalf("Error on %s %s: %s", method, query, err.Error())
}
func (dbt *DBTest) mustExec(query string, args ...interface{}) (res sql.Result) {
res, err := dbt.db.Exec(query, args...)
if err != nil {
- dbt.fail("exec", query, err)
+ dbt.fail("Exec", query, err)
}
return res
}
@@ -166,22 +112,11 @@ func (dbt *DBTest) mustExec(query string, args ...interface{}) (res sql.Result)
func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows) {
rows, err := dbt.db.Query(query, args...)
if err != nil {
- dbt.fail("query", query, err)
+ dbt.fail("Query", query, err)
}
return rows
}
-func TestEmptyQuery(t *testing.T) {
- runTests(t, dsn, func(dbt *DBTest) {
- // just a comment, no query
- rows := dbt.mustQuery("--")
- // will hang before #255
- if rows.Next() {
- dbt.Errorf("next on rows must be false")
- }
- })
-}
-
func TestCRUD(t *testing.T) {
runTests(t, dsn, func(dbt *DBTest) {
// Create Table
@@ -201,7 +136,7 @@ func TestCRUD(t *testing.T) {
dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
}
if count != 1 {
- dbt.Fatalf("expected 1 affected row, got %d", count)
+ dbt.Fatalf("Expected 1 affected row, got %d", count)
}
id, err := res.LastInsertId()
@@ -209,7 +144,7 @@ func TestCRUD(t *testing.T) {
dbt.Fatalf("res.LastInsertId() returned error: %s", err.Error())
}
if id != 0 {
- dbt.Fatalf("expected InsertId 0, got %d", id)
+ dbt.Fatalf("Expected InsertID 0, got %d", id)
}
// Read
@@ -234,7 +169,7 @@ func TestCRUD(t *testing.T) {
dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
}
if count != 1 {
- dbt.Fatalf("expected 1 affected row, got %d", count)
+ dbt.Fatalf("Expected 1 affected row, got %d", count)
}
// Check Update
@@ -259,7 +194,7 @@ func TestCRUD(t *testing.T) {
dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
}
if count != 1 {
- dbt.Fatalf("expected 1 affected row, got %d", count)
+ dbt.Fatalf("Expected 1 affected row, got %d", count)
}
// Check for unexpected rows
@@ -269,55 +204,11 @@ func TestCRUD(t *testing.T) {
dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
}
if count != 0 {
- dbt.Fatalf("expected 0 affected row, got %d", count)
+ dbt.Fatalf("Expected 0 affected row, got %d", count)
}
})
}
-func TestMultiQuery(t *testing.T) {
- runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
- // Create Table
- dbt.mustExec("CREATE TABLE `test` (`id` int(11) NOT NULL, `value` int(11) NOT NULL) ")
-
- // Create Data
- res := dbt.mustExec("INSERT INTO test VALUES (1, 1)")
- count, err := res.RowsAffected()
- if err != nil {
- dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
- }
- if count != 1 {
- dbt.Fatalf("expected 1 affected row, got %d", count)
- }
-
- // Update
- res = dbt.mustExec("UPDATE test SET value = 3 WHERE id = 1; UPDATE test SET value = 4 WHERE id = 1; UPDATE test SET value = 5 WHERE id = 1;")
- count, err = res.RowsAffected()
- if err != nil {
- dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
- }
- if count != 1 {
- dbt.Fatalf("expected 1 affected row, got %d", count)
- }
-
- // Read
- var out int
- rows := dbt.mustQuery("SELECT value FROM test WHERE id=1;")
- if rows.Next() {
- rows.Scan(&out)
- if 5 != out {
- dbt.Errorf("5 != %d", out)
- }
-
- if rows.Next() {
- dbt.Error("unexpected data")
- }
- } else {
- dbt.Error("no data")
- }
-
- })
-}
-
func TestInt(t *testing.T) {
runTests(t, dsn, func(dbt *DBTest) {
types := [5]string{"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT"}
@@ -365,7 +256,7 @@ func TestInt(t *testing.T) {
})
}
-func TestFloat32(t *testing.T) {
+func TestFloat(t *testing.T) {
runTests(t, dsn, func(dbt *DBTest) {
types := [2]string{"FLOAT", "DOUBLE"}
in := float32(42.23)
@@ -388,52 +279,6 @@ func TestFloat32(t *testing.T) {
})
}
-func TestFloat64(t *testing.T) {
- runTests(t, dsn, func(dbt *DBTest) {
- types := [2]string{"FLOAT", "DOUBLE"}
- var expected float64 = 42.23
- var out float64
- var rows *sql.Rows
- for _, v := range types {
- dbt.mustExec("CREATE TABLE test (value " + v + ")")
- dbt.mustExec("INSERT INTO test VALUES (42.23)")
- rows = dbt.mustQuery("SELECT value FROM test")
- if rows.Next() {
- rows.Scan(&out)
- if expected != out {
- dbt.Errorf("%s: %g != %g", v, expected, out)
- }
- } else {
- dbt.Errorf("%s: no data", v)
- }
- dbt.mustExec("DROP TABLE IF EXISTS test")
- }
- })
-}
-
-func TestFloat64Placeholder(t *testing.T) {
- runTests(t, dsn, func(dbt *DBTest) {
- types := [2]string{"FLOAT", "DOUBLE"}
- var expected float64 = 42.23
- var out float64
- var rows *sql.Rows
- for _, v := range types {
- dbt.mustExec("CREATE TABLE test (id int, value " + v + ")")
- dbt.mustExec("INSERT INTO test VALUES (1, 42.23)")
- rows = dbt.mustQuery("SELECT value FROM test WHERE id = ?", 1)
- if rows.Next() {
- rows.Scan(&out)
- if expected != out {
- dbt.Errorf("%s: %g != %g", v, expected, out)
- }
- } else {
- dbt.Errorf("%s: no data", v)
- }
- dbt.mustExec("DROP TABLE IF EXISTS test")
- }
- })
-}
-
func TestString(t *testing.T) {
runTests(t, dsn, func(dbt *DBTest) {
types := [6]string{"CHAR(255)", "VARCHAR(255)", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"}
@@ -482,281 +327,97 @@ func TestString(t *testing.T) {
})
}
-type timeTests struct {
- dbtype string
- tlayout string
- tests []timeTest
-}
-
-type timeTest struct {
- s string // leading "!": do not use t as value in queries
- t time.Time
-}
-
-type timeMode byte
-
-func (t timeMode) String() string {
- switch t {
- case binaryString:
- return "binary:string"
- case binaryTime:
- return "binary:time.Time"
- case textString:
- return "text:string"
- }
- panic("unsupported timeMode")
-}
-
-func (t timeMode) Binary() bool {
- switch t {
- case binaryString, binaryTime:
- return true
- }
- return false
-}
-
-const (
- binaryString timeMode = iota
- binaryTime
- textString
-)
-
-func (t timeTest) genQuery(dbtype string, mode timeMode) string {
- var inner string
- if mode.Binary() {
- inner = "?"
- } else {
- inner = `"%s"`
- }
- return `SELECT cast(` + inner + ` as ` + dbtype + `)`
-}
-
-func (t timeTest) run(dbt *DBTest, dbtype, tlayout string, mode timeMode) {
- var rows *sql.Rows
- query := t.genQuery(dbtype, mode)
- switch mode {
- case binaryString:
- rows = dbt.mustQuery(query, t.s)
- case binaryTime:
- rows = dbt.mustQuery(query, t.t)
- case textString:
- query = fmt.Sprintf(query, t.s)
- rows = dbt.mustQuery(query)
- default:
- panic("unsupported mode")
- }
- defer rows.Close()
- var err error
- if !rows.Next() {
- err = rows.Err()
- if err == nil {
- err = fmt.Errorf("no data")
- }
- dbt.Errorf("%s [%s]: %s", dbtype, mode, err)
- return
- }
- var dst interface{}
- err = rows.Scan(&dst)
- if err != nil {
- dbt.Errorf("%s [%s]: %s", dbtype, mode, err)
- return
- }
- switch val := dst.(type) {
- case []uint8:
- str := string(val)
- if str == t.s {
- return
- }
- if mode.Binary() && dbtype == "DATETIME" && len(str) == 26 && str[:19] == t.s {
- // a fix mainly for TravisCI:
- // accept full microsecond resolution in result for DATETIME columns
- // where the binary protocol was used
- return
- }
- dbt.Errorf("%s [%s] to string: expected %q, got %q",
- dbtype, mode,
- t.s, str,
- )
- case time.Time:
- if val == t.t {
- return
- }
- dbt.Errorf("%s [%s] to string: expected %q, got %q",
- dbtype, mode,
- t.s, val.Format(tlayout),
- )
- default:
- fmt.Printf("%#v\n", []interface{}{dbtype, tlayout, mode, t.s, t.t})
- dbt.Errorf("%s [%s]: unhandled type %T (is '%v')",
- dbtype, mode,
- val, val,
- )
- }
-}
-
func TestDateTime(t *testing.T) {
- afterTime := func(t time.Time, d string) time.Time {
- dur, err := time.ParseDuration(d)
- if err != nil {
- panic(err)
- }
- return t.Add(dur)
+ type testmode struct {
+ selectSuffix string
+ args []interface{}
}
- // NOTE: MySQL rounds DATETIME(x) up - but that's not included in the tests
- format := "2006-01-02 15:04:05.999999"
- t0 := time.Time{}
- tstr0 := "0000-00-00 00:00:00.000000"
- testcases := []timeTests{
- {"DATE", format[:10], []timeTest{
- {t: time.Date(2011, 11, 20, 0, 0, 0, 0, time.UTC)},
- {t: t0, s: tstr0[:10]},
- }},
- {"DATETIME", format[:19], []timeTest{
- {t: time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC)},
- {t: t0, s: tstr0[:19]},
- }},
- {"DATETIME(0)", format[:21], []timeTest{
- {t: time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC)},
- {t: t0, s: tstr0[:19]},
- }},
- {"DATETIME(1)", format[:21], []timeTest{
- {t: time.Date(2011, 11, 20, 21, 27, 37, 100000000, time.UTC)},
- {t: t0, s: tstr0[:21]},
- }},
- {"DATETIME(6)", format, []timeTest{
- {t: time.Date(2011, 11, 20, 21, 27, 37, 123456000, time.UTC)},
- {t: t0, s: tstr0},
- }},
- {"TIME", format[11:19], []timeTest{
- {t: afterTime(t0, "12345s")},
- {s: "!-12:34:56"},
- {s: "!-838:59:59"},
- {s: "!838:59:59"},
- {t: t0, s: tstr0[11:19]},
- }},
- {"TIME(0)", format[11:19], []timeTest{
- {t: afterTime(t0, "12345s")},
- {s: "!-12:34:56"},
- {s: "!-838:59:59"},
- {s: "!838:59:59"},
- {t: t0, s: tstr0[11:19]},
- }},
- {"TIME(1)", format[11:21], []timeTest{
- {t: afterTime(t0, "12345600ms")},
- {s: "!-12:34:56.7"},
- {s: "!-838:59:58.9"},
- {s: "!838:59:58.9"},
- {t: t0, s: tstr0[11:21]},
- }},
- {"TIME(6)", format[11:], []timeTest{
- {t: afterTime(t0, "1234567890123000ns")},
- {s: "!-12:34:56.789012"},
- {s: "!-838:59:58.999999"},
- {s: "!838:59:58.999999"},
- {t: t0, s: tstr0[11:]},
- }},
+ type timetest struct {
+ in interface{}
+ sOut string
+ tOut time.Time
+ tIsZero bool
}
- dsns := []string{
- dsn + "&parseTime=true",
- dsn + "&parseTime=false",
+ type tester func(dbt *DBTest, rows *sql.Rows,
+ test *timetest, sqltype, resulttype, mode string)
+ type setup struct {
+ vartype string
+ dsnSuffix string
+ test tester
}
- for _, testdsn := range dsns {
- runTests(t, testdsn, func(dbt *DBTest) {
- microsecsSupported := false
- zeroDateSupported := false
- var rows *sql.Rows
- var err error
- rows, err = dbt.db.Query(`SELECT cast("00:00:00.1" as TIME(1)) = "00:00:00.1"`)
- if err == nil {
- rows.Scan(&microsecsSupported)
- rows.Close()
- }
- rows, err = dbt.db.Query(`SELECT cast("0000-00-00" as DATE) = "0000-00-00"`)
- if err == nil {
- rows.Scan(&zeroDateSupported)
- rows.Close()
- }
- for _, setups := range testcases {
- if t := setups.dbtype; !microsecsSupported && t[len(t)-1:] == ")" {
- // skip fractional second tests if unsupported by server
- continue
+ var (
+ modes = map[string]*testmode{
+ "text": &testmode{},
+ "binary": &testmode{" WHERE 1 = ?", []interface{}{1}},
+ }
+ timetests = map[string][]*timetest{
+ "DATE": {
+ {sDate, sDate, tDate, false},
+ {sDate0, sDate0, tDate0, true},
+ {tDate, sDate, tDate, false},
+ {tDate0, sDate0, tDate0, true},
+ },
+ "DATETIME": {
+ {sDateTime, sDateTime, tDateTime, false},
+ {sDateTime0, sDateTime0, tDate0, true},
+ {tDateTime, sDateTime, tDateTime, false},
+ {tDate0, sDateTime0, tDate0, true},
+ },
+ }
+ setups = []*setup{
+ {"string", "&parseTime=false", func(
+ dbt *DBTest, rows *sql.Rows, test *timetest, sqltype, resulttype, mode string) {
+ var sOut string
+ if err := rows.Scan(&sOut); err != nil {
+ dbt.Errorf("%s (%s %s): %s", sqltype, resulttype, mode, err.Error())
+ } else if test.sOut != sOut {
+ dbt.Errorf("%s (%s %s): %s != %s", sqltype, resulttype, mode, test.sOut, sOut)
}
- for _, setup := range setups.tests {
- allowBinTime := true
- if setup.s == "" {
- // fill time string whereever Go can reliable produce it
- setup.s = setup.t.Format(setups.tlayout)
- } else if setup.s[0] == '!' {
- // skip tests using setup.t as source in queries
- allowBinTime = false
- // fix setup.s - remove the "!"
- setup.s = setup.s[1:]
- }
- if !zeroDateSupported && setup.s == tstr0[:len(setup.s)] {
- // skip disallowed 0000-00-00 date
- continue
- }
- setup.run(dbt, setups.dbtype, setups.tlayout, textString)
- setup.run(dbt, setups.dbtype, setups.tlayout, binaryString)
- if allowBinTime {
- setup.run(dbt, setups.dbtype, setups.tlayout, binaryTime)
+ }},
+ {"time.Time", "&parseTime=true", func(
+ dbt *DBTest, rows *sql.Rows, test *timetest, sqltype, resulttype, mode string) {
+ var tOut time.Time
+ if err := rows.Scan(&tOut); err != nil {
+ dbt.Errorf("%s (%s %s): %s", sqltype, resulttype, mode, err.Error())
+ } else if test.tOut != tOut || test.tIsZero != tOut.IsZero() {
+ dbt.Errorf("%s (%s %s): %s [%t] != %s [%t]", sqltype, resulttype, mode, test.tOut, test.tIsZero, tOut, tOut.IsZero())
+ }
+ }},
+ }
+ )
+
+ var s *setup
+ testTime := func(dbt *DBTest) {
+ var rows *sql.Rows
+ for sqltype, tests := range timetests {
+ dbt.mustExec("CREATE TABLE test (value " + sqltype + ")")
+ for _, test := range tests {
+ for mode, q := range modes {
+ dbt.mustExec("TRUNCATE test")
+ dbt.mustExec("INSERT INTO test VALUES (?)", test.in)
+ rows = dbt.mustQuery("SELECT value FROM test"+q.selectSuffix, q.args...)
+ if rows.Next() {
+ s.test(dbt, rows, test, sqltype, s.vartype, mode)
+ } else {
+ if err := rows.Err(); err != nil {
+ dbt.Errorf("%s (%s %s): %s",
+ sqltype, s.vartype, mode, err.Error())
+ } else {
+ dbt.Errorf("%s (%s %s): no data",
+ sqltype, s.vartype, mode)
+ }
}
}
}
- })
+ dbt.mustExec("DROP TABLE IF EXISTS test")
+ }
}
-}
-func TestTimestampMicros(t *testing.T) {
- format := "2006-01-02 15:04:05.999999"
- f0 := format[:19]
- f1 := format[:21]
- f6 := format[:26]
- runTests(t, dsn, func(dbt *DBTest) {
- // check if microseconds are supported.
- // Do not use timestamp(x) for that check - before 5.5.6, x would mean display width
- // and not precision.
- // Se last paragraph at http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html
- microsecsSupported := false
- if rows, err := dbt.db.Query(`SELECT cast("00:00:00.1" as TIME(1)) = "00:00:00.1"`); err == nil {
- rows.Scan(&microsecsSupported)
- rows.Close()
- }
- if !microsecsSupported {
- // skip test
- return
- }
- _, err := dbt.db.Exec(`
- CREATE TABLE test (
- value0 TIMESTAMP NOT NULL DEFAULT '` + f0 + `',
- value1 TIMESTAMP(1) NOT NULL DEFAULT '` + f1 + `',
- value6 TIMESTAMP(6) NOT NULL DEFAULT '` + f6 + `'
- )`,
- )
- if err != nil {
- dbt.Error(err)
- }
- defer dbt.mustExec("DROP TABLE IF EXISTS test")
- dbt.mustExec("INSERT INTO test SET value0=?, value1=?, value6=?", f0, f1, f6)
- var res0, res1, res6 string
- rows := dbt.mustQuery("SELECT * FROM test")
- if !rows.Next() {
- dbt.Errorf("test contained no selectable values")
- }
- err = rows.Scan(&res0, &res1, &res6)
- if err != nil {
- dbt.Error(err)
- }
- if res0 != f0 {
- dbt.Errorf("expected %q, got %q", f0, res0)
- }
- if res1 != f1 {
- dbt.Errorf("expected %q, got %q", f1, res1)
- }
- if res6 != f6 {
- dbt.Errorf("expected %q, got %q", f6, res6)
- }
- })
+ timeDsn := dsn + "&sql_mode=ALLOW_INVALID_DATES"
+ for _, v := range setups {
+ s = v
+ runTests(t, timeDsn+s.dsnSuffix, testTime)
+ }
}
func TestNULL(t *testing.T) {
@@ -780,14 +441,14 @@ func TestNULL(t *testing.T) {
dbt.Fatal(err)
}
if nb.Valid {
- dbt.Error("valid NullBool which should be invalid")
+ dbt.Error("Valid NullBool which should be invalid")
}
// Valid
if err = nonNullStmt.QueryRow().Scan(&nb); err != nil {
dbt.Fatal(err)
}
if !nb.Valid {
- dbt.Error("invalid NullBool which should be valid")
+ dbt.Error("Invalid NullBool which should be valid")
} else if nb.Bool != true {
dbt.Errorf("Unexpected NullBool value: %t (should be true)", nb.Bool)
}
@@ -799,16 +460,16 @@ func TestNULL(t *testing.T) {
dbt.Fatal(err)
}
if nf.Valid {
- dbt.Error("valid NullFloat64 which should be invalid")
+ dbt.Error("Valid NullFloat64 which should be invalid")
}
// Valid
if err = nonNullStmt.QueryRow().Scan(&nf); err != nil {
dbt.Fatal(err)
}
if !nf.Valid {
- dbt.Error("invalid NullFloat64 which should be valid")
+ dbt.Error("Invalid NullFloat64 which should be valid")
} else if nf.Float64 != float64(1) {
- dbt.Errorf("unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64)
+ dbt.Errorf("Unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64)
}
// NullInt64
@@ -818,16 +479,16 @@ func TestNULL(t *testing.T) {
dbt.Fatal(err)
}
if ni.Valid {
- dbt.Error("valid NullInt64 which should be invalid")
+ dbt.Error("Valid NullInt64 which should be invalid")
}
// Valid
if err = nonNullStmt.QueryRow().Scan(&ni); err != nil {
dbt.Fatal(err)
}
if !ni.Valid {
- dbt.Error("invalid NullInt64 which should be valid")
+ dbt.Error("Invalid NullInt64 which should be valid")
} else if ni.Int64 != int64(1) {
- dbt.Errorf("unexpected NullInt64 value: %d (should be 1)", ni.Int64)
+ dbt.Errorf("Unexpected NullInt64 value: %d (should be 1)", ni.Int64)
}
// NullString
@@ -837,16 +498,16 @@ func TestNULL(t *testing.T) {
dbt.Fatal(err)
}
if ns.Valid {
- dbt.Error("valid NullString which should be invalid")
+ dbt.Error("Valid NullString which should be invalid")
}
// Valid
if err = nonNullStmt.QueryRow().Scan(&ns); err != nil {
dbt.Fatal(err)
}
if !ns.Valid {
- dbt.Error("invalid NullString which should be valid")
+ dbt.Error("Invalid NullString which should be valid")
} else if ns.String != `1` {
- dbt.Error("unexpected NullString value:" + ns.String + " (should be `1`)")
+ dbt.Error("Unexpected NullString value:" + ns.String + " (should be `1`)")
}
// nil-bytes
@@ -856,14 +517,14 @@ func TestNULL(t *testing.T) {
dbt.Fatal(err)
}
if b != nil {
- dbt.Error("non-nil []byte wich should be nil")
+ dbt.Error("Non-nil []byte wich should be nil")
}
// Read non-nil
if err = nonNullStmt.QueryRow().Scan(&b); err != nil {
dbt.Fatal(err)
}
if b == nil {
- dbt.Error("nil []byte wich should be non-nil")
+ dbt.Error("Nil []byte wich should be non-nil")
}
// Insert nil
b = nil
@@ -872,7 +533,7 @@ func TestNULL(t *testing.T) {
dbt.Fatal(err)
}
if !success {
- dbt.Error("inserting []byte(nil) as NULL failed")
+ dbt.Error("Inserting []byte(nil) as NULL failed")
}
// Check input==output with input==nil
b = nil
@@ -880,7 +541,7 @@ func TestNULL(t *testing.T) {
dbt.Fatal(err)
}
if b != nil {
- dbt.Error("non-nil echo from nil input")
+ dbt.Error("Non-nil echo from nil input")
}
// Check input==output with input!=nil
b = []byte("")
@@ -909,49 +570,6 @@ func TestNULL(t *testing.T) {
})
}
-func TestUint64(t *testing.T) {
- const (
- u0 = uint64(0)
- uall = ^u0
- uhigh = uall >> 1
- utop = ^uhigh
- s0 = int64(0)
- sall = ^s0
- shigh = int64(uhigh)
- stop = ^shigh
- )
- runTests(t, dsn, func(dbt *DBTest) {
- stmt, err := dbt.db.Prepare(`SELECT ?, ?, ? ,?, ?, ?, ?, ?`)
- if err != nil {
- dbt.Fatal(err)
- }
- defer stmt.Close()
- row := stmt.QueryRow(
- u0, uhigh, utop, uall,
- s0, shigh, stop, sall,
- )
-
- var ua, ub, uc, ud uint64
- var sa, sb, sc, sd int64
-
- err = row.Scan(&ua, &ub, &uc, &ud, &sa, &sb, &sc, &sd)
- if err != nil {
- dbt.Fatal(err)
- }
- switch {
- case ua != u0,
- ub != uhigh,
- uc != utop,
- ud != uall,
- sa != s0,
- sb != shigh,
- sc != stop,
- sd != sall:
- dbt.Fatal("unexpected result value")
- }
- })
-}
-
func TestLongData(t *testing.T) {
runTests(t, dsn, func(dbt *DBTest) {
var maxAllowedPacketSize int
@@ -1042,7 +660,7 @@ func TestLoadData(t *testing.T) {
dbt.Fatalf("%d != %d", i, id)
}
if values[i-1] != value {
- dbt.Fatalf("%q != %q", values[i-1], value)
+ dbt.Fatalf("%s != %s", values[i-1], value)
}
}
err = rows.Err()
@@ -1051,7 +669,7 @@ func TestLoadData(t *testing.T) {
}
if i != 4 {
- dbt.Fatalf("rows count mismatch. Got %d, want 4", i)
+ dbt.Fatalf("Rows count mismatch. Got %d, want 4", i)
}
}
file, err := ioutil.TempFile("", "gotest")
@@ -1067,13 +685,13 @@ func TestLoadData(t *testing.T) {
// Local File
RegisterLocalFile(file.Name())
- dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE %q INTO TABLE test", file.Name()))
+ dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE '%q' INTO TABLE test", file.Name()))
verifyLoadDataResult()
// negative test
_, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'doesnotexist' INTO TABLE test")
if err == nil {
- dbt.Fatal("load non-existent file didn't fail")
- } else if err.Error() != "local file 'doesnotexist' is not registered" {
+ dbt.Fatal("Load non-existent file didn't fail")
+ } else if err.Error() != "Local File 'doesnotexist' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files" {
dbt.Fatal(err.Error())
}
@@ -1093,7 +711,7 @@ func TestLoadData(t *testing.T) {
// negative test
_, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'Reader::doesnotexist' INTO TABLE test")
if err == nil {
- dbt.Fatal("load non-existent Reader didn't fail")
+ dbt.Fatal("Load non-existent Reader didn't fail")
} else if err.Error() != "Reader 'doesnotexist' is not registered" {
dbt.Fatal(err.Error())
}
@@ -1147,18 +765,7 @@ func TestFoundRows(t *testing.T) {
func TestStrict(t *testing.T) {
// ALLOW_INVALID_DATES to get rid of stricter modes - we want to test for warnings, not errors
- relaxedDsn := dsn + "&sql_mode='ALLOW_INVALID_DATES,NO_AUTO_CREATE_USER'"
- // make sure the MySQL version is recent enough with a separate connection
- // before running the test
- conn, err := MySQLDriver{}.Open(relaxedDsn)
- if conn != nil {
- conn.Close()
- }
- if me, ok := err.(*MySQLError); ok && me.Number == 1231 {
- // Error 1231: Variable 'sql_mode' can't be set to the value of 'ALLOW_INVALID_DATES'
- // => skip test, MySQL server version is too old
- return
- }
+ relaxedDsn := dsn + "&sql_mode=ALLOW_INVALID_DATES"
runTests(t, relaxedDsn, func(dbt *DBTest) {
dbt.mustExec("CREATE TABLE test (a TINYINT NOT NULL, b CHAR(4))")
@@ -1173,7 +780,7 @@ func TestStrict(t *testing.T) {
var checkWarnings = func(err error, mode string, idx int) {
if err == nil {
- dbt.Errorf("expected STRICT error on query [%s] %s", mode, queries[idx].in)
+ dbt.Errorf("Expected STRICT error on query [%s] %s", mode, queries[idx].in)
}
if warnings, ok := err.(MySQLWarnings); ok {
@@ -1182,18 +789,18 @@ func TestStrict(t *testing.T) {
codes[i] = warnings[i].Code
}
if len(codes) != len(queries[idx].codes) {
- dbt.Errorf("unexpected STRICT error count on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes)
+ dbt.Errorf("Unexpected STRICT error count on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes)
}
for i := range warnings {
if codes[i] != queries[idx].codes[i] {
- dbt.Errorf("unexpected STRICT error codes on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes)
+ dbt.Errorf("Unexpected STRICT error codes on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes)
return
}
}
} else {
- dbt.Errorf("unexpected error on query [%s] %s: %s", mode, queries[idx].in, err.Error())
+ dbt.Errorf("Unexpected error on query [%s] %s: %s", mode, queries[idx].in, err.Error())
}
}
@@ -1209,7 +816,7 @@ func TestStrict(t *testing.T) {
for i := range queries {
stmt, err = dbt.db.Prepare(queries[i].in)
if err != nil {
- dbt.Errorf("error on preparing query %s: %s", queries[i].in, err.Error())
+ dbt.Errorf("Error on preparing query %s: %s", queries[i].in, err.Error())
}
_, err = stmt.Exec()
@@ -1217,7 +824,7 @@ func TestStrict(t *testing.T) {
err = stmt.Close()
if err != nil {
- dbt.Errorf("error on closing stmt for query %s: %s", queries[i].in, err.Error())
+ dbt.Errorf("Error on closing stmt for query %s: %s", queries[i].in, err.Error())
}
}
})
@@ -1227,9 +834,9 @@ func TestTLS(t *testing.T) {
tlsTest := func(dbt *DBTest) {
if err := dbt.db.Ping(); err != nil {
if err == ErrNoTLS {
- dbt.Skip("server does not support TLS")
+ dbt.Skip("Server does not support TLS")
} else {
- dbt.Fatalf("error on Ping: %s", err.Error())
+ dbt.Fatalf("Error on Ping: %s", err.Error())
}
}
@@ -1242,7 +849,7 @@ func TestTLS(t *testing.T) {
}
if value == nil {
- dbt.Fatal("no Cipher")
+ dbt.Fatal("No Cipher")
}
}
}
@@ -1259,42 +866,42 @@ func TestTLS(t *testing.T) {
func TestReuseClosedConnection(t *testing.T) {
// this test does not use sql.database, it uses the driver directly
if !available {
- t.Skipf("MySQL server not running on %s", netAddr)
+ t.Skipf("MySQL-Server not running on %s", netAddr)
}
md := &MySQLDriver{}
conn, err := md.Open(dsn)
if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
+ t.Fatalf("Error connecting: %s", err.Error())
}
stmt, err := conn.Prepare("DO 1")
if err != nil {
- t.Fatalf("error preparing statement: %s", err.Error())
+ t.Fatalf("Error preparing statement: %s", err.Error())
}
_, err = stmt.Exec(nil)
if err != nil {
- t.Fatalf("error executing statement: %s", err.Error())
+ t.Fatalf("Error executing statement: %s", err.Error())
}
err = conn.Close()
if err != nil {
- t.Fatalf("error closing connection: %s", err.Error())
+ t.Fatalf("Error closing connection: %s", err.Error())
}
defer func() {
if err := recover(); err != nil {
- t.Errorf("panic after reusing a closed connection: %v", err)
+ t.Errorf("Panic after reusing a closed connection: %v", err)
}
}()
_, err = stmt.Exec(nil)
if err != nil && err != driver.ErrBadConn {
- t.Errorf("unexpected error '%s', expected '%s'",
+ t.Errorf("Unexpected error '%s', expected '%s'",
err.Error(), driver.ErrBadConn.Error())
}
}
func TestCharset(t *testing.T) {
if !available {
- t.Skipf("MySQL server not running on %s", netAddr)
+ t.Skipf("MySQL-Server not running on %s", netAddr)
}
mustSetCharset := func(charsetParam, expected string) {
@@ -1303,14 +910,14 @@ func TestCharset(t *testing.T) {
defer rows.Close()
if !rows.Next() {
- dbt.Fatalf("error getting connection charset: %s", rows.Err())
+ dbt.Fatalf("Error getting connection charset: %s", rows.Err())
}
var got string
rows.Scan(&got)
if got != expected {
- dbt.Fatalf("expected connection charset %s but got %s", expected, got)
+ dbt.Fatalf("Expected connection charset %s but got %s", expected, got)
}
})
}
@@ -1332,14 +939,14 @@ func TestFailingCharset(t *testing.T) {
_, err := dbt.db.Exec("SELECT 1")
if err == nil {
dbt.db.Close()
- t.Fatalf("connection must not succeed without a valid charset")
+ t.Fatalf("Connection must not succeed without a valid charset")
}
})
}
func TestCollation(t *testing.T) {
if !available {
- t.Skipf("MySQL server not running on %s", netAddr)
+ t.Skipf("MySQL-Server not running on %s", netAddr)
}
defaultCollation := "utf8_general_ci"
@@ -1349,7 +956,7 @@ func TestCollation(t *testing.T) {
"latin1_general_ci",
"binary",
"utf8_unicode_ci",
- "cp1257_bin",
+ "utf8mb4_general_ci",
}
for _, collation := range testCollations {
@@ -1369,36 +976,12 @@ func TestCollation(t *testing.T) {
}
if got != expected {
- dbt.Fatalf("expected connection collation %s but got %s", expected, got)
+ dbt.Fatalf("Expected connection collation %s but got %s", expected, got)
}
})
}
}
-func TestColumnsWithAlias(t *testing.T) {
- runTests(t, dsn+"&columnsWithAlias=true", func(dbt *DBTest) {
- rows := dbt.mustQuery("SELECT 1 AS A")
- defer rows.Close()
- cols, _ := rows.Columns()
- if len(cols) != 1 {
- t.Fatalf("expected 1 column, got %d", len(cols))
- }
- if cols[0] != "A" {
- t.Fatalf("expected column name \"A\", got \"%s\"", cols[0])
- }
- rows.Close()
-
- rows = dbt.mustQuery("SELECT * FROM (SELECT 1 AS one) AS A")
- cols, _ = rows.Columns()
- if len(cols) != 1 {
- t.Fatalf("expected 1 column, got %d", len(cols))
- }
- if cols[0] != "A.one" {
- t.Fatalf("expected column name \"A.one\", got \"%s\"", cols[0])
- }
- })
-}
-
func TestRawBytesResultExceedsBuffer(t *testing.T) {
runTests(t, dsn, func(dbt *DBTest) {
// defaultBufSize from buffer.go
@@ -1428,26 +1011,26 @@ func TestTimezoneConversion(t *testing.T) {
// Insert local time into database (should be converted)
usCentral, _ := time.LoadLocation("US/Central")
- reftime := time.Date(2014, 05, 30, 18, 03, 17, 0, time.UTC).In(usCentral)
- dbt.mustExec("INSERT INTO test VALUE (?)", reftime)
+ now := time.Now().In(usCentral)
+ dbt.mustExec("INSERT INTO test VALUE (?)", now)
// Retrieve time from DB
rows := dbt.mustQuery("SELECT ts FROM test")
if !rows.Next() {
- dbt.Fatal("did not get any rows out")
+ dbt.Fatal("Didn't get any rows out")
}
- var dbTime time.Time
- err := rows.Scan(&dbTime)
+ var nowDB time.Time
+ err := rows.Scan(&nowDB)
if err != nil {
dbt.Fatal("Err", err)
}
// Check that dates match
- if reftime.Unix() != dbTime.Unix() {
- dbt.Errorf("times do not match.\n")
- dbt.Errorf(" Now(%v)=%v\n", usCentral, reftime)
- dbt.Errorf(" Now(UTC)=%v\n", dbTime)
+ if now.Unix() != nowDB.Unix() {
+ dbt.Errorf("Times don't match.\n")
+ dbt.Errorf(" Now(%v)=%v\n", usCentral, now)
+ dbt.Errorf(" Now(UTC)=%v\n", nowDB)
}
}
@@ -1456,6 +1039,42 @@ func TestTimezoneConversion(t *testing.T) {
}
}
+// This tests for https://github.com/go-sql-driver/mysql/pull/139
+//
+// An extra (invisible) nil byte was being added to the beginning of positive
+// time strings.
+func TestTimeSign(t *testing.T) {
+ runTests(t, dsn, func(dbt *DBTest) {
+ var sTimes = []struct {
+ value string
+ fieldType string
+ }{
+ {"12:34:56", "TIME"},
+ {"-12:34:56", "TIME"},
+ // As described in http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html
+ // they *should* work, but only in 5.6+.
+ // { "12:34:56.789", "TIME(3)" },
+ // { "-12:34:56.789", "TIME(3)" },
+ }
+
+ for _, sTime := range sTimes {
+ dbt.db.Exec("DROP TABLE IF EXISTS test")
+ dbt.mustExec("CREATE TABLE test (id INT, time_field " + sTime.fieldType + ")")
+ dbt.mustExec("INSERT INTO test (id, time_field) VALUES(1, '" + sTime.value + "')")
+ rows := dbt.mustQuery("SELECT time_field FROM test WHERE id = ?", 1)
+ if rows.Next() {
+ var oTime string
+ rows.Scan(&oTime)
+ if oTime != sTime.value {
+ dbt.Errorf(`time values differ: got %q, expected %q.`, oTime, sTime.value)
+ }
+ } else {
+ dbt.Error("expecting at least one row.")
+ }
+ }
+ })
+}
+
// Special cases
func TestRowsClose(t *testing.T) {
@@ -1471,7 +1090,7 @@ func TestRowsClose(t *testing.T) {
}
if rows.Next() {
- dbt.Fatal("unexpected row after rows.Close()")
+ dbt.Fatal("Unexpected row after rows.Close()")
}
err = rows.Err()
@@ -1503,7 +1122,7 @@ func TestCloseStmtBeforeRows(t *testing.T) {
}
if !rows.Next() {
- dbt.Fatal("getting row failed")
+ dbt.Fatal("Getting row failed")
} else {
err = rows.Err()
if err != nil {
@@ -1513,7 +1132,7 @@ func TestCloseStmtBeforeRows(t *testing.T) {
var out bool
err = rows.Scan(&out)
if err != nil {
- dbt.Fatalf("error on rows.Scan(): %s", err.Error())
+ dbt.Fatalf("Error on rows.Scan(): %s", err.Error())
}
if out != true {
dbt.Errorf("true != %t", out)
@@ -1549,7 +1168,7 @@ func TestStmtMultiRows(t *testing.T) {
// 1
if !rows1.Next() {
- dbt.Fatal("first rows1.Next failed")
+ dbt.Fatal("1st rows1.Next failed")
} else {
err = rows1.Err()
if err != nil {
@@ -1558,7 +1177,7 @@ func TestStmtMultiRows(t *testing.T) {
err = rows1.Scan(&out)
if err != nil {
- dbt.Fatalf("error on rows.Scan(): %s", err.Error())
+ dbt.Fatalf("Error on rows.Scan(): %s", err.Error())
}
if out != true {
dbt.Errorf("true != %t", out)
@@ -1566,7 +1185,7 @@ func TestStmtMultiRows(t *testing.T) {
}
if !rows2.Next() {
- dbt.Fatal("first rows2.Next failed")
+ dbt.Fatal("1st rows2.Next failed")
} else {
err = rows2.Err()
if err != nil {
@@ -1575,7 +1194,7 @@ func TestStmtMultiRows(t *testing.T) {
err = rows2.Scan(&out)
if err != nil {
- dbt.Fatalf("error on rows.Scan(): %s", err.Error())
+ dbt.Fatalf("Error on rows.Scan(): %s", err.Error())
}
if out != true {
dbt.Errorf("true != %t", out)
@@ -1584,7 +1203,7 @@ func TestStmtMultiRows(t *testing.T) {
// 2
if !rows1.Next() {
- dbt.Fatal("second rows1.Next failed")
+ dbt.Fatal("2nd rows1.Next failed")
} else {
err = rows1.Err()
if err != nil {
@@ -1593,14 +1212,14 @@ func TestStmtMultiRows(t *testing.T) {
err = rows1.Scan(&out)
if err != nil {
- dbt.Fatalf("error on rows.Scan(): %s", err.Error())
+ dbt.Fatalf("Error on rows.Scan(): %s", err.Error())
}
if out != false {
dbt.Errorf("false != %t", out)
}
if rows1.Next() {
- dbt.Fatal("unexpected row on rows1")
+ dbt.Fatal("Unexpected row on rows1")
}
err = rows1.Close()
if err != nil {
@@ -1609,7 +1228,7 @@ func TestStmtMultiRows(t *testing.T) {
}
if !rows2.Next() {
- dbt.Fatal("second rows2.Next failed")
+ dbt.Fatal("2nd rows2.Next failed")
} else {
err = rows2.Err()
if err != nil {
@@ -1618,14 +1237,14 @@ func TestStmtMultiRows(t *testing.T) {
err = rows2.Scan(&out)
if err != nil {
- dbt.Fatalf("error on rows.Scan(): %s", err.Error())
+ dbt.Fatalf("Error on rows.Scan(): %s", err.Error())
}
if out != false {
dbt.Errorf("false != %t", out)
}
if rows2.Next() {
- dbt.Fatal("unexpected row on rows2")
+ dbt.Fatal("Unexpected row on rows2")
}
err = rows2.Close()
if err != nil {
@@ -1670,7 +1289,7 @@ func TestConcurrent(t *testing.T) {
if err != nil {
dbt.Fatalf("%s", err.Error())
}
- dbt.Logf("testing up to %d concurrent connections \r\n", max)
+ dbt.Logf("Testing up to %d concurrent connections \r\n", max)
var remaining, succeeded int32 = int32(max), 0
@@ -1694,7 +1313,7 @@ func TestConcurrent(t *testing.T) {
if err != nil {
if err.Error() != "Error 1040: Too many connections" {
- fatalf("error on conn %d: %s", id, err.Error())
+ fatalf("Error on Conn %d: %s", id, err.Error())
}
return
}
@@ -1702,13 +1321,13 @@ func TestConcurrent(t *testing.T) {
// keep the connection busy until all connections are open
for remaining > 0 {
if _, err = tx.Exec("DO 1"); err != nil {
- fatalf("error on conn %d: %s", id, err.Error())
+ fatalf("Error on Conn %d: %s", id, err.Error())
return
}
}
if err = tx.Commit(); err != nil {
- fatalf("error on conn %d: %s", id, err.Error())
+ fatalf("Error on Conn %d: %s", id, err.Error())
return
}
@@ -1724,14 +1343,14 @@ func TestConcurrent(t *testing.T) {
dbt.Fatal(fatalError)
}
- dbt.Logf("reached %d concurrent connections\r\n", succeeded)
+ dbt.Logf("Reached %d concurrent connections\r\n", succeeded)
})
}
// Tests custom dial functions
func TestCustomDial(t *testing.T) {
if !available {
- t.Skipf("MySQL server not running on %s", netAddr)
+ t.Skipf("MySQL-Server not running on %s", netAddr)
}
// our custom dial function which justs wraps net.Dial here
@@ -1741,117 +1360,11 @@ func TestCustomDial(t *testing.T) {
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@mydial(%s)/%s?timeout=30s&strict=true", user, pass, addr, dbname))
if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
+ t.Fatalf("Error connecting: %s", err.Error())
}
defer db.Close()
if _, err = db.Exec("DO 1"); err != nil {
- t.Fatalf("connection failed: %s", err.Error())
- }
-}
-
-func TestSQLInjection(t *testing.T) {
- createTest := func(arg string) func(dbt *DBTest) {
- return func(dbt *DBTest) {
- dbt.mustExec("CREATE TABLE test (v INTEGER)")
- dbt.mustExec("INSERT INTO test VALUES (?)", 1)
-
- var v int
- // NULL can't be equal to anything, the idea here is to inject query so it returns row
- // This test verifies that escapeQuotes and escapeBackslash are working properly
- err := dbt.db.QueryRow("SELECT v FROM test WHERE NULL = ?", arg).Scan(&v)
- if err == sql.ErrNoRows {
- return // success, sql injection failed
- } else if err == nil {
- dbt.Errorf("sql injection successful with arg: %s", arg)
- } else {
- dbt.Errorf("error running query with arg: %s; err: %s", arg, err.Error())
- }
- }
- }
-
- dsns := []string{
- dsn,
- dsn + "&sql_mode='NO_BACKSLASH_ESCAPES,NO_AUTO_CREATE_USER'",
- }
- for _, testdsn := range dsns {
- runTests(t, testdsn, createTest("1 OR 1=1"))
- runTests(t, testdsn, createTest("' OR '1'='1"))
- }
-}
-
-// Test if inserted data is correctly retrieved after being escaped
-func TestInsertRetrieveEscapedData(t *testing.T) {
- testData := func(dbt *DBTest) {
- dbt.mustExec("CREATE TABLE test (v VARCHAR(255))")
-
- // All sequences that are escaped by escapeQuotes and escapeBackslash
- v := "foo \x00\n\r\x1a\"'\\"
- dbt.mustExec("INSERT INTO test VALUES (?)", v)
-
- var out string
- err := dbt.db.QueryRow("SELECT v FROM test").Scan(&out)
- if err != nil {
- dbt.Fatalf("%s", err.Error())
- }
-
- if out != v {
- dbt.Errorf("%q != %q", out, v)
- }
+ t.Fatalf("Connection failed: %s", err.Error())
}
-
- dsns := []string{
- dsn,
- dsn + "&sql_mode='NO_BACKSLASH_ESCAPES,NO_AUTO_CREATE_USER'",
- }
- for _, testdsn := range dsns {
- runTests(t, testdsn, testData)
- }
-}
-
-func TestUnixSocketAuthFail(t *testing.T) {
- runTests(t, dsn, func(dbt *DBTest) {
- // Save the current logger so we can restore it.
- oldLogger := errLog
-
- // Set a new logger so we can capture its output.
- buffer := bytes.NewBuffer(make([]byte, 0, 64))
- newLogger := log.New(buffer, "prefix: ", 0)
- SetLogger(newLogger)
-
- // Restore the logger.
- defer SetLogger(oldLogger)
-
- // Make a new DSN that uses the MySQL socket file and a bad password, which
- // we can make by simply appending any character to the real password.
- badPass := pass + "x"
- socket := ""
- if prot == "unix" {
- socket = addr
- } else {
- // Get socket file from MySQL.
- err := dbt.db.QueryRow("SELECT @@socket").Scan(&socket)
- if err != nil {
- t.Fatalf("error on SELECT @@socket: %s", err.Error())
- }
- }
- t.Logf("socket: %s", socket)
- badDSN := fmt.Sprintf("%s:%s@unix(%s)/%s?timeout=30s&strict=true", user, badPass, socket, dbname)
- db, err := sql.Open("mysql", badDSN)
- if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
- }
- defer db.Close()
-
- // Connect to MySQL for real. This will cause an auth failure.
- err = db.Ping()
- if err == nil {
- t.Error("expected Ping() to return an error")
- }
-
- // The driver should not log anything.
- if actual := buffer.String(); actual != "" {
- t.Errorf("expected no output, got %q", actual)
- }
- })
}
diff --git a/vendor/github.com/go-sql-driver/mysql/dsn.go b/vendor/github.com/go-sql-driver/mysql/dsn.go
deleted file mode 100644
index 73138bc57..000000000
--- a/vendor/github.com/go-sql-driver/mysql/dsn.go
+++ /dev/null
@@ -1,513 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-package mysql
-
-import (
- "bytes"
- "crypto/tls"
- "errors"
- "fmt"
- "net"
- "net/url"
- "strings"
- "time"
-)
-
-var (
- errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
- errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
- errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
- errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
-)
-
-// Config is a configuration parsed from a DSN string
-type Config struct {
- User string // Username
- Passwd string // Password (requires User)
- Net string // Network type
- Addr string // Network address (requires Net)
- DBName string // Database name
- Params map[string]string // Connection parameters
- Collation string // Connection collation
- Loc *time.Location // Location for time.Time values
- TLSConfig string // TLS configuration name
- tls *tls.Config // TLS configuration
- Timeout time.Duration // Dial timeout
- ReadTimeout time.Duration // I/O read timeout
- WriteTimeout time.Duration // I/O write timeout
-
- AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
- AllowCleartextPasswords bool // Allows the cleartext client side plugin
- AllowOldPasswords bool // Allows the old insecure password method
- ClientFoundRows bool // Return number of matching rows instead of rows changed
- ColumnsWithAlias bool // Prepend table alias to column names
- InterpolateParams bool // Interpolate placeholders into query string
- MultiStatements bool // Allow multiple statements in one query
- ParseTime bool // Parse time values to time.Time
- Strict bool // Return warnings as errors
-}
-
-// FormatDSN formats the given Config into a DSN string which can be passed to
-// the driver.
-func (cfg *Config) FormatDSN() string {
- var buf bytes.Buffer
-
- // [username[:password]@]
- if len(cfg.User) > 0 {
- buf.WriteString(cfg.User)
- if len(cfg.Passwd) > 0 {
- buf.WriteByte(':')
- buf.WriteString(cfg.Passwd)
- }
- buf.WriteByte('@')
- }
-
- // [protocol[(address)]]
- if len(cfg.Net) > 0 {
- buf.WriteString(cfg.Net)
- if len(cfg.Addr) > 0 {
- buf.WriteByte('(')
- buf.WriteString(cfg.Addr)
- buf.WriteByte(')')
- }
- }
-
- // /dbname
- buf.WriteByte('/')
- buf.WriteString(cfg.DBName)
-
- // [?param1=value1&...&paramN=valueN]
- hasParam := false
-
- if cfg.AllowAllFiles {
- hasParam = true
- buf.WriteString("?allowAllFiles=true")
- }
-
- if cfg.AllowCleartextPasswords {
- if hasParam {
- buf.WriteString("&allowCleartextPasswords=true")
- } else {
- hasParam = true
- buf.WriteString("?allowCleartextPasswords=true")
- }
- }
-
- if cfg.AllowOldPasswords {
- if hasParam {
- buf.WriteString("&allowOldPasswords=true")
- } else {
- hasParam = true
- buf.WriteString("?allowOldPasswords=true")
- }
- }
-
- if cfg.ClientFoundRows {
- if hasParam {
- buf.WriteString("&clientFoundRows=true")
- } else {
- hasParam = true
- buf.WriteString("?clientFoundRows=true")
- }
- }
-
- if col := cfg.Collation; col != defaultCollation && len(col) > 0 {
- if hasParam {
- buf.WriteString("&collation=")
- } else {
- hasParam = true
- buf.WriteString("?collation=")
- }
- buf.WriteString(col)
- }
-
- if cfg.ColumnsWithAlias {
- if hasParam {
- buf.WriteString("&columnsWithAlias=true")
- } else {
- hasParam = true
- buf.WriteString("?columnsWithAlias=true")
- }
- }
-
- if cfg.InterpolateParams {
- if hasParam {
- buf.WriteString("&interpolateParams=true")
- } else {
- hasParam = true
- buf.WriteString("?interpolateParams=true")
- }
- }
-
- if cfg.Loc != time.UTC && cfg.Loc != nil {
- if hasParam {
- buf.WriteString("&loc=")
- } else {
- hasParam = true
- buf.WriteString("?loc=")
- }
- buf.WriteString(url.QueryEscape(cfg.Loc.String()))
- }
-
- if cfg.MultiStatements {
- if hasParam {
- buf.WriteString("&multiStatements=true")
- } else {
- hasParam = true
- buf.WriteString("?multiStatements=true")
- }
- }
-
- if cfg.ParseTime {
- if hasParam {
- buf.WriteString("&parseTime=true")
- } else {
- hasParam = true
- buf.WriteString("?parseTime=true")
- }
- }
-
- if cfg.ReadTimeout > 0 {
- if hasParam {
- buf.WriteString("&readTimeout=")
- } else {
- hasParam = true
- buf.WriteString("?readTimeout=")
- }
- buf.WriteString(cfg.ReadTimeout.String())
- }
-
- if cfg.Strict {
- if hasParam {
- buf.WriteString("&strict=true")
- } else {
- hasParam = true
- buf.WriteString("?strict=true")
- }
- }
-
- if cfg.Timeout > 0 {
- if hasParam {
- buf.WriteString("&timeout=")
- } else {
- hasParam = true
- buf.WriteString("?timeout=")
- }
- buf.WriteString(cfg.Timeout.String())
- }
-
- if len(cfg.TLSConfig) > 0 {
- if hasParam {
- buf.WriteString("&tls=")
- } else {
- hasParam = true
- buf.WriteString("?tls=")
- }
- buf.WriteString(url.QueryEscape(cfg.TLSConfig))
- }
-
- if cfg.WriteTimeout > 0 {
- if hasParam {
- buf.WriteString("&writeTimeout=")
- } else {
- hasParam = true
- buf.WriteString("?writeTimeout=")
- }
- buf.WriteString(cfg.WriteTimeout.String())
- }
-
- // other params
- if cfg.Params != nil {
- for param, value := range cfg.Params {
- if hasParam {
- buf.WriteByte('&')
- } else {
- hasParam = true
- buf.WriteByte('?')
- }
-
- buf.WriteString(param)
- buf.WriteByte('=')
- buf.WriteString(url.QueryEscape(value))
- }
- }
-
- return buf.String()
-}
-
-// ParseDSN parses the DSN string to a Config
-func ParseDSN(dsn string) (cfg *Config, err error) {
- // New config with some default values
- cfg = &Config{
- Loc: time.UTC,
- Collation: defaultCollation,
- }
-
- // [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
- // Find the last '/' (since the password or the net addr might contain a '/')
- foundSlash := false
- for i := len(dsn) - 1; i >= 0; i-- {
- if dsn[i] == '/' {
- foundSlash = true
- var j, k int
-
- // left part is empty if i <= 0
- if i > 0 {
- // [username[:password]@][protocol[(address)]]
- // Find the last '@' in dsn[:i]
- for j = i; j >= 0; j-- {
- if dsn[j] == '@' {
- // username[:password]
- // Find the first ':' in dsn[:j]
- for k = 0; k < j; k++ {
- if dsn[k] == ':' {
- cfg.Passwd = dsn[k+1 : j]
- break
- }
- }
- cfg.User = dsn[:k]
-
- break
- }
- }
-
- // [protocol[(address)]]
- // Find the first '(' in dsn[j+1:i]
- for k = j + 1; k < i; k++ {
- if dsn[k] == '(' {
- // dsn[i-1] must be == ')' if an address is specified
- if dsn[i-1] != ')' {
- if strings.ContainsRune(dsn[k+1:i], ')') {
- return nil, errInvalidDSNUnescaped
- }
- return nil, errInvalidDSNAddr
- }
- cfg.Addr = dsn[k+1 : i-1]
- break
- }
- }
- cfg.Net = dsn[j+1 : k]
- }
-
- // dbname[?param1=value1&...&paramN=valueN]
- // Find the first '?' in dsn[i+1:]
- for j = i + 1; j < len(dsn); j++ {
- if dsn[j] == '?' {
- if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
- return
- }
- break
- }
- }
- cfg.DBName = dsn[i+1 : j]
-
- break
- }
- }
-
- if !foundSlash && len(dsn) > 0 {
- return nil, errInvalidDSNNoSlash
- }
-
- if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
- return nil, errInvalidDSNUnsafeCollation
- }
-
- // Set default network if empty
- if cfg.Net == "" {
- cfg.Net = "tcp"
- }
-
- // Set default address if empty
- if cfg.Addr == "" {
- switch cfg.Net {
- case "tcp":
- cfg.Addr = "127.0.0.1:3306"
- case "unix":
- cfg.Addr = "/tmp/mysql.sock"
- default:
- return nil, errors.New("default addr for network '" + cfg.Net + "' unknown")
- }
-
- }
-
- return
-}
-
-// parseDSNParams parses the DSN "query string"
-// Values must be url.QueryEscape'ed
-func parseDSNParams(cfg *Config, params string) (err error) {
- for _, v := range strings.Split(params, "&") {
- param := strings.SplitN(v, "=", 2)
- if len(param) != 2 {
- continue
- }
-
- // cfg params
- switch value := param[1]; param[0] {
-
- // Disable INFILE whitelist / enable all files
- case "allowAllFiles":
- var isBool bool
- cfg.AllowAllFiles, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // Use cleartext authentication mode (MySQL 5.5.10+)
- case "allowCleartextPasswords":
- var isBool bool
- cfg.AllowCleartextPasswords, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // Use old authentication mode (pre MySQL 4.1)
- case "allowOldPasswords":
- var isBool bool
- cfg.AllowOldPasswords, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // Switch "rowsAffected" mode
- case "clientFoundRows":
- var isBool bool
- cfg.ClientFoundRows, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // Collation
- case "collation":
- cfg.Collation = value
- break
-
- case "columnsWithAlias":
- var isBool bool
- cfg.ColumnsWithAlias, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // Compression
- case "compress":
- return errors.New("compression not implemented yet")
-
- // Enable client side placeholder substitution
- case "interpolateParams":
- var isBool bool
- cfg.InterpolateParams, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // Time Location
- case "loc":
- if value, err = url.QueryUnescape(value); err != nil {
- return
- }
- cfg.Loc, err = time.LoadLocation(value)
- if err != nil {
- return
- }
-
- // multiple statements in one query
- case "multiStatements":
- var isBool bool
- cfg.MultiStatements, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // time.Time parsing
- case "parseTime":
- var isBool bool
- cfg.ParseTime, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // I/O read Timeout
- case "readTimeout":
- cfg.ReadTimeout, err = time.ParseDuration(value)
- if err != nil {
- return
- }
-
- // Strict mode
- case "strict":
- var isBool bool
- cfg.Strict, isBool = readBool(value)
- if !isBool {
- return errors.New("invalid bool value: " + value)
- }
-
- // Dial Timeout
- case "timeout":
- cfg.Timeout, err = time.ParseDuration(value)
- if err != nil {
- return
- }
-
- // TLS-Encryption
- case "tls":
- boolValue, isBool := readBool(value)
- if isBool {
- if boolValue {
- cfg.TLSConfig = "true"
- cfg.tls = &tls.Config{}
- } else {
- cfg.TLSConfig = "false"
- }
- } else if vl := strings.ToLower(value); vl == "skip-verify" {
- cfg.TLSConfig = vl
- cfg.tls = &tls.Config{InsecureSkipVerify: true}
- } else {
- name, err := url.QueryUnescape(value)
- if err != nil {
- return fmt.Errorf("invalid value for TLS config name: %v", err)
- }
-
- if tlsConfig, ok := tlsConfigRegister[name]; ok {
- if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
- host, _, err := net.SplitHostPort(cfg.Addr)
- if err == nil {
- tlsConfig.ServerName = host
- }
- }
-
- cfg.TLSConfig = name
- cfg.tls = tlsConfig
- } else {
- return errors.New("invalid value / unknown config name: " + name)
- }
- }
-
- // I/O write Timeout
- case "writeTimeout":
- cfg.WriteTimeout, err = time.ParseDuration(value)
- if err != nil {
- return
- }
-
- default:
- // lazy init
- if cfg.Params == nil {
- cfg.Params = make(map[string]string)
- }
-
- if cfg.Params[param[0]], err = url.QueryUnescape(value); err != nil {
- return
- }
- }
- }
-
- return
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/dsn_test.go b/vendor/github.com/go-sql-driver/mysql/dsn_test.go
deleted file mode 100644
index 80949e18a..000000000
--- a/vendor/github.com/go-sql-driver/mysql/dsn_test.go
+++ /dev/null
@@ -1,207 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-package mysql
-
-import (
- "crypto/tls"
- "fmt"
- "net/url"
- "testing"
-)
-
-var testDSNs = []struct {
- in string
- out string
-}{
- {"username:password@protocol(address)/dbname?param=value", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:true ParseTime:false Strict:false}"},
- {"user@unix(/path/to/socket)/dbname?charset=utf8", "&{User:user Passwd: Net:unix Addr:/path/to/socket DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:true tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8mb4,utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:skip-verify tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{User:user Passwd:password Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8mb4_unicode_ci Loc:UTC TLSConfig: tls:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{User:user Passwd:p@ss(word) Net:tcp Addr:[de:ad:be:ef::ca:fe]:80 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:Local TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"user:p@/ssword@/", "&{User:user Passwd:p@/ssword Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
- {"unix/?arg=%2Fsome%2Fpath.ext", "&{User: Passwd: Net:unix Addr:/tmp/mysql.sock DBName: Params:map[arg:/some/path.ext] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
-}
-
-func TestDSNParser(t *testing.T) {
- var cfg *Config
- var err error
- var res string
-
- for i, tst := range testDSNs {
- cfg, err = ParseDSN(tst.in)
- if err != nil {
- t.Error(err.Error())
- }
-
- // pointer not static
- cfg.tls = nil
-
- res = fmt.Sprintf("%+v", cfg)
- if res != tst.out {
- t.Errorf("%d. ParseDSN(%q) => %q, want %q", i, tst.in, res, tst.out)
- }
- }
-}
-
-func TestDSNParserInvalid(t *testing.T) {
- var invalidDSNs = []string{
- "@net(addr/", // no closing brace
- "@tcp(/", // no closing brace
- "tcp(/", // no closing brace
- "(/", // no closing brace
- "net(addr)//", // unescaped
- "User:pass@tcp(1.2.3.4:3306)", // no trailing slash
- //"/dbname?arg=/some/unescaped/path",
- }
-
- for i, tst := range invalidDSNs {
- if _, err := ParseDSN(tst); err == nil {
- t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
- }
- }
-}
-
-func TestDSNReformat(t *testing.T) {
- for i, tst := range testDSNs {
- dsn1 := tst.in
- cfg1, err := ParseDSN(dsn1)
- if err != nil {
- t.Error(err.Error())
- continue
- }
- cfg1.tls = nil // pointer not static
- res1 := fmt.Sprintf("%+v", cfg1)
-
- dsn2 := cfg1.FormatDSN()
- cfg2, err := ParseDSN(dsn2)
- if err != nil {
- t.Error(err.Error())
- continue
- }
- cfg2.tls = nil // pointer not static
- res2 := fmt.Sprintf("%+v", cfg2)
-
- if res1 != res2 {
- t.Errorf("%d. %q does not match %q", i, res2, res1)
- }
- }
-}
-
-func TestDSNWithCustomTLS(t *testing.T) {
- baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
- tlsCfg := tls.Config{}
-
- RegisterTLSConfig("utils_test", &tlsCfg)
-
- // Custom TLS is missing
- tst := baseDSN + "invalid_tls"
- cfg, err := ParseDSN(tst)
- if err == nil {
- t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
- }
-
- tst = baseDSN + "utils_test"
-
- // Custom TLS with a server name
- name := "foohost"
- tlsCfg.ServerName = name
- cfg, err = ParseDSN(tst)
-
- if err != nil {
- t.Error(err.Error())
- } else if cfg.tls.ServerName != name {
- t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
- }
-
- // Custom TLS without a server name
- name = "localhost"
- tlsCfg.ServerName = ""
- cfg, err = ParseDSN(tst)
-
- if err != nil {
- t.Error(err.Error())
- } else if cfg.tls.ServerName != name {
- t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
- }
-
- DeregisterTLSConfig("utils_test")
-}
-
-func TestDSNWithCustomTLSQueryEscape(t *testing.T) {
- const configKey = "&%!:"
- dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
- name := "foohost"
- tlsCfg := tls.Config{ServerName: name}
-
- RegisterTLSConfig(configKey, &tlsCfg)
-
- cfg, err := ParseDSN(dsn)
-
- if err != nil {
- t.Error(err.Error())
- } else if cfg.tls.ServerName != name {
- t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn)
- }
-}
-
-func TestDSNUnsafeCollation(t *testing.T) {
- _, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
- if err != errInvalidDSNUnsafeCollation {
- t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err)
- }
-
- _, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
- if err != nil {
- t.Errorf("expected %v, got %v", nil, err)
- }
-
- _, err = ParseDSN("/dbname?collation=gbk_chinese_ci")
- if err != nil {
- t.Errorf("expected %v, got %v", nil, err)
- }
-
- _, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
- if err != nil {
- t.Errorf("expected %v, got %v", nil, err)
- }
-
- _, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
- if err != nil {
- t.Errorf("expected %v, got %v", nil, err)
- }
-
- _, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
- if err != nil {
- t.Errorf("expected %v, got %v", nil, err)
- }
-
- _, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
- if err != nil {
- t.Errorf("expected %v, got %v", nil, err)
- }
-}
-
-func BenchmarkParseDSN(b *testing.B) {
- b.ReportAllocs()
-
- for i := 0; i < b.N; i++ {
- for _, tst := range testDSNs {
- if _, err := ParseDSN(tst.in); err != nil {
- b.Error(err.Error())
- }
- }
- }
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/errors.go b/vendor/github.com/go-sql-driver/mysql/errors.go
index 1543a8054..97d7b3996 100644
--- a/vendor/github.com/go-sql-driver/mysql/errors.go
+++ b/vendor/github.com/go-sql-driver/mysql/errors.go
@@ -19,20 +19,18 @@ import (
// Various errors the driver might return. Can change between driver versions.
var (
- ErrInvalidConn = errors.New("invalid connection")
- ErrMalformPkt = errors.New("malformed packet")
- ErrNoTLS = errors.New("TLS requested but server does not support TLS")
- ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
- ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
- ErrUnknownPlugin = errors.New("this authentication plugin is not supported")
- ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+")
- ErrPktSync = errors.New("commands out of sync. You can't run this command now")
- ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?")
- ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server")
- ErrBusyBuffer = errors.New("busy buffer")
+ ErrInvalidConn = errors.New("Invalid Connection")
+ ErrMalformPkt = errors.New("Malformed Packet")
+ ErrNoTLS = errors.New("TLS encryption requested but server does not support TLS")
+ ErrOldPassword = errors.New("This server only supports the insecure old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
+ ErrOldProtocol = errors.New("MySQL-Server does not support required Protocol 41+")
+ ErrPktSync = errors.New("Commands out of sync. You can't run this command now")
+ ErrPktSyncMul = errors.New("Commands out of sync. Did you run multiple statements at once?")
+ ErrPktTooLarge = errors.New("Packet for query is too large. You can change this value on the server by adjusting the 'max_allowed_packet' variable.")
+ ErrBusyBuffer = errors.New("Busy buffer")
)
-var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
+var errLog Logger = log.New(os.Stderr, "[MySQL] ", log.Ldate|log.Ltime|log.Lshortfile)
// Logger is used to log critical error messages.
type Logger interface {
diff --git a/vendor/github.com/go-sql-driver/mysql/infile.go b/vendor/github.com/go-sql-driver/mysql/infile.go
index 0f975bbc2..121a04c71 100644
--- a/vendor/github.com/go-sql-driver/mysql/infile.go
+++ b/vendor/github.com/go-sql-driver/mysql/infile.go
@@ -13,14 +13,11 @@ import (
"io"
"os"
"strings"
- "sync"
)
var (
- fileRegister map[string]bool
- fileRegisterLock sync.RWMutex
- readerRegister map[string]func() io.Reader
- readerRegisterLock sync.RWMutex
+ fileRegister map[string]bool
+ readerRegister map[string]func() io.Reader
)
// RegisterLocalFile adds the given file to the file whitelist,
@@ -35,21 +32,17 @@ var (
// ...
//
func RegisterLocalFile(filePath string) {
- fileRegisterLock.Lock()
// lazy map init
if fileRegister == nil {
fileRegister = make(map[string]bool)
}
fileRegister[strings.Trim(filePath, `"`)] = true
- fileRegisterLock.Unlock()
}
// DeregisterLocalFile removes the given filepath from the whitelist.
func DeregisterLocalFile(filePath string) {
- fileRegisterLock.Lock()
delete(fileRegister, strings.Trim(filePath, `"`))
- fileRegisterLock.Unlock()
}
// RegisterReaderHandler registers a handler function which is used
@@ -68,22 +61,18 @@ func DeregisterLocalFile(filePath string) {
// ...
//
func RegisterReaderHandler(name string, handler func() io.Reader) {
- readerRegisterLock.Lock()
// lazy map init
if readerRegister == nil {
readerRegister = make(map[string]func() io.Reader)
}
readerRegister[name] = handler
- readerRegisterLock.Unlock()
}
// DeregisterReaderHandler removes the ReaderHandler function with
// the given name from the registry.
func DeregisterReaderHandler(name string) {
- readerRegisterLock.Lock()
delete(readerRegister, name)
- readerRegisterLock.Unlock()
}
func deferredClose(err *error, closer io.Closer) {
@@ -96,22 +85,14 @@ func deferredClose(err *error, closer io.Closer) {
func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
var rdr io.Reader
var data []byte
- packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
- if mc.maxWriteSize < packetSize {
- packetSize = mc.maxWriteSize
- }
-
- if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
- // The server might return an an absolute path. See issue #355.
- name = name[idx+8:]
-
- readerRegisterLock.RLock()
- handler, inMap := readerRegister[name]
- readerRegisterLock.RUnlock()
- if inMap {
+ if strings.HasPrefix(name, "Reader::") { // io.Reader
+ name = name[8:]
+ if handler, inMap := readerRegister[name]; inMap {
rdr = handler()
if rdr != nil {
+ data = make([]byte, 4+mc.maxWriteSize)
+
if cl, ok := rdr.(io.Closer); ok {
defer deferredClose(&err, cl)
}
@@ -123,10 +104,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
}
} else { // File
name = strings.Trim(name, `"`)
- fileRegisterLock.RLock()
- fr := fileRegister[name]
- fileRegisterLock.RUnlock()
- if mc.cfg.AllowAllFiles || fr {
+ if mc.cfg.allowAllFiles || fileRegister[name] {
var file *os.File
var fi os.FileInfo
@@ -136,19 +114,22 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
// get file size
if fi, err = file.Stat(); err == nil {
rdr = file
- if fileSize := int(fi.Size()); fileSize < packetSize {
- packetSize = fileSize
+ if fileSize := int(fi.Size()); fileSize <= mc.maxWriteSize {
+ data = make([]byte, 4+fileSize)
+ } else if fileSize <= mc.maxPacketAllowed {
+ data = make([]byte, 4+mc.maxWriteSize)
+ } else {
+ err = fmt.Errorf("Local File '%s' too large: Size: %d, Max: %d", name, fileSize, mc.maxPacketAllowed)
}
}
}
} else {
- err = fmt.Errorf("local file '%s' is not registered", name)
+ err = fmt.Errorf("Local File '%s' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files", name)
}
}
// send content packets
if err == nil {
- data := make([]byte, 4+packetSize)
var n int
for err == nil {
n, err = rdr.Read(data[4:])
@@ -174,8 +155,8 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
// read OK packet
if err == nil {
return mc.readResultOK()
+ } else {
+ mc.readPacket()
}
-
- mc.readPacket()
return err
}
diff --git a/vendor/github.com/go-sql-driver/mysql/packets.go b/vendor/github.com/go-sql-driver/mysql/packets.go
index 8d9166578..618098146 100644
--- a/vendor/github.com/go-sql-driver/mysql/packets.go
+++ b/vendor/github.com/go-sql-driver/mysql/packets.go
@@ -13,7 +13,6 @@ import (
"crypto/tls"
"database/sql/driver"
"encoding/binary"
- "errors"
"fmt"
"io"
"math"
@@ -48,8 +47,9 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
if data[3] != mc.sequence {
if data[3] > mc.sequence {
return nil, ErrPktSyncMul
+ } else {
+ return nil, ErrPktSync
}
- return nil, ErrPktSync
}
mc.sequence++
@@ -100,12 +100,6 @@ func (mc *mysqlConn) writePacket(data []byte) error {
data[3] = mc.sequence
// Write packet
- if mc.writeTimeout > 0 {
- if err := mc.netConn.SetWriteDeadline(time.Now().Add(mc.writeTimeout)); err != nil {
- return err
- }
- }
-
n, err := mc.netConn.Write(data[:4+size])
if err == nil && n == 4+size {
mc.sequence++
@@ -146,7 +140,7 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) {
// protocol version [1 byte]
if data[0] < minProtocolVersion {
return nil, fmt.Errorf(
- "unsupported protocol version %d. Version %d or higher is required",
+ "Unsupported MySQL Protocol Version %d. Protocol Version %d or higher is required",
data[0],
minProtocolVersion,
)
@@ -202,11 +196,7 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) {
// return
//}
//return ErrMalformPkt
-
- // make a memory safe copy of the cipher slice
- var b [20]byte
- copy(b[:], cipher)
- return b[:], nil
+ return cipher, nil
}
// make a memory safe copy of the cipher slice
@@ -224,11 +214,9 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
clientLongPassword |
clientTransactions |
clientLocalFiles |
- clientPluginAuth |
- clientMultiResults |
mc.flags&clientLongFlag
- if mc.cfg.ClientFoundRows {
+ if mc.cfg.clientFoundRows {
clientFlags |= clientFoundRows
}
@@ -237,17 +225,13 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
clientFlags |= clientSSL
}
- if mc.cfg.MultiStatements {
- clientFlags |= clientMultiStatements
- }
-
// User Password
- scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))
+ scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.passwd))
- pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + 1 + len(scrambleBuff) + 21 + 1
+ pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.user) + 1 + 1 + len(scrambleBuff)
// To specify a db name
- if n := len(mc.cfg.DBName); n > 0 {
+ if n := len(mc.cfg.dbname); n > 0 {
clientFlags |= clientConnectWithDB
pktLen += n + 1
}
@@ -273,14 +257,7 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
data[11] = 0x00
// Charset [1 byte]
- var found bool
- data[12], found = collations[mc.cfg.Collation]
- if !found {
- // Note possibility for false negatives:
- // could be triggered although the collation is valid if the
- // collations map does not contain entries the server supports.
- return errors.New("unknown collation")
- }
+ data[12] = mc.cfg.collation
// SSL Connection Request Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
@@ -296,18 +273,15 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
return err
}
mc.netConn = tlsConn
- mc.buf.nc = tlsConn
+ mc.buf.rd = tlsConn
}
// Filler [23 bytes] (all 0x00)
- pos := 13
- for ; pos < 13+23; pos++ {
- data[pos] = 0
- }
+ pos := 13 + 23
// User [null terminated string]
- if len(mc.cfg.User) > 0 {
- pos += copy(data[pos:], mc.cfg.User)
+ if len(mc.cfg.user) > 0 {
+ pos += copy(data[pos:], mc.cfg.user)
}
data[pos] = 0x00
pos++
@@ -317,16 +291,11 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
pos += 1 + copy(data[pos+1:], scrambleBuff)
// Databasename [null terminated string]
- if len(mc.cfg.DBName) > 0 {
- pos += copy(data[pos:], mc.cfg.DBName)
+ if len(mc.cfg.dbname) > 0 {
+ pos += copy(data[pos:], mc.cfg.dbname)
data[pos] = 0x00
- pos++
}
- // Assume native client during response
- pos += copy(data[pos:], "mysql_native_password")
- data[pos] = 0x00
-
// Send Auth packet
return mc.writePacket(data)
}
@@ -335,9 +304,9 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
// User password
- scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.Passwd))
+ scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.passwd))
- // Calculate the packet length and add a tailing 0
+ // Calculate the packet lenght and add a tailing 0
pktLen := len(scrambleBuff) + 1
data := mc.buf.takeSmallBuffer(4 + pktLen)
if data == nil {
@@ -353,25 +322,6 @@ func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
return mc.writePacket(data)
}
-// Client clear text authentication packet
-// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
-func (mc *mysqlConn) writeClearAuthPacket() error {
- // Calculate the packet length and add a tailing 0
- pktLen := len(mc.cfg.Passwd) + 1
- data := mc.buf.takeSmallBuffer(4 + pktLen)
- if data == nil {
- // can not take the buffer. Something must be wrong with the connection
- errLog.Print(ErrBusyBuffer)
- return driver.ErrBadConn
- }
-
- // Add the clear password [null terminated string]
- copy(data[4:], mc.cfg.Passwd)
- data[4+pktLen-1] = 0x00
-
- return mc.writePacket(data)
-}
-
/******************************************************************************
* Command Packets *
******************************************************************************/
@@ -455,20 +405,8 @@ func (mc *mysqlConn) readResultOK() error {
return mc.handleOkPacket(data)
case iEOF:
- if len(data) > 1 {
- plugin := string(data[1:bytes.IndexByte(data, 0x00)])
- if plugin == "mysql_old_password" {
- // using old_passwords
- return ErrOldPassword
- } else if plugin == "mysql_clear_password" {
- // using clear text password
- return ErrCleartextPassword
- } else {
- return ErrUnknownPlugin
- }
- } else {
- return ErrOldPassword
- }
+ // someone is using old_passwords
+ return ErrOldPassword
default: // Error otherwise
return mc.handleErrorPacket(data)
@@ -532,10 +470,6 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
}
}
-func readStatus(b []byte) statusFlag {
- return statusFlag(b[0]) | statusFlag(b[1])<<8
-}
-
// Ok Packet
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet
func (mc *mysqlConn) handleOkPacket(data []byte) error {
@@ -550,21 +484,17 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
mc.insertId, _, m = readLengthEncodedInteger(data[1+n:])
// server_status [2 bytes]
- mc.status = readStatus(data[1+n+m : 1+n+m+2])
- if err := mc.discardResults(); err != nil {
- return err
- }
// warning count [2 bytes]
if !mc.strict {
return nil
+ } else {
+ pos := 1 + n + m + 2
+ if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 {
+ return mc.getWarnings()
+ }
+ return nil
}
-
- pos := 1 + n + m + 2
- if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 {
- return mc.getWarnings()
- }
- return nil
}
// Read Packets as Field Packets until EOF-Packet or an Error appears
@@ -583,7 +513,7 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
if i == count {
return columns, nil
}
- return nil, fmt.Errorf("column count mismatch n:%d len:%d", count, len(columns))
+ return nil, fmt.Errorf("ColumnsCount mismatch n:%d len:%d", count, len(columns))
}
// Catalog
@@ -600,20 +530,11 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
pos += n
// Table [len coded string]
- if mc.cfg.ColumnsWithAlias {
- tableName, _, n, err := readLengthEncodedString(data[pos:])
- if err != nil {
- return nil, err
- }
- pos += n
- columns[i].tableName = string(tableName)
- } else {
- n, err = skipLengthEncodedString(data[pos:])
- if err != nil {
- return nil, err
- }
- pos += n
+ n, err = skipLengthEncodedString(data[pos:])
+ if err != nil {
+ return nil, err
}
+ pos += n
// Original table [len coded string]
n, err = skipLengthEncodedString(data[pos:])
@@ -636,21 +557,20 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
return nil, err
}
- // Filler [uint8]
- // Charset [charset, collation uint8]
- // Length [uint32]
+ // Filler [1 byte]
+ // Charset [16 bit uint]
+ // Length [32 bit uint]
pos += n + 1 + 2 + 4
- // Field type [uint8]
+ // Field type [byte]
columns[i].fieldType = data[pos]
pos++
- // Flags [uint16]
+ // Flags [16 bit uint]
columns[i].flags = fieldFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
- pos += 2
+ //pos += 2
- // Decimals [uint8]
- columns[i].decimals = data[pos]
+ // Decimals [8 bit uint]
//pos++
// Default value [len coded binary]
@@ -672,18 +592,8 @@ func (rows *textRows) readRow(dest []driver.Value) error {
// EOF Packet
if data[0] == iEOF && len(data) == 5 {
- // server_status [2 bytes]
- rows.mc.status = readStatus(data[3:])
- if err := rows.mc.discardResults(); err != nil {
- return err
- }
- rows.mc = nil
return io.EOF
}
- if data[0] == iERR {
- rows.mc = nil
- return mc.handleErrorPacket(data)
- }
// RowSet Packet
var n int
@@ -704,7 +614,7 @@ func (rows *textRows) readRow(dest []driver.Value) error {
fieldTypeDate, fieldTypeNewDate:
dest[i], err = parseDateTime(
string(dest[i].([]byte)),
- mc.cfg.Loc,
+ mc.cfg.loc,
)
if err == nil {
continue
@@ -734,10 +644,6 @@ func (mc *mysqlConn) readUntilEOF() error {
if err == nil && data[0] != iEOF {
continue
}
- if err == nil && data[0] == iEOF && len(data) == 5 {
- mc.status = readStatus(data[3:])
- }
-
return err // Err or EOF
}
}
@@ -770,13 +676,13 @@ func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
// Warning count [16 bit uint]
if !stmt.mc.strict {
return columnCount, nil
+ } else {
+ // Check for warnings count > 0, only available in MySQL > 4.1
+ if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 {
+ return columnCount, stmt.mc.getWarnings()
+ }
+ return columnCount, nil
}
-
- // Check for warnings count > 0, only available in MySQL > 4.1
- if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 {
- return columnCount, stmt.mc.getWarnings()
- }
- return columnCount, nil
}
return 0, err
}
@@ -838,7 +744,7 @@ func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error {
func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
if len(args) != stmt.paramCount {
return fmt.Errorf(
- "argument count mismatch (got: %d; has: %d)",
+ "Arguments count mismatch (Got: %d Has: %d)",
len(args),
stmt.paramCount,
)
@@ -1015,7 +921,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
if v.IsZero() {
val = []byte("0000-00-00")
} else {
- val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
+ val = []byte(v.In(mc.cfg.loc).Format(timeFormat))
}
paramValues = appendLengthEncodedInteger(paramValues,
@@ -1024,7 +930,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
paramValues = append(paramValues, val...)
default:
- return fmt.Errorf("can not convert type: %T", arg)
+ return fmt.Errorf("Can't convert type: %T", arg)
}
}
@@ -1042,28 +948,6 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
return mc.writePacket(data)
}
-func (mc *mysqlConn) discardResults() error {
- for mc.status&statusMoreResultsExists != 0 {
- resLen, err := mc.readResultSetHeaderPacket()
- if err != nil {
- return err
- }
- if resLen > 0 {
- // columns
- if err := mc.readUntilEOF(); err != nil {
- return err
- }
- // rows
- if err := mc.readUntilEOF(); err != nil {
- return err
- }
- } else {
- mc.status &^= statusMoreResultsExists
- }
- }
- return nil
-}
-
// http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html
func (rows *binaryRows) readRow(dest []driver.Value) error {
data, err := rows.mc.readPacket()
@@ -1075,14 +959,8 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
if data[0] != iOK {
// EOF Packet
if data[0] == iEOF && len(data) == 5 {
- rows.mc.status = readStatus(data[3:])
- if err := rows.mc.discardResults(); err != nil {
- return err
- }
- rows.mc = nil
return io.EOF
}
- rows.mc = nil
// Error otherwise
return rows.mc.handleErrorPacket(data)
@@ -1149,7 +1027,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
continue
case fieldTypeFloat:
- dest[i] = float32(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4])))
+ dest[i] = float64(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4])))
pos += 4
continue
@@ -1162,7 +1040,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
- fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON:
+ fieldTypeVarString, fieldTypeString, fieldTypeGeometry:
var isNull bool
var n int
dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
@@ -1177,53 +1055,88 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
}
return err
- case
- fieldTypeDate, fieldTypeNewDate, // Date YYYY-MM-DD
- fieldTypeTime, // Time [-][H]HH:MM:SS[.fractal]
- fieldTypeTimestamp, fieldTypeDateTime: // Timestamp YYYY-MM-DD HH:MM:SS[.fractal]
-
+ // Date YYYY-MM-DD
+ case fieldTypeDate, fieldTypeNewDate:
num, isNull, n := readLengthEncodedInteger(data[pos:])
pos += n
- switch {
- case isNull:
+ if isNull {
dest[i] = nil
continue
- case rows.columns[i].fieldType == fieldTypeTime:
- // database/sql does not support an equivalent to TIME, return a string
- var dstlen uint8
- switch decimals := rows.columns[i].decimals; decimals {
- case 0x00, 0x1f:
- dstlen = 8
- case 1, 2, 3, 4, 5, 6:
- dstlen = 8 + 1 + decimals
- default:
- return fmt.Errorf(
- "protocol error, illegal decimals value %d",
- rows.columns[i].decimals,
- )
- }
- dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true)
- case rows.mc.parseTime:
- dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
- default:
- var dstlen uint8
- if rows.columns[i].fieldType == fieldTypeDate {
- dstlen = 10
+ }
+
+ if rows.mc.parseTime {
+ dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.loc)
+ } else {
+ dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], false)
+ }
+
+ if err == nil {
+ pos += int(num)
+ continue
+ } else {
+ return err
+ }
+
+ // Time [-][H]HH:MM:SS[.fractal]
+ case fieldTypeTime:
+ num, isNull, n := readLengthEncodedInteger(data[pos:])
+ pos += n
+
+ if num == 0 {
+ if isNull {
+ dest[i] = nil
+ continue
} else {
- switch decimals := rows.columns[i].decimals; decimals {
- case 0x00, 0x1f:
- dstlen = 19
- case 1, 2, 3, 4, 5, 6:
- dstlen = 19 + 1 + decimals
- default:
- return fmt.Errorf(
- "protocol error, illegal decimals value %d",
- rows.columns[i].decimals,
- )
- }
+ dest[i] = []byte("00:00:00")
+ continue
}
- dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, false)
+ }
+
+ var sign string
+ if data[pos] == 1 {
+ sign = "-"
+ }
+
+ switch num {
+ case 8:
+ dest[i] = []byte(fmt.Sprintf(
+ sign+"%02d:%02d:%02d",
+ uint16(data[pos+1])*24+uint16(data[pos+5]),
+ data[pos+6],
+ data[pos+7],
+ ))
+ pos += 8
+ continue
+ case 12:
+ dest[i] = []byte(fmt.Sprintf(
+ sign+"%02d:%02d:%02d.%06d",
+ uint16(data[pos+1])*24+uint16(data[pos+5]),
+ data[pos+6],
+ data[pos+7],
+ binary.LittleEndian.Uint32(data[pos+8:pos+12]),
+ ))
+ pos += 12
+ continue
+ default:
+ return fmt.Errorf("Invalid TIME-packet length %d", num)
+ }
+
+ // Timestamp YYYY-MM-DD HH:MM:SS[.fractal]
+ case fieldTypeTimestamp, fieldTypeDateTime:
+ num, isNull, n := readLengthEncodedInteger(data[pos:])
+
+ pos += n
+
+ if isNull {
+ dest[i] = nil
+ continue
+ }
+
+ if rows.mc.parseTime {
+ dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.loc)
+ } else {
+ dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], true)
}
if err == nil {
@@ -1235,7 +1148,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
// Please report if this happens!
default:
- return fmt.Errorf("unknown field type %d", rows.columns[i].fieldType)
+ return fmt.Errorf("Unknown FieldType %d", rows.columns[i].fieldType)
}
}
diff --git a/vendor/github.com/go-sql-driver/mysql/rows.go b/vendor/github.com/go-sql-driver/mysql/rows.go
index c08255eee..df4ef06cb 100644
--- a/vendor/github.com/go-sql-driver/mysql/rows.go
+++ b/vendor/github.com/go-sql-driver/mysql/rows.go
@@ -14,11 +14,9 @@ import (
)
type mysqlField struct {
- tableName string
- name string
- flags fieldFlag
fieldType byte
- decimals byte
+ flags fieldFlag
+ name string
}
type mysqlRows struct {
@@ -34,22 +32,10 @@ type textRows struct {
mysqlRows
}
-type emptyRows struct{}
-
func (rows *mysqlRows) Columns() []string {
columns := make([]string, len(rows.columns))
- if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias {
- for i := range columns {
- if tableName := rows.columns[i].tableName; len(tableName) > 0 {
- columns[i] = tableName + "." + rows.columns[i].name
- } else {
- columns[i] = rows.columns[i].name
- }
- }
- } else {
- for i := range columns {
- columns[i] = rows.columns[i].name
- }
+ for i := range columns {
+ columns[i] = rows.columns[i].name
}
return columns
}
@@ -65,12 +51,6 @@ func (rows *mysqlRows) Close() error {
// Remove unread packets from stream
err := mc.readUntilEOF()
- if err == nil {
- if err = mc.discardResults(); err != nil {
- return err
- }
- }
-
rows.mc = nil
return err
}
@@ -82,7 +62,10 @@ func (rows *binaryRows) Next(dest []driver.Value) error {
}
// Fetch next row from stream
- return rows.readRow(dest)
+ if err := rows.readRow(dest); err != io.EOF {
+ return err
+ }
+ rows.mc = nil
}
return io.EOF
}
@@ -94,19 +77,10 @@ func (rows *textRows) Next(dest []driver.Value) error {
}
// Fetch next row from stream
- return rows.readRow(dest)
+ if err := rows.readRow(dest); err != io.EOF {
+ return err
+ }
+ rows.mc = nil
}
return io.EOF
}
-
-func (rows emptyRows) Columns() []string {
- return nil
-}
-
-func (rows emptyRows) Close() error {
- return nil
-}
-
-func (rows emptyRows) Next(dest []driver.Value) error {
- return io.EOF
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/statement.go b/vendor/github.com/go-sql-driver/mysql/statement.go
index ead9a6bf4..142ef5416 100644
--- a/vendor/github.com/go-sql-driver/mysql/statement.go
+++ b/vendor/github.com/go-sql-driver/mysql/statement.go
@@ -10,9 +10,6 @@ package mysql
import (
"database/sql/driver"
- "fmt"
- "reflect"
- "strconv"
)
type mysqlStmt struct {
@@ -37,10 +34,6 @@ func (stmt *mysqlStmt) NumInput() int {
return stmt.paramCount
}
-func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
- return converter{}
-}
-
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
if stmt.mc.netConn == nil {
errLog.Print(ErrInvalidConn)
@@ -101,9 +94,9 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
}
rows := new(binaryRows)
+ rows.mc = mc
if resLen > 0 {
- rows.mc = mc
// Columns
// If not cached, read them and cache them
if stmt.columns == nil {
@@ -117,34 +110,3 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
return rows, err
}
-
-type converter struct{}
-
-func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
- if driver.IsValue(v) {
- return v, nil
- }
-
- rv := reflect.ValueOf(v)
- switch rv.Kind() {
- case reflect.Ptr:
- // indirect pointers
- if rv.IsNil() {
- return nil, nil
- }
- return c.ConvertValue(rv.Elem().Interface())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return rv.Int(), nil
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
- return int64(rv.Uint()), nil
- case reflect.Uint64:
- u64 := rv.Uint()
- if u64 >= 1<<63 {
- return strconv.FormatUint(u64, 10), nil
- }
- return int64(u64), nil
- case reflect.Float32, reflect.Float64:
- return rv.Float(), nil
- }
- return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/utils.go b/vendor/github.com/go-sql-driver/mysql/utils.go
index d523b7ffd..b6f200389 100644
--- a/vendor/github.com/go-sql-driver/mysql/utils.go
+++ b/vendor/github.com/go-sql-driver/mysql/utils.go
@@ -13,16 +13,26 @@ import (
"crypto/tls"
"database/sql/driver"
"encoding/binary"
+ "errors"
"fmt"
"io"
+ "net/url"
"strings"
"time"
)
var (
tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
+
+ errInvalidDSNUnescaped = errors.New("Invalid DSN: Did you forget to escape a param value?")
+ errInvalidDSNAddr = errors.New("Invalid DSN: Network Address not terminated (missing closing brace)")
+ errInvalidDSNNoSlash = errors.New("Invalid DSN: Missing the slash separating the database name")
)
+func init() {
+ tlsConfigRegister = make(map[string]*tls.Config)
+}
+
// RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
// Use the key as a value in the DSN where tls=value.
//
@@ -48,11 +58,7 @@ var (
//
func RegisterTLSConfig(key string, config *tls.Config) error {
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" {
- return fmt.Errorf("key '%s' is reserved", key)
- }
-
- if tlsConfigRegister == nil {
- tlsConfigRegister = make(map[string]*tls.Config)
+ return fmt.Errorf("Key '%s' is reserved", key)
}
tlsConfigRegister[key] = config
@@ -61,9 +67,202 @@ func RegisterTLSConfig(key string, config *tls.Config) error {
// DeregisterTLSConfig removes the tls.Config associated with key.
func DeregisterTLSConfig(key string) {
- if tlsConfigRegister != nil {
- delete(tlsConfigRegister, key)
+ delete(tlsConfigRegister, key)
+}
+
+// parseDSN parses the DSN string to a config
+func parseDSN(dsn string) (cfg *config, err error) {
+ // New config with some default values
+ cfg = &config{
+ loc: time.UTC,
+ collation: defaultCollation,
+ }
+
+ // TODO: use strings.IndexByte when we can depend on Go 1.2
+
+ // [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
+ // Find the last '/' (since the password or the net addr might contain a '/')
+ foundSlash := false
+ for i := len(dsn) - 1; i >= 0; i-- {
+ if dsn[i] == '/' {
+ foundSlash = true
+ var j, k int
+
+ // left part is empty if i <= 0
+ if i > 0 {
+ // [username[:password]@][protocol[(address)]]
+ // Find the last '@' in dsn[:i]
+ for j = i; j >= 0; j-- {
+ if dsn[j] == '@' {
+ // username[:password]
+ // Find the first ':' in dsn[:j]
+ for k = 0; k < j; k++ {
+ if dsn[k] == ':' {
+ cfg.passwd = dsn[k+1 : j]
+ break
+ }
+ }
+ cfg.user = dsn[:k]
+
+ break
+ }
+ }
+
+ // [protocol[(address)]]
+ // Find the first '(' in dsn[j+1:i]
+ for k = j + 1; k < i; k++ {
+ if dsn[k] == '(' {
+ // dsn[i-1] must be == ')' if an address is specified
+ if dsn[i-1] != ')' {
+ if strings.ContainsRune(dsn[k+1:i], ')') {
+ return nil, errInvalidDSNUnescaped
+ }
+ return nil, errInvalidDSNAddr
+ }
+ cfg.addr = dsn[k+1 : i-1]
+ break
+ }
+ }
+ cfg.net = dsn[j+1 : k]
+ }
+
+ // dbname[?param1=value1&...&paramN=valueN]
+ // Find the first '?' in dsn[i+1:]
+ for j = i + 1; j < len(dsn); j++ {
+ if dsn[j] == '?' {
+ if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
+ return
+ }
+ break
+ }
+ }
+ cfg.dbname = dsn[i+1 : j]
+
+ break
+ }
+ }
+
+ if !foundSlash && len(dsn) > 0 {
+ return nil, errInvalidDSNNoSlash
+ }
+
+ // Set default network if empty
+ if cfg.net == "" {
+ cfg.net = "tcp"
+ }
+
+ // Set default address if empty
+ if cfg.addr == "" {
+ switch cfg.net {
+ case "tcp":
+ cfg.addr = "127.0.0.1:3306"
+ case "unix":
+ cfg.addr = "/tmp/mysql.sock"
+ default:
+ return nil, errors.New("Default addr for network '" + cfg.net + "' unknown")
+ }
+
+ }
+
+ return
+}
+
+// parseDSNParams parses the DSN "query string"
+// Values must be url.QueryEscape'ed
+func parseDSNParams(cfg *config, params string) (err error) {
+ for _, v := range strings.Split(params, "&") {
+ param := strings.SplitN(v, "=", 2)
+ if len(param) != 2 {
+ continue
+ }
+
+ // cfg params
+ switch value := param[1]; param[0] {
+
+ // Disable INFILE whitelist / enable all files
+ case "allowAllFiles":
+ var isBool bool
+ cfg.allowAllFiles, isBool = readBool(value)
+ if !isBool {
+ return fmt.Errorf("Invalid Bool value: %s", value)
+ }
+
+ // Use old authentication mode (pre MySQL 4.1)
+ case "allowOldPasswords":
+ var isBool bool
+ cfg.allowOldPasswords, isBool = readBool(value)
+ if !isBool {
+ return fmt.Errorf("Invalid Bool value: %s", value)
+ }
+
+ // Switch "rowsAffected" mode
+ case "clientFoundRows":
+ var isBool bool
+ cfg.clientFoundRows, isBool = readBool(value)
+ if !isBool {
+ return fmt.Errorf("Invalid Bool value: %s", value)
+ }
+
+ // Collation
+ case "collation":
+ collation, ok := collations[value]
+ if !ok {
+ // Note possibility for false negatives:
+ // could be triggered although the collation is valid if the
+ // collations map does not contain entries the server supports.
+ err = errors.New("unknown collation")
+ return
+ }
+ cfg.collation = collation
+ break
+
+ // Time Location
+ case "loc":
+ if value, err = url.QueryUnescape(value); err != nil {
+ return
+ }
+ cfg.loc, err = time.LoadLocation(value)
+ if err != nil {
+ return
+ }
+
+ // Dial Timeout
+ case "timeout":
+ cfg.timeout, err = time.ParseDuration(value)
+ if err != nil {
+ return
+ }
+
+ // TLS-Encryption
+ case "tls":
+ boolValue, isBool := readBool(value)
+ if isBool {
+ if boolValue {
+ cfg.tls = &tls.Config{}
+ }
+ } else {
+ if strings.ToLower(value) == "skip-verify" {
+ cfg.tls = &tls.Config{InsecureSkipVerify: true}
+ } else if tlsConfig, ok := tlsConfigRegister[value]; ok {
+ cfg.tls = tlsConfig
+ } else {
+ return fmt.Errorf("Invalid value / unknown config name: %s", value)
+ }
+ }
+
+ default:
+ // lazy init
+ if cfg.params == nil {
+ cfg.params = make(map[string]string)
+ }
+
+ if cfg.params[param[0]], err = url.QueryUnescape(value); err != nil {
+ return
+ }
+ }
}
+
+ return
}
// Returns the bool value of the input.
@@ -252,15 +451,19 @@ func (nt NullTime) Value() (driver.Value, error) {
}
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
- base := "0000-00-00 00:00:00.0000000"
switch len(str) {
- case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
- if str == base[:len(str)] {
+ case 10: // YYYY-MM-DD
+ if str == "0000-00-00" {
return
}
- t, err = time.Parse(timeFormat[:len(str)], str)
+ t, err = time.Parse(timeFormat[:10], str)
+ case 19: // YYYY-MM-DD HH:MM:SS
+ if str == "0000-00-00 00:00:00" {
+ return
+ }
+ t, err = time.Parse(timeFormat, str)
default:
- err = fmt.Errorf("invalid time string: %s", str)
+ err = fmt.Errorf("Invalid Time-String: %s", str)
return
}
@@ -309,151 +512,87 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
loc,
), nil
}
- return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
+ return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num)
}
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation
// if the DATE or DATETIME has the zero value.
// It must never be changed.
// The current behavior depends on database/sql copying the result.
-var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
-
-const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
-const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+var zeroDateTime = []byte("0000-00-00 00:00:00")
-func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) {
- // length expects the deterministic length of the zero value,
- // negative time and 100+ hours are automatically added if needed
+func formatBinaryDateTime(src []byte, withTime bool) (driver.Value, error) {
if len(src) == 0 {
- if justTime {
- return zeroDateTime[11 : 11+length], nil
+ if withTime {
+ return zeroDateTime, nil
}
- return zeroDateTime[:length], nil
- }
- var dst []byte // return value
- var pt, p1, p2, p3 byte // current digit pair
- var zOffs byte // offset of value in zeroDateTime
- if justTime {
- switch length {
- case
- 8, // time (can be up to 10 when negative and 100+ hours)
- 10, 11, 12, 13, 14, 15: // time with fractional seconds
- default:
- return nil, fmt.Errorf("illegal TIME length %d", length)
- }
- switch len(src) {
- case 8, 12:
- default:
- return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
- }
- // +2 to enable negative time and 100+ hours
- dst = make([]byte, 0, length+2)
- if src[0] == 1 {
- dst = append(dst, '-')
- }
- if src[1] != 0 {
- hour := uint16(src[1])*24 + uint16(src[5])
- pt = byte(hour / 100)
- p1 = byte(hour - 100*uint16(pt))
- dst = append(dst, digits01[pt])
+ return zeroDateTime[:10], nil
+ }
+ var dst []byte
+ if withTime {
+ if len(src) == 11 {
+ dst = []byte("0000-00-00 00:00:00.000000")
} else {
- p1 = src[5]
+ dst = []byte("0000-00-00 00:00:00")
}
- zOffs = 11
- src = src[6:]
} else {
- switch length {
- case 10, 19, 21, 22, 23, 24, 25, 26:
- default:
- t := "DATE"
- if length > 10 {
- t += "TIME"
- }
- return nil, fmt.Errorf("illegal %s length %d", t, length)
- }
- switch len(src) {
- case 4, 7, 11:
- default:
- t := "DATE"
- if length > 10 {
- t += "TIME"
- }
- return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
- }
- dst = make([]byte, 0, length)
- // start with the date
+ dst = []byte("0000-00-00")
+ }
+ switch len(src) {
+ case 11:
+ microsecs := binary.LittleEndian.Uint32(src[7:11])
+ tmp32 := microsecs / 10
+ dst[25] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[24] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[23] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[22] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[21] += byte(microsecs - 10*tmp32)
+ dst[20] += byte(microsecs / 10)
+ fallthrough
+ case 7:
+ second := src[6]
+ tmp := second / 10
+ dst[18] += second - 10*tmp
+ dst[17] += tmp
+ minute := src[5]
+ tmp = minute / 10
+ dst[15] += minute - 10*tmp
+ dst[14] += tmp
+ hour := src[4]
+ tmp = hour / 10
+ dst[12] += hour - 10*tmp
+ dst[11] += tmp
+ fallthrough
+ case 4:
+ day := src[3]
+ tmp := day / 10
+ dst[9] += day - 10*tmp
+ dst[8] += tmp
+ month := src[2]
+ tmp = month / 10
+ dst[6] += month - 10*tmp
+ dst[5] += tmp
year := binary.LittleEndian.Uint16(src[:2])
- pt = byte(year / 100)
- p1 = byte(year - 100*uint16(pt))
- p2, p3 = src[2], src[3]
- dst = append(dst,
- digits10[pt], digits01[pt],
- digits10[p1], digits01[p1], '-',
- digits10[p2], digits01[p2], '-',
- digits10[p3], digits01[p3],
- )
- if length == 10 {
- return dst, nil
- }
- if len(src) == 4 {
- return append(dst, zeroDateTime[10:length]...), nil
- }
- dst = append(dst, ' ')
- p1 = src[4] // hour
- src = src[5:]
- }
- // p1 is 2-digit hour, src is after hour
- p2, p3 = src[0], src[1]
- dst = append(dst,
- digits10[p1], digits01[p1], ':',
- digits10[p2], digits01[p2], ':',
- digits10[p3], digits01[p3],
- )
- if length <= byte(len(dst)) {
+ tmp16 := year / 10
+ dst[3] += byte(year - 10*tmp16)
+ tmp16, year = tmp16/10, tmp16
+ dst[2] += byte(year - 10*tmp16)
+ tmp16, year = tmp16/10, tmp16
+ dst[1] += byte(year - 10*tmp16)
+ dst[0] += byte(tmp16)
return dst, nil
}
- src = src[2:]
- if len(src) == 0 {
- return append(dst, zeroDateTime[19:zOffs+length]...), nil
- }
- microsecs := binary.LittleEndian.Uint32(src[:4])
- p1 = byte(microsecs / 10000)
- microsecs -= 10000 * uint32(p1)
- p2 = byte(microsecs / 100)
- microsecs -= 100 * uint32(p2)
- p3 = byte(microsecs)
- switch decimals := zOffs + length - 20; decimals {
- default:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2], digits01[p2],
- digits10[p3], digits01[p3],
- ), nil
- case 1:
- return append(dst, '.',
- digits10[p1],
- ), nil
- case 2:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- ), nil
- case 3:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2],
- ), nil
- case 4:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2], digits01[p2],
- ), nil
- case 5:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2], digits01[p2],
- digits10[p3],
- ), nil
+ var t string
+ if withTime {
+ t = "DATETIME"
+ } else {
+ t = "DATE"
}
+ return nil, fmt.Errorf("invalid %s-packet length %d", t, len(src))
}
/******************************************************************************
@@ -544,10 +683,6 @@ func skipLengthEncodedString(b []byte) (int, error) {
// returns the number read, whether the value is NULL and the number of bytes read
func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
- // See issue #349
- if len(b) == 0 {
- return 0, true, 1
- }
switch b[0] {
// 251: NULL
@@ -589,152 +724,3 @@ func appendLengthEncodedInteger(b []byte, n uint64) []byte {
return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24),
byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56))
}
-
-// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
-// If cap(buf) is not enough, reallocate new buffer.
-func reserveBuffer(buf []byte, appendSize int) []byte {
- newSize := len(buf) + appendSize
- if cap(buf) < newSize {
- // Grow buffer exponentially
- newBuf := make([]byte, len(buf)*2+appendSize)
- copy(newBuf, buf)
- buf = newBuf
- }
- return buf[:newSize]
-}
-
-// escapeBytesBackslash escapes []byte with backslashes (\)
-// This escapes the contents of a string (provided as []byte) by adding backslashes before special
-// characters, and turning others into specific escape sequences, such as
-// turning newlines into \n and null bytes into \0.
-// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932
-func escapeBytesBackslash(buf, v []byte) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for _, c := range v {
- switch c {
- case '\x00':
- buf[pos] = '\\'
- buf[pos+1] = '0'
- pos += 2
- case '\n':
- buf[pos] = '\\'
- buf[pos+1] = 'n'
- pos += 2
- case '\r':
- buf[pos] = '\\'
- buf[pos+1] = 'r'
- pos += 2
- case '\x1a':
- buf[pos] = '\\'
- buf[pos+1] = 'Z'
- pos += 2
- case '\'':
- buf[pos] = '\\'
- buf[pos+1] = '\''
- pos += 2
- case '"':
- buf[pos] = '\\'
- buf[pos+1] = '"'
- pos += 2
- case '\\':
- buf[pos] = '\\'
- buf[pos+1] = '\\'
- pos += 2
- default:
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}
-
-// escapeStringBackslash is similar to escapeBytesBackslash but for string.
-func escapeStringBackslash(buf []byte, v string) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for i := 0; i < len(v); i++ {
- c := v[i]
- switch c {
- case '\x00':
- buf[pos] = '\\'
- buf[pos+1] = '0'
- pos += 2
- case '\n':
- buf[pos] = '\\'
- buf[pos+1] = 'n'
- pos += 2
- case '\r':
- buf[pos] = '\\'
- buf[pos+1] = 'r'
- pos += 2
- case '\x1a':
- buf[pos] = '\\'
- buf[pos+1] = 'Z'
- pos += 2
- case '\'':
- buf[pos] = '\\'
- buf[pos+1] = '\''
- pos += 2
- case '"':
- buf[pos] = '\\'
- buf[pos+1] = '"'
- pos += 2
- case '\\':
- buf[pos] = '\\'
- buf[pos+1] = '\\'
- pos += 2
- default:
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}
-
-// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
-// This escapes the contents of a string by doubling up any apostrophes that
-// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
-// effect on the server.
-// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038
-func escapeBytesQuotes(buf, v []byte) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for _, c := range v {
- if c == '\'' {
- buf[pos] = '\''
- buf[pos+1] = '\''
- pos += 2
- } else {
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}
-
-// escapeStringQuotes is similar to escapeBytesQuotes but for string.
-func escapeStringQuotes(buf []byte, v string) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for i := 0; i < len(v); i++ {
- c := v[i]
- if c == '\'' {
- buf[pos] = '\''
- buf[pos+1] = '\''
- pos += 2
- } else {
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}
diff --git a/vendor/github.com/go-sql-driver/mysql/utils_test.go b/vendor/github.com/go-sql-driver/mysql/utils_test.go
index 0d6c6684f..301d81a62 100644
--- a/vendor/github.com/go-sql-driver/mysql/utils_test.go
+++ b/vendor/github.com/go-sql-driver/mysql/utils_test.go
@@ -16,6 +16,76 @@ import (
"time"
)
+var testDSNs = []struct {
+ in string
+ out string
+ loc *time.Location
+}{
+ {"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true}", time.UTC},
+ {"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.Local},
+ {"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+ {"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
+}
+
+func TestDSNParser(t *testing.T) {
+ var cfg *config
+ var err error
+ var res string
+
+ for i, tst := range testDSNs {
+ cfg, err = parseDSN(tst.in)
+ if err != nil {
+ t.Error(err.Error())
+ }
+
+ // pointer not static
+ cfg.tls = nil
+
+ res = fmt.Sprintf("%+v", cfg)
+ if res != fmt.Sprintf(tst.out, tst.loc) {
+ t.Errorf("%d. parseDSN(%q) => %q, want %q", i, tst.in, res, fmt.Sprintf(tst.out, tst.loc))
+ }
+ }
+}
+
+func TestDSNParserInvalid(t *testing.T) {
+ var invalidDSNs = []string{
+ "@net(addr/", // no closing brace
+ "@tcp(/", // no closing brace
+ "tcp(/", // no closing brace
+ "(/", // no closing brace
+ "net(addr)//", // unescaped
+ "user:pass@tcp(1.2.3.4:3306)", // no trailing slash
+ //"/dbname?arg=/some/unescaped/path",
+ }
+
+ for i, tst := range invalidDSNs {
+ if _, err := parseDSN(tst); err == nil {
+ t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
+ }
+ }
+}
+
+func BenchmarkParseDSN(b *testing.B) {
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ for _, tst := range testDSNs {
+ if _, err := parseDSN(tst.in); err != nil {
+ b.Error(err.Error())
+ }
+ }
+ }
+}
+
func TestScanNullTime(t *testing.T) {
var scanTests = []struct {
in interface{}
@@ -121,77 +191,22 @@ func TestFormatBinaryDateTime(t *testing.T) {
rawDate[5] = 46 // minutes
rawDate[6] = 23 // seconds
binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds
- expect := func(expected string, inlen, outlen uint8) {
- actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen, false)
+ expect := func(expected string, length int, withTime bool) {
+ actual, _ := formatBinaryDateTime(rawDate[:length], withTime)
bytes, ok := actual.([]byte)
if !ok {
t.Errorf("formatBinaryDateTime must return []byte, was %T", actual)
}
if string(bytes) != expected {
t.Errorf(
- "expected %q, got %q for length in %d, out %d",
- bytes, actual, inlen, outlen,
+ "expected %q, got %q for length %d, withTime %v",
+ bytes, actual, length, withTime,
)
}
}
- expect("0000-00-00", 0, 10)
- expect("0000-00-00 00:00:00", 0, 19)
- expect("1978-12-30", 4, 10)
- expect("1978-12-30 15:46:23", 7, 19)
- expect("1978-12-30 15:46:23.987654", 11, 26)
-}
-
-func TestEscapeBackslash(t *testing.T) {
- expect := func(expected, value string) {
- actual := string(escapeBytesBackslash([]byte{}, []byte(value)))
- if actual != expected {
- t.Errorf(
- "expected %s, got %s",
- expected, actual,
- )
- }
-
- actual = string(escapeStringBackslash([]byte{}, value))
- if actual != expected {
- t.Errorf(
- "expected %s, got %s",
- expected, actual,
- )
- }
- }
-
- expect("foo\\0bar", "foo\x00bar")
- expect("foo\\nbar", "foo\nbar")
- expect("foo\\rbar", "foo\rbar")
- expect("foo\\Zbar", "foo\x1abar")
- expect("foo\\\"bar", "foo\"bar")
- expect("foo\\\\bar", "foo\\bar")
- expect("foo\\'bar", "foo'bar")
-}
-
-func TestEscapeQuotes(t *testing.T) {
- expect := func(expected, value string) {
- actual := string(escapeBytesQuotes([]byte{}, []byte(value)))
- if actual != expected {
- t.Errorf(
- "expected %s, got %s",
- expected, actual,
- )
- }
-
- actual = string(escapeStringQuotes([]byte{}, value))
- if actual != expected {
- t.Errorf(
- "expected %s, got %s",
- expected, actual,
- )
- }
- }
-
- expect("foo\x00bar", "foo\x00bar") // not affected
- expect("foo\nbar", "foo\nbar") // not affected
- expect("foo\rbar", "foo\rbar") // not affected
- expect("foo\x1abar", "foo\x1abar") // not affected
- expect("foo''bar", "foo'bar") // affected
- expect("foo\"bar", "foo\"bar") // not affected
+ expect("0000-00-00", 0, false)
+ expect("0000-00-00 00:00:00", 0, true)
+ expect("1978-12-30", 4, false)
+ expect("1978-12-30 15:46:23", 7, true)
+ expect("1978-12-30 15:46:23.987654", 11, true)
}
diff --git a/vendor/github.com/goamz/goamz/aws/aws.go b/vendor/github.com/goamz/goamz/aws/aws.go
index 77bf563d6..49eed632e 100644
--- a/vendor/github.com/goamz/goamz/aws/aws.go
+++ b/vendor/github.com/goamz/goamz/aws/aws.go
@@ -65,6 +65,7 @@ type Region struct {
var Regions = map[string]Region{
APNortheast.Name: APNortheast,
+ APNortheast2.Name: APNortheast2,
APSoutheast.Name: APSoutheast,
APSoutheast2.Name: APSoutheast2,
EUCentral.Name: EUCentral,
@@ -390,6 +391,7 @@ func SharedAuth() (auth Auth, err error) {
auth.AccessKey = profile["aws_access_key_id"]
auth.SecretKey = profile["aws_secret_access_key"]
+ auth.token = profile["aws_session_token"]
if auth.AccessKey == "" {
err = errors.New("AWS_ACCESS_KEY_ID not found in environment in credentials file")
diff --git a/vendor/github.com/goamz/goamz/aws/regions.go b/vendor/github.com/goamz/goamz/aws/regions.go
index 5e18f023d..94d79d46d 100644
--- a/vendor/github.com/goamz/goamz/aws/regions.go
+++ b/vendor/github.com/goamz/goamz/aws/regions.go
@@ -207,6 +207,29 @@ var APNortheast = Region{
"https://streams.dynamodb.ap-northeast-1.amazonaws.com",
}
+var APNortheast2 = Region{
+ "ap-northeast-2",
+ "https://ec2.ap-northeast-2.amazonaws.com",
+ "https://s3-ap-northeast-2.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.ap-northeast-2.amazonaws.com",
+ "",
+ "https://sns.ap-northeast-2.amazonaws.com",
+ "https://sqs.ap-northeast-2.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.ap-northeast-2.amazonaws.com",
+ "https://dynamodb.ap-northeast-2.amazonaws.com",
+ ServiceInfo{"https://monitoring.ap-northeast-2.amazonaws.com", V2Signature},
+ "https://autoscaling.ap-northeast-2.amazonaws.com",
+ ServiceInfo{"https://rds.ap-northeast-2.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.ap-northeast-2.amazonaws.com",
+ "https://ecs.ap-northeast-2.amazonaws.com",
+ "https://streams.dynamodb.ap-northeast-2.amazonaws.com",
+}
+
var SAEast = Region{
"sa-east-1",
"https://ec2.sa-east-1.amazonaws.com",
diff --git a/vendor/github.com/goamz/goamz/exp/ses/ses.go b/vendor/github.com/goamz/goamz/exp/ses/ses.go
index 7c9c7c352..3896c5253 100644
--- a/vendor/github.com/goamz/goamz/exp/ses/ses.go
+++ b/vendor/github.com/goamz/goamz/exp/ses/ses.go
@@ -113,6 +113,10 @@ func (ses *SES) doPost(action string, data url.Values) error {
req.URL = URL
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+ token := ses.auth.Token()
+ if token != "" {
+ req.Header.Set("X-Amz-Security-Token", token)
+ }
sign(ses.auth, "POST", req.Header)
data.Add("AWSAccessKeyId", ses.auth.AccessKey)
@@ -139,7 +143,7 @@ func (ses *SES) doPost(action string, data url.Values) error {
}
func buildError(r *http.Response) *SESError {
- err := SESError{}
- xml.NewDecoder(r.Body).Decode(&err)
- return &err
+ rootElem := errorResponse{}
+ xml.NewDecoder(r.Body).Decode(&rootElem)
+ return &rootElem.Error
}
diff --git a/vendor/github.com/goamz/goamz/exp/ses/ses_types.go b/vendor/github.com/goamz/goamz/exp/ses/ses_types.go
index 698fa7623..d26e83740 100644
--- a/vendor/github.com/goamz/goamz/exp/ses/ses_types.go
+++ b/vendor/github.com/goamz/goamz/exp/ses/ses_types.go
@@ -45,6 +45,10 @@ type SESError struct {
RequestId string
}
+type errorResponse struct {
+ Error SESError
+}
+
func (err *SESError) Error() string {
return err.Message
}
diff --git a/vendor/github.com/goamz/goamz/s3/s3test/server.go b/vendor/github.com/goamz/goamz/s3/s3test/server.go
index bf4dd8a3c..04808d3ad 100644
--- a/vendor/github.com/goamz/goamz/s3/s3test/server.go
+++ b/vendor/github.com/goamz/goamz/s3/s3test/server.go
@@ -497,6 +497,8 @@ type objectResource struct {
object *object // may be nil.
}
+const awsTimeFormat = "Mon, 2 Jan 2006 15:04:05 GMT"
+
// GET on an object gets the contents of the object.
// http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html
func (objr objectResource) get(a *action) interface{} {
@@ -531,7 +533,7 @@ func (objr objectResource) get(a *action) interface{} {
// TODO x-amz-request-id
h.Set("Content-Length", fmt.Sprint(len(obj.data)))
h.Set("ETag", hex.EncodeToString(obj.checksum))
- h.Set("Last-Modified", obj.mtime.Format(time.RFC1123))
+ h.Set("Last-Modified", obj.mtime.Format(awsTimeFormat))
if a.req.Method == "HEAD" {
return nil
}
diff --git a/vendor/github.com/goamz/goamz/sqs/sqs.go b/vendor/github.com/goamz/goamz/sqs/sqs.go
index 4005b1b37..23f1951ab 100644
--- a/vendor/github.com/goamz/goamz/sqs/sqs.go
+++ b/vendor/github.com/goamz/goamz/sqs/sqs.go
@@ -34,7 +34,8 @@ const debug = false
type SQS struct {
aws.Auth
aws.Region
- private byte // Reserve the right of using private data.
+ private byte // Reserve the right of using private data.
+ transport *http.Transport
}
// NewFrom Create A new SQS Client given an access and secret Key
@@ -59,6 +60,8 @@ func NewFrom(accessKey, secretKey, region string) (*SQS, error) {
aws_region = aws.APSoutheast2
case "ap.northeast", "ap.northeast.1":
aws_region = aws.APNortheast
+ case "ap.northeast.2":
+ aws_region = aws.APNortheast2
case "sa.east", "sa.east.1":
aws_region = aws.SAEast
case "cn.north", "cn.north.1":
@@ -73,7 +76,12 @@ func NewFrom(accessKey, secretKey, region string) (*SQS, error) {
// NewFrom Create A new SQS Client from an exisisting aws.Auth
func New(auth aws.Auth, region aws.Region) *SQS {
- return &SQS{auth, region, 0}
+ return &SQS{auth, region, 0, nil}
+}
+
+// NewFromTransport Create A new SQS Client that uses a given &http.Transport
+func NewFromTransport(auth aws.Auth, region aws.Region, transport *http.Transport) *SQS {
+ return &SQS{auth, region, 0, transport}
}
// Queue Reference to a Queue
@@ -511,11 +519,16 @@ func (s *SQS) query(queueUrl string, params map[string]string, resp interface{})
}
signer := aws.NewV4Signer(s.Auth, "sqs", s.Region)
signer.Sign(req)
- client := http.Client{}
+ var client http.Client
+ if s.transport == nil {
+ client = http.Client{}
+ } else {
+ client = http.Client{Transport: s.transport}
+ }
r, err = client.Do(req)
if debug {
- log.Printf("GET ", url_.String())
+ log.Printf("GET %s\n", url_.String())
}
if err != nil {
@@ -526,7 +539,7 @@ func (s *SQS) query(queueUrl string, params map[string]string, resp interface{})
if debug {
dump, _ := httputil.DumpResponse(r, true)
- log.Printf("DUMP:\n", string(dump))
+ log.Printf("DUMP:%s\n", string(dump))
}
if r.StatusCode != 200 {
diff --git a/vendor/github.com/golang/freetype/example/genbasicfont/main.go b/vendor/github.com/golang/freetype/example/genbasicfont/main.go
new file mode 100644
index 000000000..5b2f2bc6f
--- /dev/null
+++ b/vendor/github.com/golang/freetype/example/genbasicfont/main.go
@@ -0,0 +1,237 @@
+// Copyright 2016 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// +build example
+//
+// This build tag means that "go install github.com/golang/freetype/..."
+// doesn't install this example program. Use "go run main.go" to run it or "go
+// install -tags=example" to install it.
+
+// Program genbasicfont generates Go source code that imports
+// golang.org/x/image/font/basicfont to provide a fixed width font face.
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/format"
+ "image"
+ "image/draw"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "strings"
+ "unicode"
+
+ "github.com/golang/freetype/truetype"
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+var (
+ fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename or URL of the TTF font")
+ hinting = flag.String("hinting", "none", "none, vertical or full")
+ pkg = flag.String("pkg", "example", "the package name for the generated code")
+ size = flag.Float64("size", 12, "the number of pixels in 1 em")
+ vr = flag.String("var", "example", "the variable name for the generated code")
+)
+
+func loadFontFile() ([]byte, error) {
+ if strings.HasPrefix(*fontfile, "http://") || strings.HasPrefix(*fontfile, "https://") {
+ resp, err := http.Get(*fontfile)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ return ioutil.ReadAll(resp.Body)
+ }
+ return ioutil.ReadFile(*fontfile)
+}
+
+func parseHinting(h string) font.Hinting {
+ switch h {
+ case "full":
+ return font.HintingFull
+ case "vertical":
+ log.Fatal("TODO: have package truetype implement vertical hinting")
+ return font.HintingVertical
+ }
+ return font.HintingNone
+}
+
+func privateUseArea(r rune) bool {
+ return 0xe000 <= r && r <= 0xf8ff ||
+ 0xf0000 <= r && r <= 0xffffd ||
+ 0x100000 <= r && r <= 0x10fffd
+}
+
+func loadRanges(f *truetype.Font) (ret [][2]rune) {
+ rr := [2]rune{-1, -1}
+ for r := rune(0); r <= unicode.MaxRune; r++ {
+ if privateUseArea(r) {
+ continue
+ }
+ if f.Index(r) == 0 {
+ continue
+ }
+ if rr[1] == r {
+ rr[1] = r + 1
+ continue
+ }
+ if rr[0] != -1 {
+ ret = append(ret, rr)
+ }
+ rr = [2]rune{r, r + 1}
+ }
+ if rr[0] != -1 {
+ ret = append(ret, rr)
+ }
+ return ret
+}
+
+func emptyCol(m *image.Gray, r image.Rectangle, x int) bool {
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ if m.GrayAt(x, y).Y > 0 {
+ return false
+ }
+ }
+ return true
+}
+
+func emptyRow(m *image.Gray, r image.Rectangle, y int) bool {
+ for x := r.Min.X; x < r.Max.X; x++ {
+ if m.GrayAt(x, y).Y > 0 {
+ return false
+ }
+ }
+ return true
+}
+
+func tightBounds(m *image.Gray) (r image.Rectangle) {
+ r = m.Bounds()
+ for ; r.Min.Y < r.Max.Y && emptyRow(m, r, r.Min.Y+0); r.Min.Y++ {
+ }
+ for ; r.Min.Y < r.Max.Y && emptyRow(m, r, r.Max.Y-1); r.Max.Y-- {
+ }
+ for ; r.Min.X < r.Max.X && emptyCol(m, r, r.Min.X+0); r.Min.X++ {
+ }
+ for ; r.Min.X < r.Max.X && emptyCol(m, r, r.Max.X-1); r.Max.X-- {
+ }
+ return r
+}
+
+func printPix(ranges [][2]rune, glyphs map[rune]*image.Gray, b image.Rectangle) []byte {
+ buf := new(bytes.Buffer)
+ for _, rr := range ranges {
+ for r := rr[0]; r < rr[1]; r++ {
+ m := glyphs[r]
+ fmt.Fprintf(buf, "// U+%08x '%c'\n", r, r)
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ fmt.Fprintf(buf, "%#02x, ", m.GrayAt(x, y).Y)
+ }
+ fmt.Fprintln(buf)
+ }
+ fmt.Fprintln(buf)
+ }
+ }
+ return buf.Bytes()
+}
+
+func printRanges(ranges [][2]rune) []byte {
+ buf := new(bytes.Buffer)
+ offset := 0
+ for _, rr := range ranges {
+ fmt.Fprintf(buf, "{'\\U%08x', '\\U%08x', %d},\n", rr[0], rr[1], offset)
+ offset += int(rr[1] - rr[0])
+ }
+ return buf.Bytes()
+}
+
+func main() {
+ flag.Parse()
+ b, err := loadFontFile()
+ if err != nil {
+ log.Fatal(err)
+ }
+ f, err := truetype.Parse(b)
+ if err != nil {
+ log.Fatal(err)
+ }
+ face := truetype.NewFace(f, &truetype.Options{
+ Size: *size,
+ Hinting: parseHinting(*hinting),
+ })
+ defer face.Close()
+
+ fBounds := f.Bounds(fixed.Int26_6(*size * 64))
+ iBounds := image.Rect(
+ +fBounds.Min.X.Floor(),
+ -fBounds.Max.Y.Ceil(),
+ +fBounds.Max.X.Ceil(),
+ -fBounds.Min.Y.Floor(),
+ )
+
+ tBounds := image.Rectangle{}
+ glyphs := map[rune]*image.Gray{}
+ advance := fixed.Int26_6(-1)
+
+ ranges := loadRanges(f)
+ for _, rr := range ranges {
+ for r := rr[0]; r < rr[1]; r++ {
+ dr, mask, maskp, adv, ok := face.Glyph(fixed.Point26_6{}, r)
+ if !ok {
+ log.Fatalf("could not load glyph for %U", r)
+ }
+ if advance < 0 {
+ advance = adv
+ } else if advance != adv {
+ log.Fatalf("advance was not constant: got %v and %v", advance, adv)
+ }
+ dst := image.NewGray(iBounds)
+ draw.DrawMask(dst, dr, image.White, image.Point{}, mask, maskp, draw.Src)
+ glyphs[r] = dst
+ tBounds = tBounds.Union(tightBounds(dst))
+ }
+ }
+
+ // height is the glyph image height, not the inter-line spacing.
+ width, height := tBounds.Dx(), tBounds.Dy()
+
+ buf := new(bytes.Buffer)
+ fmt.Fprintf(buf, "// generated by go generate; DO NOT EDIT.\n\npackage %s\n\n", *pkg)
+ fmt.Fprintf(buf, "import (\n\"image\"\n\n\"golang.org/x/image/font/basicfont\"\n)\n\n")
+ fmt.Fprintf(buf, "// %s contains %d %d×%d glyphs in %d Pix bytes.\n",
+ *vr, len(glyphs), width, height, len(glyphs)*width*height)
+ fmt.Fprintf(buf, `var %s = basicfont.Face{
+ Advance: %d,
+ Width: %d,
+ Height: %d,
+ Ascent: %d,
+ Descent: %d,
+ Left: %d,
+ Mask: &image.Alpha{
+ Stride: %d,
+ Rect: image.Rectangle{Max: image.Point{%d, %d*%d}},
+ Pix: []byte{
+ %s
+ },
+ },
+ Ranges: []basicfont.Range{
+ %s
+ },
+ }`, *vr, advance.Ceil(), width, face.Metrics().Height.Ceil(), -tBounds.Min.Y, +tBounds.Max.Y, tBounds.Min.X,
+ width, width, len(glyphs), height,
+ printPix(ranges, glyphs, tBounds), printRanges(ranges))
+
+ fmted, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatalf("format.Source: %v", err)
+ }
+ if err := ioutil.WriteFile(*vr+".go", fmted, 0644); err != nil {
+ log.Fatalf("ioutil.WriteFile: %v", err)
+ }
+}
diff --git a/vendor/github.com/golang/freetype/truetype/hint.go b/vendor/github.com/golang/freetype/truetype/hint.go
index 0315de511..13f785bc3 100644
--- a/vendor/github.com/golang/freetype/truetype/hint.go
+++ b/vendor/github.com/golang/freetype/truetype/hint.go
@@ -341,7 +341,14 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
case opSLOOP:
top--
- if h.stack[top] <= 0 {
+ // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM05/Chap5.html#SLOOP
+ // says that "Setting the loop variable to zero is an error". In
+ // theory, the inequality on the next line should be "<=" instead
+ // of "<". In practice, some font files' bytecode, such as the '2'
+ // glyph in the DejaVuSansMono.ttf that comes with Ubuntu 14.04,
+ // issue SLOOP with a zero on top of the stack. Just like the C
+ // Freetype code, we allow the zero.
+ if h.stack[top] < 0 {
return errors.New("truetype: hinting: invalid data")
}
h.gs.loop = h.stack[top]
diff --git a/vendor/github.com/golang/groupcache/.gitignore b/vendor/github.com/golang/groupcache/.gitignore
deleted file mode 100644
index b25c15b81..000000000
--- a/vendor/github.com/golang/groupcache/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*~
diff --git a/vendor/github.com/golang/groupcache/LICENSE b/vendor/github.com/golang/groupcache/LICENSE
deleted file mode 100644
index 37ec93a14..000000000
--- a/vendor/github.com/golang/groupcache/LICENSE
+++ /dev/null
@@ -1,191 +0,0 @@
-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.
diff --git a/vendor/github.com/golang/groupcache/README.md b/vendor/github.com/golang/groupcache/README.md
deleted file mode 100644
index 70c29da16..000000000
--- a/vendor/github.com/golang/groupcache/README.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# groupcache
-
-## Summary
-
-groupcache is a caching and cache-filling library, intended as a
-replacement for memcached in many cases.
-
-For API docs and examples, see http://godoc.org/github.com/golang/groupcache
-
-## Comparison to memcached
-
-### **Like memcached**, groupcache:
-
- * shards by key to select which peer is responsible for that key
-
-### **Unlike memcached**, groupcache:
-
- * does not require running a separate set of servers, thus massively
- reducing deployment/configuration pain. groupcache is a client
- library as well as a server. It connects to its own peers.
-
- * comes with a cache filling mechanism. Whereas memcached just says
- "Sorry, cache miss", often resulting in a thundering herd of
- database (or whatever) loads from an unbounded number of clients
- (which has resulted in several fun outages), groupcache coordinates
- cache fills such that only one load in one process of an entire
- replicated set of processes populates the cache, then multiplexes
- the loaded value to all callers.
-
- * does not support versioned values. If key "foo" is value "bar",
- key "foo" must always be "bar". There are neither cache expiration
- times, nor explicit cache evictions. Thus there is also no CAS,
- nor Increment/Decrement. This also means that groupcache....
-
- * ... supports automatic mirroring of super-hot items to multiple
- processes. This prevents memcached hot spotting where a machine's
- CPU and/or NIC are overloaded by very popular keys/values.
-
- * is currently only available for Go. It's very unlikely that I
- (bradfitz@) will port the code to any other language.
-
-## Loading process
-
-In a nutshell, a groupcache lookup of **Get("foo")** looks like:
-
-(On machine #5 of a set of N machines running the same code)
-
- 1. Is the value of "foo" in local memory because it's super hot? If so, use it.
-
- 2. Is the value of "foo" in local memory because peer #5 (the current
- peer) is the owner of it? If so, use it.
-
- 3. Amongst all the peers in my set of N, am I the owner of the key
- "foo"? (e.g. does it consistent hash to 5?) If so, load it. If
- other callers come in, via the same process or via RPC requests
- from peers, they block waiting for the load to finish and get the
- same answer. If not, RPC to the peer that's the owner and get
- the answer. If the RPC fails, just load it locally (still with
- local dup suppression).
-
-## Users
-
-groupcache is in production use by dl.google.com (its original user),
-parts of Blogger, parts of Google Code, parts of Google Fiber, parts
-of Google production monitoring systems, etc.
-
-## Presentations
-
-See http://talks.golang.org/2013/oscon-dl.slide
-
-## Help
-
-Use the golang-nuts mailing list for any discussion or questions.
diff --git a/vendor/github.com/golang/groupcache/byteview.go b/vendor/github.com/golang/groupcache/byteview.go
deleted file mode 100644
index 035a9ee44..000000000
--- a/vendor/github.com/golang/groupcache/byteview.go
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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 groupcache
-
-import (
- "bytes"
- "errors"
- "io"
- "strings"
-)
-
-// A ByteView holds an immutable view of bytes.
-// Internally it wraps either a []byte or a string,
-// but that detail is invisible to callers.
-//
-// A ByteView is meant to be used as a value type, not
-// a pointer (like a time.Time).
-type ByteView struct {
- // If b is non-nil, b is used, else s is used.
- b []byte
- s string
-}
-
-// Len returns the view's length.
-func (v ByteView) Len() int {
- if v.b != nil {
- return len(v.b)
- }
- return len(v.s)
-}
-
-// ByteSlice returns a copy of the data as a byte slice.
-func (v ByteView) ByteSlice() []byte {
- if v.b != nil {
- return cloneBytes(v.b)
- }
- return []byte(v.s)
-}
-
-// String returns the data as a string, making a copy if necessary.
-func (v ByteView) String() string {
- if v.b != nil {
- return string(v.b)
- }
- return v.s
-}
-
-// At returns the byte at index i.
-func (v ByteView) At(i int) byte {
- if v.b != nil {
- return v.b[i]
- }
- return v.s[i]
-}
-
-// Slice slices the view between the provided from and to indices.
-func (v ByteView) Slice(from, to int) ByteView {
- if v.b != nil {
- return ByteView{b: v.b[from:to]}
- }
- return ByteView{s: v.s[from:to]}
-}
-
-// SliceFrom slices the view from the provided index until the end.
-func (v ByteView) SliceFrom(from int) ByteView {
- if v.b != nil {
- return ByteView{b: v.b[from:]}
- }
- return ByteView{s: v.s[from:]}
-}
-
-// Copy copies b into dest and returns the number of bytes copied.
-func (v ByteView) Copy(dest []byte) int {
- if v.b != nil {
- return copy(dest, v.b)
- }
- return copy(dest, v.s)
-}
-
-// Equal returns whether the bytes in b are the same as the bytes in
-// b2.
-func (v ByteView) Equal(b2 ByteView) bool {
- if b2.b == nil {
- return v.EqualString(b2.s)
- }
- return v.EqualBytes(b2.b)
-}
-
-// EqualString returns whether the bytes in b are the same as the bytes
-// in s.
-func (v ByteView) EqualString(s string) bool {
- if v.b == nil {
- return v.s == s
- }
- l := v.Len()
- if len(s) != l {
- return false
- }
- for i, bi := range v.b {
- if bi != s[i] {
- return false
- }
- }
- return true
-}
-
-// EqualBytes returns whether the bytes in b are the same as the bytes
-// in b2.
-func (v ByteView) EqualBytes(b2 []byte) bool {
- if v.b != nil {
- return bytes.Equal(v.b, b2)
- }
- l := v.Len()
- if len(b2) != l {
- return false
- }
- for i, bi := range b2 {
- if bi != v.s[i] {
- return false
- }
- }
- return true
-}
-
-// Reader returns an io.ReadSeeker for the bytes in v.
-func (v ByteView) Reader() io.ReadSeeker {
- if v.b != nil {
- return bytes.NewReader(v.b)
- }
- return strings.NewReader(v.s)
-}
-
-// ReadAt implements io.ReaderAt on the bytes in v.
-func (v ByteView) ReadAt(p []byte, off int64) (n int, err error) {
- if off < 0 {
- return 0, errors.New("view: invalid offset")
- }
- if off >= int64(v.Len()) {
- return 0, io.EOF
- }
- n = v.SliceFrom(int(off)).Copy(p)
- if n < len(p) {
- err = io.EOF
- }
- return
-}
diff --git a/vendor/github.com/golang/groupcache/byteview_test.go b/vendor/github.com/golang/groupcache/byteview_test.go
deleted file mode 100644
index 9ece00f45..000000000
--- a/vendor/github.com/golang/groupcache/byteview_test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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 groupcache
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "testing"
-)
-
-func TestByteView(t *testing.T) {
- for _, s := range []string{"", "x", "yy"} {
- for _, v := range []ByteView{of([]byte(s)), of(s)} {
- name := fmt.Sprintf("string %q, view %+v", s, v)
- if v.Len() != len(s) {
- t.Errorf("%s: Len = %d; want %d", name, v.Len(), len(s))
- }
- if v.String() != s {
- t.Errorf("%s: String = %q; want %q", name, v.String(), s)
- }
- var longDest [3]byte
- if n := v.Copy(longDest[:]); n != len(s) {
- t.Errorf("%s: long Copy = %d; want %d", name, n, len(s))
- }
- var shortDest [1]byte
- if n := v.Copy(shortDest[:]); n != min(len(s), 1) {
- t.Errorf("%s: short Copy = %d; want %d", name, n, min(len(s), 1))
- }
- if got, err := ioutil.ReadAll(v.Reader()); err != nil || string(got) != s {
- t.Errorf("%s: Reader = %q, %v; want %q", name, got, err, s)
- }
- if got, err := ioutil.ReadAll(io.NewSectionReader(v, 0, int64(len(s)))); err != nil || string(got) != s {
- t.Errorf("%s: SectionReader of ReaderAt = %q, %v; want %q", name, got, err, s)
- }
- }
- }
-}
-
-// of returns a byte view of the []byte or string in x.
-func of(x interface{}) ByteView {
- if bytes, ok := x.([]byte); ok {
- return ByteView{b: bytes}
- }
- return ByteView{s: x.(string)}
-}
-
-func TestByteViewEqual(t *testing.T) {
- tests := []struct {
- a interface{} // string or []byte
- b interface{} // string or []byte
- want bool
- }{
- {"x", "x", true},
- {"x", "y", false},
- {"x", "yy", false},
- {[]byte("x"), []byte("x"), true},
- {[]byte("x"), []byte("y"), false},
- {[]byte("x"), []byte("yy"), false},
- {[]byte("x"), "x", true},
- {[]byte("x"), "y", false},
- {[]byte("x"), "yy", false},
- {"x", []byte("x"), true},
- {"x", []byte("y"), false},
- {"x", []byte("yy"), false},
- }
- for i, tt := range tests {
- va := of(tt.a)
- if bytes, ok := tt.b.([]byte); ok {
- if got := va.EqualBytes(bytes); got != tt.want {
- t.Errorf("%d. EqualBytes = %v; want %v", i, got, tt.want)
- }
- } else {
- if got := va.EqualString(tt.b.(string)); got != tt.want {
- t.Errorf("%d. EqualString = %v; want %v", i, got, tt.want)
- }
- }
- if got := va.Equal(of(tt.b)); got != tt.want {
- t.Errorf("%d. Equal = %v; want %v", i, got, tt.want)
- }
- }
-}
-
-func TestByteViewSlice(t *testing.T) {
- tests := []struct {
- in string
- from int
- to interface{} // nil to mean the end (SliceFrom); else int
- want string
- }{
- {
- in: "abc",
- from: 1,
- to: 2,
- want: "b",
- },
- {
- in: "abc",
- from: 1,
- want: "bc",
- },
- {
- in: "abc",
- to: 2,
- want: "ab",
- },
- }
- for i, tt := range tests {
- for _, v := range []ByteView{of([]byte(tt.in)), of(tt.in)} {
- name := fmt.Sprintf("test %d, view %+v", i, v)
- if tt.to != nil {
- v = v.Slice(tt.from, tt.to.(int))
- } else {
- v = v.SliceFrom(tt.from)
- }
- if v.String() != tt.want {
- t.Errorf("%s: got %q; want %q", name, v.String(), tt.want)
- }
- }
- }
-}
-
-func min(a, b int) int {
- if a < b {
- return a
- }
- return b
-}
diff --git a/vendor/github.com/golang/groupcache/consistenthash/consistenthash.go b/vendor/github.com/golang/groupcache/consistenthash/consistenthash.go
deleted file mode 100644
index a9c56f076..000000000
--- a/vendor/github.com/golang/groupcache/consistenthash/consistenthash.go
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-Copyright 2013 Google Inc.
-
-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 consistenthash provides an implementation of a ring hash.
-package consistenthash
-
-import (
- "hash/crc32"
- "sort"
- "strconv"
-)
-
-type Hash func(data []byte) uint32
-
-type Map struct {
- hash Hash
- replicas int
- keys []int // Sorted
- hashMap map[int]string
-}
-
-func New(replicas int, fn Hash) *Map {
- m := &Map{
- replicas: replicas,
- hash: fn,
- hashMap: make(map[int]string),
- }
- if m.hash == nil {
- m.hash = crc32.ChecksumIEEE
- }
- return m
-}
-
-// Returns true if there are no items available.
-func (m *Map) IsEmpty() bool {
- return len(m.keys) == 0
-}
-
-// Adds some keys to the hash.
-func (m *Map) Add(keys ...string) {
- for _, key := range keys {
- for i := 0; i < m.replicas; i++ {
- hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
- m.keys = append(m.keys, hash)
- m.hashMap[hash] = key
- }
- }
- sort.Ints(m.keys)
-}
-
-// Gets the closest item in the hash to the provided key.
-func (m *Map) Get(key string) string {
- if m.IsEmpty() {
- return ""
- }
-
- hash := int(m.hash([]byte(key)))
-
- // Binary search for appropriate replica.
- idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash })
-
- // Means we have cycled back to the first replica.
- if idx == len(m.keys) {
- idx = 0
- }
-
- return m.hashMap[m.keys[idx]]
-}
diff --git a/vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go b/vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go
deleted file mode 100644
index 1a37fd7ff..000000000
--- a/vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-Copyright 2013 Google Inc.
-
-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 consistenthash
-
-import (
- "fmt"
- "strconv"
- "testing"
-)
-
-func TestHashing(t *testing.T) {
-
- // Override the hash function to return easier to reason about values. Assumes
- // the keys can be converted to an integer.
- hash := New(3, func(key []byte) uint32 {
- i, err := strconv.Atoi(string(key))
- if err != nil {
- panic(err)
- }
- return uint32(i)
- })
-
- // Given the above hash function, this will give replicas with "hashes":
- // 2, 4, 6, 12, 14, 16, 22, 24, 26
- hash.Add("6", "4", "2")
-
- testCases := map[string]string{
- "2": "2",
- "11": "2",
- "23": "4",
- "27": "2",
- }
-
- for k, v := range testCases {
- if hash.Get(k) != v {
- t.Errorf("Asking for %s, should have yielded %s", k, v)
- }
- }
-
- // Adds 8, 18, 28
- hash.Add("8")
-
- // 27 should now map to 8.
- testCases["27"] = "8"
-
- for k, v := range testCases {
- if hash.Get(k) != v {
- t.Errorf("Asking for %s, should have yielded %s", k, v)
- }
- }
-
-}
-
-func TestConsistency(t *testing.T) {
- hash1 := New(1, nil)
- hash2 := New(1, nil)
-
- hash1.Add("Bill", "Bob", "Bonny")
- hash2.Add("Bob", "Bonny", "Bill")
-
- if hash1.Get("Ben") != hash2.Get("Ben") {
- t.Errorf("Fetching 'Ben' from both hashes should be the same")
- }
-
- hash2.Add("Becky", "Ben", "Bobby")
-
- if hash1.Get("Ben") != hash2.Get("Ben") ||
- hash1.Get("Bob") != hash2.Get("Bob") ||
- hash1.Get("Bonny") != hash2.Get("Bonny") {
- t.Errorf("Direct matches should always return the same entry")
- }
-
-}
-
-func BenchmarkGet8(b *testing.B) { benchmarkGet(b, 8) }
-func BenchmarkGet32(b *testing.B) { benchmarkGet(b, 32) }
-func BenchmarkGet128(b *testing.B) { benchmarkGet(b, 128) }
-func BenchmarkGet512(b *testing.B) { benchmarkGet(b, 512) }
-
-func benchmarkGet(b *testing.B, shards int) {
-
- hash := New(50, nil)
-
- var buckets []string
- for i := 0; i < shards; i++ {
- buckets = append(buckets, fmt.Sprintf("shard-%d", i))
- }
-
- hash.Add(buckets...)
-
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- hash.Get(buckets[i&(shards-1)])
- }
-}
diff --git a/vendor/github.com/golang/groupcache/groupcache.go b/vendor/github.com/golang/groupcache/groupcache.go
deleted file mode 100644
index 40410a0cc..000000000
--- a/vendor/github.com/golang/groupcache/groupcache.go
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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 groupcache provides a data loading mechanism with caching
-// and de-duplication that works across a set of peer processes.
-//
-// Each data Get first consults its local cache, otherwise delegates
-// to the requested key's canonical owner, which then checks its cache
-// or finally gets the data. In the common case, many concurrent
-// cache misses across a set of peers for the same key result in just
-// one cache fill.
-package groupcache
-
-import (
- "errors"
- "math/rand"
- "strconv"
- "sync"
- "sync/atomic"
-
- pb "github.com/golang/groupcache/groupcachepb"
- "github.com/golang/groupcache/lru"
- "github.com/golang/groupcache/singleflight"
-)
-
-// A Getter loads data for a key.
-type Getter interface {
- // Get returns the value identified by key, populating dest.
- //
- // The returned data must be unversioned. That is, key must
- // uniquely describe the loaded data, without an implicit
- // current time, and without relying on cache expiration
- // mechanisms.
- Get(ctx Context, key string, dest Sink) error
-}
-
-// A GetterFunc implements Getter with a function.
-type GetterFunc func(ctx Context, key string, dest Sink) error
-
-func (f GetterFunc) Get(ctx Context, key string, dest Sink) error {
- return f(ctx, key, dest)
-}
-
-var (
- mu sync.RWMutex
- groups = make(map[string]*Group)
-
- initPeerServerOnce sync.Once
- initPeerServer func()
-)
-
-// GetGroup returns the named group previously created with NewGroup, or
-// nil if there's no such group.
-func GetGroup(name string) *Group {
- mu.RLock()
- g := groups[name]
- mu.RUnlock()
- return g
-}
-
-// NewGroup creates a coordinated group-aware Getter from a Getter.
-//
-// The returned Getter tries (but does not guarantee) to run only one
-// Get call at once for a given key across an entire set of peer
-// processes. Concurrent callers both in the local process and in
-// other processes receive copies of the answer once the original Get
-// completes.
-//
-// The group name must be unique for each getter.
-func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
- return newGroup(name, cacheBytes, getter, nil)
-}
-
-// If peers is nil, the peerPicker is called via a sync.Once to initialize it.
-func newGroup(name string, cacheBytes int64, getter Getter, peers PeerPicker) *Group {
- if getter == nil {
- panic("nil Getter")
- }
- mu.Lock()
- defer mu.Unlock()
- initPeerServerOnce.Do(callInitPeerServer)
- if _, dup := groups[name]; dup {
- panic("duplicate registration of group " + name)
- }
- g := &Group{
- name: name,
- getter: getter,
- peers: peers,
- cacheBytes: cacheBytes,
- loadGroup: &singleflight.Group{},
- }
- if fn := newGroupHook; fn != nil {
- fn(g)
- }
- groups[name] = g
- return g
-}
-
-// newGroupHook, if non-nil, is called right after a new group is created.
-var newGroupHook func(*Group)
-
-// RegisterNewGroupHook registers a hook that is run each time
-// a group is created.
-func RegisterNewGroupHook(fn func(*Group)) {
- if newGroupHook != nil {
- panic("RegisterNewGroupHook called more than once")
- }
- newGroupHook = fn
-}
-
-// RegisterServerStart registers a hook that is run when the first
-// group is created.
-func RegisterServerStart(fn func()) {
- if initPeerServer != nil {
- panic("RegisterServerStart called more than once")
- }
- initPeerServer = fn
-}
-
-func callInitPeerServer() {
- if initPeerServer != nil {
- initPeerServer()
- }
-}
-
-// A Group is a cache namespace and associated data loaded spread over
-// a group of 1 or more machines.
-type Group struct {
- name string
- getter Getter
- peersOnce sync.Once
- peers PeerPicker
- cacheBytes int64 // limit for sum of mainCache and hotCache size
-
- // mainCache is a cache of the keys for which this process
- // (amongst its peers) is authorative. That is, this cache
- // contains keys which consistent hash on to this process's
- // peer number.
- mainCache cache
-
- // hotCache contains keys/values for which this peer is not
- // authorative (otherwise they would be in mainCache), but
- // are popular enough to warrant mirroring in this process to
- // avoid going over the network to fetch from a peer. Having
- // a hotCache avoids network hotspotting, where a peer's
- // network card could become the bottleneck on a popular key.
- // This cache is used sparingly to maximize the total number
- // of key/value pairs that can be stored globally.
- hotCache cache
-
- // loadGroup ensures that each key is only fetched once
- // (either locally or remotely), regardless of the number of
- // concurrent callers.
- loadGroup flightGroup
-
- // Stats are statistics on the group.
- Stats Stats
-}
-
-// flightGroup is defined as an interface which flightgroup.Group
-// satisfies. We define this so that we may test with an alternate
-// implementation.
-type flightGroup interface {
- // Done is called when Do is done.
- Do(key string, fn func() (interface{}, error)) (interface{}, error)
-}
-
-// Stats are per-group statistics.
-type Stats struct {
- Gets AtomicInt // any Get request, including from peers
- CacheHits AtomicInt // either cache was good
- PeerLoads AtomicInt // either remote load or remote cache hit (not an error)
- PeerErrors AtomicInt
- Loads AtomicInt // (gets - cacheHits)
- LoadsDeduped AtomicInt // after singleflight
- LocalLoads AtomicInt // total good local loads
- LocalLoadErrs AtomicInt // total bad local loads
- ServerRequests AtomicInt // gets that came over the network from peers
-}
-
-// Name returns the name of the group.
-func (g *Group) Name() string {
- return g.name
-}
-
-func (g *Group) initPeers() {
- if g.peers == nil {
- g.peers = getPeers()
- }
-}
-
-func (g *Group) Get(ctx Context, key string, dest Sink) error {
- g.peersOnce.Do(g.initPeers)
- g.Stats.Gets.Add(1)
- if dest == nil {
- return errors.New("groupcache: nil dest Sink")
- }
- value, cacheHit := g.lookupCache(key)
-
- if cacheHit {
- g.Stats.CacheHits.Add(1)
- return setSinkView(dest, value)
- }
-
- // Optimization to avoid double unmarshalling or copying: keep
- // track of whether the dest was already populated. One caller
- // (if local) will set this; the losers will not. The common
- // case will likely be one caller.
- destPopulated := false
- value, destPopulated, err := g.load(ctx, key, dest)
- if err != nil {
- return err
- }
- if destPopulated {
- return nil
- }
- return setSinkView(dest, value)
-}
-
-// load loads key either by invoking the getter locally or by sending it to another machine.
-func (g *Group) load(ctx Context, key string, dest Sink) (value ByteView, destPopulated bool, err error) {
- g.Stats.Loads.Add(1)
- viewi, err := g.loadGroup.Do(key, func() (interface{}, error) {
- // Check the cache again because singleflight can only dedup calls
- // that overlap concurrently. It's possible for 2 concurrent
- // requests to miss the cache, resulting in 2 load() calls. An
- // unfortunate goroutine scheduling would result in this callback
- // being run twice, serially. If we don't check the cache again,
- // cache.nbytes would be incremented below even though there will
- // be only one entry for this key.
- //
- // Consider the following serialized event ordering for two
- // goroutines in which this callback gets called twice for hte
- // same key:
- // 1: Get("key")
- // 2: Get("key")
- // 1: lookupCache("key")
- // 2: lookupCache("key")
- // 1: load("key")
- // 2: load("key")
- // 1: loadGroup.Do("key", fn)
- // 1: fn()
- // 2: loadGroup.Do("key", fn)
- // 2: fn()
- if value, cacheHit := g.lookupCache(key); cacheHit {
- g.Stats.CacheHits.Add(1)
- return value, nil
- }
- g.Stats.LoadsDeduped.Add(1)
- var value ByteView
- var err error
- if peer, ok := g.peers.PickPeer(key); ok {
- value, err = g.getFromPeer(ctx, peer, key)
- if err == nil {
- g.Stats.PeerLoads.Add(1)
- return value, nil
- }
- g.Stats.PeerErrors.Add(1)
- // TODO(bradfitz): log the peer's error? keep
- // log of the past few for /groupcachez? It's
- // probably boring (normal task movement), so not
- // worth logging I imagine.
- }
- value, err = g.getLocally(ctx, key, dest)
- if err != nil {
- g.Stats.LocalLoadErrs.Add(1)
- return nil, err
- }
- g.Stats.LocalLoads.Add(1)
- destPopulated = true // only one caller of load gets this return value
- g.populateCache(key, value, &g.mainCache)
- return value, nil
- })
- if err == nil {
- value = viewi.(ByteView)
- }
- return
-}
-
-func (g *Group) getLocally(ctx Context, key string, dest Sink) (ByteView, error) {
- err := g.getter.Get(ctx, key, dest)
- if err != nil {
- return ByteView{}, err
- }
- return dest.view()
-}
-
-func (g *Group) getFromPeer(ctx Context, peer ProtoGetter, key string) (ByteView, error) {
- req := &pb.GetRequest{
- Group: &g.name,
- Key: &key,
- }
- res := &pb.GetResponse{}
- err := peer.Get(ctx, req, res)
- if err != nil {
- return ByteView{}, err
- }
- value := ByteView{b: res.Value}
- // TODO(bradfitz): use res.MinuteQps or something smart to
- // conditionally populate hotCache. For now just do it some
- // percentage of the time.
- if rand.Intn(10) == 0 {
- g.populateCache(key, value, &g.hotCache)
- }
- return value, nil
-}
-
-func (g *Group) lookupCache(key string) (value ByteView, ok bool) {
- if g.cacheBytes <= 0 {
- return
- }
- value, ok = g.mainCache.get(key)
- if ok {
- return
- }
- value, ok = g.hotCache.get(key)
- return
-}
-
-func (g *Group) populateCache(key string, value ByteView, cache *cache) {
- if g.cacheBytes <= 0 {
- return
- }
- cache.add(key, value)
-
- // Evict items from cache(s) if necessary.
- for {
- mainBytes := g.mainCache.bytes()
- hotBytes := g.hotCache.bytes()
- if mainBytes+hotBytes <= g.cacheBytes {
- return
- }
-
- // TODO(bradfitz): this is good-enough-for-now logic.
- // It should be something based on measurements and/or
- // respecting the costs of different resources.
- victim := &g.mainCache
- if hotBytes > mainBytes/8 {
- victim = &g.hotCache
- }
- victim.removeOldest()
- }
-}
-
-// CacheType represents a type of cache.
-type CacheType int
-
-const (
- // The MainCache is the cache for items that this peer is the
- // owner for.
- MainCache CacheType = iota + 1
-
- // The HotCache is the cache for items that seem popular
- // enough to replicate to this node, even though it's not the
- // owner.
- HotCache
-)
-
-// CacheStats returns stats about the provided cache within the group.
-func (g *Group) CacheStats(which CacheType) CacheStats {
- switch which {
- case MainCache:
- return g.mainCache.stats()
- case HotCache:
- return g.hotCache.stats()
- default:
- return CacheStats{}
- }
-}
-
-// cache is a wrapper around an *lru.Cache that adds synchronization,
-// makes values always be ByteView, and counts the size of all keys and
-// values.
-type cache struct {
- mu sync.RWMutex
- nbytes int64 // of all keys and values
- lru *lru.Cache
- nhit, nget int64
- nevict int64 // number of evictions
-}
-
-func (c *cache) stats() CacheStats {
- c.mu.RLock()
- defer c.mu.RUnlock()
- return CacheStats{
- Bytes: c.nbytes,
- Items: c.itemsLocked(),
- Gets: c.nget,
- Hits: c.nhit,
- Evictions: c.nevict,
- }
-}
-
-func (c *cache) add(key string, value ByteView) {
- c.mu.Lock()
- defer c.mu.Unlock()
- if c.lru == nil {
- c.lru = &lru.Cache{
- OnEvicted: func(key lru.Key, value interface{}) {
- val := value.(ByteView)
- c.nbytes -= int64(len(key.(string))) + int64(val.Len())
- c.nevict++
- },
- }
- }
- c.lru.Add(key, value)
- c.nbytes += int64(len(key)) + int64(value.Len())
-}
-
-func (c *cache) get(key string) (value ByteView, ok bool) {
- c.mu.Lock()
- defer c.mu.Unlock()
- c.nget++
- if c.lru == nil {
- return
- }
- vi, ok := c.lru.Get(key)
- if !ok {
- return
- }
- c.nhit++
- return vi.(ByteView), true
-}
-
-func (c *cache) removeOldest() {
- c.mu.Lock()
- defer c.mu.Unlock()
- if c.lru != nil {
- c.lru.RemoveOldest()
- }
-}
-
-func (c *cache) bytes() int64 {
- c.mu.RLock()
- defer c.mu.RUnlock()
- return c.nbytes
-}
-
-func (c *cache) items() int64 {
- c.mu.RLock()
- defer c.mu.RUnlock()
- return c.itemsLocked()
-}
-
-func (c *cache) itemsLocked() int64 {
- if c.lru == nil {
- return 0
- }
- return int64(c.lru.Len())
-}
-
-// An AtomicInt is an int64 to be accessed atomically.
-type AtomicInt int64
-
-// Add atomically adds n to i.
-func (i *AtomicInt) Add(n int64) {
- atomic.AddInt64((*int64)(i), n)
-}
-
-// Get atomically gets the value of i.
-func (i *AtomicInt) Get() int64 {
- return atomic.LoadInt64((*int64)(i))
-}
-
-func (i *AtomicInt) String() string {
- return strconv.FormatInt(i.Get(), 10)
-}
-
-// CacheStats are returned by stats accessors on Group.
-type CacheStats struct {
- Bytes int64
- Items int64
- Gets int64
- Hits int64
- Evictions int64
-}
diff --git a/vendor/github.com/golang/groupcache/groupcache_test.go b/vendor/github.com/golang/groupcache/groupcache_test.go
deleted file mode 100644
index 3a4ecc2cc..000000000
--- a/vendor/github.com/golang/groupcache/groupcache_test.go
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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.
-*/
-
-// Tests for groupcache.
-
-package groupcache
-
-import (
- "errors"
- "fmt"
- "hash/crc32"
- "math/rand"
- "reflect"
- "sync"
- "testing"
- "time"
-
- "github.com/golang/protobuf/proto"
-
- pb "github.com/golang/groupcache/groupcachepb"
- testpb "github.com/golang/groupcache/testpb"
-)
-
-var (
- once sync.Once
- stringGroup, protoGroup Getter
-
- stringc = make(chan string)
-
- dummyCtx Context
-
- // cacheFills is the number of times stringGroup or
- // protoGroup's Getter have been called. Read using the
- // cacheFills function.
- cacheFills AtomicInt
-)
-
-const (
- stringGroupName = "string-group"
- protoGroupName = "proto-group"
- testMessageType = "google3/net/groupcache/go/test_proto.TestMessage"
- fromChan = "from-chan"
- cacheSize = 1 << 20
-)
-
-func testSetup() {
- stringGroup = NewGroup(stringGroupName, cacheSize, GetterFunc(func(_ Context, key string, dest Sink) error {
- if key == fromChan {
- key = <-stringc
- }
- cacheFills.Add(1)
- return dest.SetString("ECHO:" + key)
- }))
-
- protoGroup = NewGroup(protoGroupName, cacheSize, GetterFunc(func(_ Context, key string, dest Sink) error {
- if key == fromChan {
- key = <-stringc
- }
- cacheFills.Add(1)
- return dest.SetProto(&testpb.TestMessage{
- Name: proto.String("ECHO:" + key),
- City: proto.String("SOME-CITY"),
- })
- }))
-}
-
-// tests that a Getter's Get method is only called once with two
-// outstanding callers. This is the string variant.
-func TestGetDupSuppressString(t *testing.T) {
- once.Do(testSetup)
- // Start two getters. The first should block (waiting reading
- // from stringc) and the second should latch on to the first
- // one.
- resc := make(chan string, 2)
- for i := 0; i < 2; i++ {
- go func() {
- var s string
- if err := stringGroup.Get(dummyCtx, fromChan, StringSink(&s)); err != nil {
- resc <- "ERROR:" + err.Error()
- return
- }
- resc <- s
- }()
- }
-
- // Wait a bit so both goroutines get merged together via
- // singleflight.
- // TODO(bradfitz): decide whether there are any non-offensive
- // debug/test hooks that could be added to singleflight to
- // make a sleep here unnecessary.
- time.Sleep(250 * time.Millisecond)
-
- // Unblock the first getter, which should unblock the second
- // as well.
- stringc <- "foo"
-
- for i := 0; i < 2; i++ {
- select {
- case v := <-resc:
- if v != "ECHO:foo" {
- t.Errorf("got %q; want %q", v, "ECHO:foo")
- }
- case <-time.After(5 * time.Second):
- t.Errorf("timeout waiting on getter #%d of 2", i+1)
- }
- }
-}
-
-// tests that a Getter's Get method is only called once with two
-// outstanding callers. This is the proto variant.
-func TestGetDupSuppressProto(t *testing.T) {
- once.Do(testSetup)
- // Start two getters. The first should block (waiting reading
- // from stringc) and the second should latch on to the first
- // one.
- resc := make(chan *testpb.TestMessage, 2)
- for i := 0; i < 2; i++ {
- go func() {
- tm := new(testpb.TestMessage)
- if err := protoGroup.Get(dummyCtx, fromChan, ProtoSink(tm)); err != nil {
- tm.Name = proto.String("ERROR:" + err.Error())
- }
- resc <- tm
- }()
- }
-
- // Wait a bit so both goroutines get merged together via
- // singleflight.
- // TODO(bradfitz): decide whether there are any non-offensive
- // debug/test hooks that could be added to singleflight to
- // make a sleep here unnecessary.
- time.Sleep(250 * time.Millisecond)
-
- // Unblock the first getter, which should unblock the second
- // as well.
- stringc <- "Fluffy"
- want := &testpb.TestMessage{
- Name: proto.String("ECHO:Fluffy"),
- City: proto.String("SOME-CITY"),
- }
- for i := 0; i < 2; i++ {
- select {
- case v := <-resc:
- if !reflect.DeepEqual(v, want) {
- t.Errorf(" Got: %v\nWant: %v", proto.CompactTextString(v), proto.CompactTextString(want))
- }
- case <-time.After(5 * time.Second):
- t.Errorf("timeout waiting on getter #%d of 2", i+1)
- }
- }
-}
-
-func countFills(f func()) int64 {
- fills0 := cacheFills.Get()
- f()
- return cacheFills.Get() - fills0
-}
-
-func TestCaching(t *testing.T) {
- once.Do(testSetup)
- fills := countFills(func() {
- for i := 0; i < 10; i++ {
- var s string
- if err := stringGroup.Get(dummyCtx, "TestCaching-key", StringSink(&s)); err != nil {
- t.Fatal(err)
- }
- }
- })
- if fills != 1 {
- t.Errorf("expected 1 cache fill; got %d", fills)
- }
-}
-
-func TestCacheEviction(t *testing.T) {
- once.Do(testSetup)
- testKey := "TestCacheEviction-key"
- getTestKey := func() {
- var res string
- for i := 0; i < 10; i++ {
- if err := stringGroup.Get(dummyCtx, testKey, StringSink(&res)); err != nil {
- t.Fatal(err)
- }
- }
- }
- fills := countFills(getTestKey)
- if fills != 1 {
- t.Fatalf("expected 1 cache fill; got %d", fills)
- }
-
- g := stringGroup.(*Group)
- evict0 := g.mainCache.nevict
-
- // Trash the cache with other keys.
- var bytesFlooded int64
- // cacheSize/len(testKey) is approximate
- for bytesFlooded < cacheSize+1024 {
- var res string
- key := fmt.Sprintf("dummy-key-%d", bytesFlooded)
- stringGroup.Get(dummyCtx, key, StringSink(&res))
- bytesFlooded += int64(len(key) + len(res))
- }
- evicts := g.mainCache.nevict - evict0
- if evicts <= 0 {
- t.Errorf("evicts = %v; want more than 0", evicts)
- }
-
- // Test that the key is gone.
- fills = countFills(getTestKey)
- if fills != 1 {
- t.Fatalf("expected 1 cache fill after cache trashing; got %d", fills)
- }
-}
-
-type fakePeer struct {
- hits int
- fail bool
-}
-
-func (p *fakePeer) Get(_ Context, in *pb.GetRequest, out *pb.GetResponse) error {
- p.hits++
- if p.fail {
- return errors.New("simulated error from peer")
- }
- out.Value = []byte("got:" + in.GetKey())
- return nil
-}
-
-type fakePeers []ProtoGetter
-
-func (p fakePeers) PickPeer(key string) (peer ProtoGetter, ok bool) {
- if len(p) == 0 {
- return
- }
- n := crc32.Checksum([]byte(key), crc32.IEEETable) % uint32(len(p))
- return p[n], p[n] != nil
-}
-
-// tests that peers (virtual, in-process) are hit, and how much.
-func TestPeers(t *testing.T) {
- once.Do(testSetup)
- rand.Seed(123)
- peer0 := &fakePeer{}
- peer1 := &fakePeer{}
- peer2 := &fakePeer{}
- peerList := fakePeers([]ProtoGetter{peer0, peer1, peer2, nil})
- const cacheSize = 0 // disabled
- localHits := 0
- getter := func(_ Context, key string, dest Sink) error {
- localHits++
- return dest.SetString("got:" + key)
- }
- testGroup := newGroup("TestPeers-group", cacheSize, GetterFunc(getter), peerList)
- run := func(name string, n int, wantSummary string) {
- // Reset counters
- localHits = 0
- for _, p := range []*fakePeer{peer0, peer1, peer2} {
- p.hits = 0
- }
-
- for i := 0; i < n; i++ {
- key := fmt.Sprintf("key-%d", i)
- want := "got:" + key
- var got string
- err := testGroup.Get(dummyCtx, key, StringSink(&got))
- if err != nil {
- t.Errorf("%s: error on key %q: %v", name, key, err)
- continue
- }
- if got != want {
- t.Errorf("%s: for key %q, got %q; want %q", name, key, got, want)
- }
- }
- summary := func() string {
- return fmt.Sprintf("localHits = %d, peers = %d %d %d", localHits, peer0.hits, peer1.hits, peer2.hits)
- }
- if got := summary(); got != wantSummary {
- t.Errorf("%s: got %q; want %q", name, got, wantSummary)
- }
- }
- resetCacheSize := func(maxBytes int64) {
- g := testGroup
- g.cacheBytes = maxBytes
- g.mainCache = cache{}
- g.hotCache = cache{}
- }
-
- // Base case; peers all up, with no problems.
- resetCacheSize(1 << 20)
- run("base", 200, "localHits = 49, peers = 51 49 51")
-
- // Verify cache was hit. All localHits are gone, and some of
- // the peer hits (the ones randomly selected to be maybe hot)
- run("cached_base", 200, "localHits = 0, peers = 49 47 48")
- resetCacheSize(0)
-
- // With one of the peers being down.
- // TODO(bradfitz): on a peer number being unavailable, the
- // consistent hashing should maybe keep trying others to
- // spread the load out. Currently it fails back to local
- // execution if the first consistent-hash slot is unavailable.
- peerList[0] = nil
- run("one_peer_down", 200, "localHits = 100, peers = 0 49 51")
-
- // Failing peer
- peerList[0] = peer0
- peer0.fail = true
- run("peer0_failing", 200, "localHits = 100, peers = 51 49 51")
-}
-
-func TestTruncatingByteSliceTarget(t *testing.T) {
- var buf [100]byte
- s := buf[:]
- if err := stringGroup.Get(dummyCtx, "short", TruncatingByteSliceSink(&s)); err != nil {
- t.Fatal(err)
- }
- if want := "ECHO:short"; string(s) != want {
- t.Errorf("short key got %q; want %q", s, want)
- }
-
- s = buf[:6]
- if err := stringGroup.Get(dummyCtx, "truncated", TruncatingByteSliceSink(&s)); err != nil {
- t.Fatal(err)
- }
- if want := "ECHO:t"; string(s) != want {
- t.Errorf("truncated key got %q; want %q", s, want)
- }
-}
-
-func TestAllocatingByteSliceTarget(t *testing.T) {
- var dst []byte
- sink := AllocatingByteSliceSink(&dst)
-
- inBytes := []byte("some bytes")
- sink.SetBytes(inBytes)
- if want := "some bytes"; string(dst) != want {
- t.Errorf("SetBytes resulted in %q; want %q", dst, want)
- }
- v, err := sink.view()
- if err != nil {
- t.Fatalf("view after SetBytes failed: %v", err)
- }
- if &inBytes[0] == &dst[0] {
- t.Error("inBytes and dst share memory")
- }
- if &inBytes[0] == &v.b[0] {
- t.Error("inBytes and view share memory")
- }
- if &dst[0] == &v.b[0] {
- t.Error("dst and view share memory")
- }
-}
-
-// orderedFlightGroup allows the caller to force the schedule of when
-// orig.Do will be called. This is useful to serialize calls such
-// that singleflight cannot dedup them.
-type orderedFlightGroup struct {
- mu sync.Mutex
- stage1 chan bool
- stage2 chan bool
- orig flightGroup
-}
-
-func (g *orderedFlightGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
- <-g.stage1
- <-g.stage2
- g.mu.Lock()
- defer g.mu.Unlock()
- return g.orig.Do(key, fn)
-}
-
-// TestNoDedup tests invariants on the cache size when singleflight is
-// unable to dedup calls.
-func TestNoDedup(t *testing.T) {
- const testkey = "testkey"
- const testval = "testval"
- g := newGroup("testgroup", 1024, GetterFunc(func(_ Context, key string, dest Sink) error {
- return dest.SetString(testval)
- }), nil)
-
- orderedGroup := &orderedFlightGroup{
- stage1: make(chan bool),
- stage2: make(chan bool),
- orig: g.loadGroup,
- }
- // Replace loadGroup with our wrapper so we can control when
- // loadGroup.Do is entered for each concurrent request.
- g.loadGroup = orderedGroup
-
- // Issue two idential requests concurrently. Since the cache is
- // empty, it will miss. Both will enter load(), but we will only
- // allow one at a time to enter singleflight.Do, so the callback
- // function will be called twice.
- resc := make(chan string, 2)
- for i := 0; i < 2; i++ {
- go func() {
- var s string
- if err := g.Get(dummyCtx, testkey, StringSink(&s)); err != nil {
- resc <- "ERROR:" + err.Error()
- return
- }
- resc <- s
- }()
- }
-
- // Ensure both goroutines have entered the Do routine. This implies
- // both concurrent requests have checked the cache, found it empty,
- // and called load().
- orderedGroup.stage1 <- true
- orderedGroup.stage1 <- true
- orderedGroup.stage2 <- true
- orderedGroup.stage2 <- true
-
- for i := 0; i < 2; i++ {
- if s := <-resc; s != testval {
- t.Errorf("result is %s want %s", s, testval)
- }
- }
-
- const wantItems = 1
- if g.mainCache.items() != wantItems {
- t.Errorf("mainCache has %d items, want %d", g.mainCache.items(), wantItems)
- }
-
- // If the singleflight callback doesn't double-check the cache again
- // upon entry, we would increment nbytes twice but the entry would
- // only be in the cache once.
- const wantBytes = int64(len(testkey) + len(testval))
- if g.mainCache.nbytes != wantBytes {
- t.Errorf("cache has %d bytes, want %d", g.mainCache.nbytes, wantBytes)
- }
-}
-
-// TODO(bradfitz): port the Google-internal full integration test into here,
-// using HTTP requests instead of our RPC system.
diff --git a/vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go b/vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go
deleted file mode 100644
index 520d1ee9a..000000000
--- a/vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Code generated by protoc-gen-go.
-// source: groupcache.proto
-// DO NOT EDIT!
-
-package groupcachepb
-
-import proto "github.com/golang/protobuf/proto"
-import json "encoding/json"
-import math "math"
-
-// Reference proto, json, and math imports to suppress error if they are not otherwise used.
-var _ = proto.Marshal
-var _ = &json.SyntaxError{}
-var _ = math.Inf
-
-type GetRequest struct {
- Group *string `protobuf:"bytes,1,req,name=group" json:"group,omitempty"`
- Key *string `protobuf:"bytes,2,req,name=key" json:"key,omitempty"`
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *GetRequest) Reset() { *m = GetRequest{} }
-func (m *GetRequest) String() string { return proto.CompactTextString(m) }
-func (*GetRequest) ProtoMessage() {}
-
-func (m *GetRequest) GetGroup() string {
- if m != nil && m.Group != nil {
- return *m.Group
- }
- return ""
-}
-
-func (m *GetRequest) GetKey() string {
- if m != nil && m.Key != nil {
- return *m.Key
- }
- return ""
-}
-
-type GetResponse struct {
- Value []byte `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
- MinuteQps *float64 `protobuf:"fixed64,2,opt,name=minute_qps" json:"minute_qps,omitempty"`
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *GetResponse) Reset() { *m = GetResponse{} }
-func (m *GetResponse) String() string { return proto.CompactTextString(m) }
-func (*GetResponse) ProtoMessage() {}
-
-func (m *GetResponse) GetValue() []byte {
- if m != nil {
- return m.Value
- }
- return nil
-}
-
-func (m *GetResponse) GetMinuteQps() float64 {
- if m != nil && m.MinuteQps != nil {
- return *m.MinuteQps
- }
- return 0
-}
-
-func init() {
-}
diff --git a/vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto b/vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto
deleted file mode 100644
index b5bdff94f..000000000
--- a/vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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.
-*/
-
-syntax = "proto2";
-
-package groupcachepb;
-
-message GetRequest {
- required string group = 1;
- required string key = 2; // not actually required/guaranteed to be UTF-8
-}
-
-message GetResponse {
- optional bytes value = 1;
- optional double minute_qps = 2;
-}
-
-service GroupCache {
- rpc Get(GetRequest) returns (GetResponse) {
- };
-}
diff --git a/vendor/github.com/golang/groupcache/http.go b/vendor/github.com/golang/groupcache/http.go
deleted file mode 100644
index 14eb345a8..000000000
--- a/vendor/github.com/golang/groupcache/http.go
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-Copyright 2013 Google Inc.
-
-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 groupcache
-
-import (
- "bytes"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "sync"
-
- "github.com/golang/groupcache/consistenthash"
- pb "github.com/golang/groupcache/groupcachepb"
- "github.com/golang/protobuf/proto"
-)
-
-const defaultBasePath = "/_groupcache/"
-
-const defaultReplicas = 50
-
-// HTTPPool implements PeerPicker for a pool of HTTP peers.
-type HTTPPool struct {
- // Context optionally specifies a context for the server to use when it
- // receives a request.
- // If nil, the server uses a nil Context.
- Context func(*http.Request) Context
-
- // Transport optionally specifies an http.RoundTripper for the client
- // to use when it makes a request.
- // If nil, the client uses http.DefaultTransport.
- Transport func(Context) http.RoundTripper
-
- // this peer's base URL, e.g. "https://example.net:8000"
- self string
-
- // opts specifies the options.
- opts HTTPPoolOptions
-
- mu sync.Mutex // guards peers and httpGetters
- peers *consistenthash.Map
- httpGetters map[string]*httpGetter // keyed by e.g. "http://10.0.0.2:8008"
-}
-
-// HTTPPoolOptions are the configurations of a HTTPPool.
-type HTTPPoolOptions struct {
- // BasePath specifies the HTTP path that will serve groupcache requests.
- // If blank, it defaults to "/_groupcache/".
- BasePath string
-
- // Replicas specifies the number of key replicas on the consistent hash.
- // If blank, it defaults to 50.
- Replicas int
-
- // HashFn specifies the hash function of the consistent hash.
- // If blank, it defaults to crc32.ChecksumIEEE.
- HashFn consistenthash.Hash
-}
-
-// NewHTTPPool initializes an HTTP pool of peers, and registers itself as a PeerPicker.
-// For convenience, it also registers itself as an http.Handler with http.DefaultServeMux.
-// The self argument be a valid base URL that points to the current server,
-// for example "http://example.net:8000".
-func NewHTTPPool(self string) *HTTPPool {
- p := NewHTTPPoolOpts(self, nil)
- http.Handle(p.opts.BasePath, p)
- return p
-}
-
-var httpPoolMade bool
-
-// NewHTTPPoolOpts initializes an HTTP pool of peers with the given options.
-// Unlike NewHTTPPool, this function does not register the created pool as an HTTP handler.
-// The returned *HTTPPool implements http.Handler and must be registered using http.Handle.
-func NewHTTPPoolOpts(self string, o *HTTPPoolOptions) *HTTPPool {
- if httpPoolMade {
- panic("groupcache: NewHTTPPool must be called only once")
- }
- httpPoolMade = true
-
- p := &HTTPPool{
- self: self,
- httpGetters: make(map[string]*httpGetter),
- }
- if o != nil {
- p.opts = *o
- }
- if p.opts.BasePath == "" {
- p.opts.BasePath = defaultBasePath
- }
- if p.opts.Replicas == 0 {
- p.opts.Replicas = defaultReplicas
- }
- p.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn)
-
- RegisterPeerPicker(func() PeerPicker { return p })
- return p
-}
-
-// Set updates the pool's list of peers.
-// Each peer value should be a valid base URL,
-// for example "http://example.net:8000".
-func (p *HTTPPool) Set(peers ...string) {
- p.mu.Lock()
- defer p.mu.Unlock()
- p.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn)
- p.peers.Add(peers...)
- p.httpGetters = make(map[string]*httpGetter, len(peers))
- for _, peer := range peers {
- p.httpGetters[peer] = &httpGetter{transport: p.Transport, baseURL: peer + p.opts.BasePath}
- }
-}
-
-func (p *HTTPPool) PickPeer(key string) (ProtoGetter, bool) {
- p.mu.Lock()
- defer p.mu.Unlock()
- if p.peers.IsEmpty() {
- return nil, false
- }
- if peer := p.peers.Get(key); peer != p.self {
- return p.httpGetters[peer], true
- }
- return nil, false
-}
-
-func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // Parse request.
- if !strings.HasPrefix(r.URL.Path, p.opts.BasePath) {
- panic("HTTPPool serving unexpected path: " + r.URL.Path)
- }
- parts := strings.SplitN(r.URL.Path[len(p.opts.BasePath):], "/", 2)
- if len(parts) != 2 {
- http.Error(w, "bad request", http.StatusBadRequest)
- return
- }
- groupName := parts[0]
- key := parts[1]
-
- // Fetch the value for this group/key.
- group := GetGroup(groupName)
- if group == nil {
- http.Error(w, "no such group: "+groupName, http.StatusNotFound)
- return
- }
- var ctx Context
- if p.Context != nil {
- ctx = p.Context(r)
- }
-
- group.Stats.ServerRequests.Add(1)
- var value []byte
- err := group.Get(ctx, key, AllocatingByteSliceSink(&value))
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- // Write the value to the response body as a proto message.
- body, err := proto.Marshal(&pb.GetResponse{Value: value})
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Type", "application/x-protobuf")
- w.Write(body)
-}
-
-type httpGetter struct {
- transport func(Context) http.RoundTripper
- baseURL string
-}
-
-var bufferPool = sync.Pool{
- New: func() interface{} { return new(bytes.Buffer) },
-}
-
-func (h *httpGetter) Get(context Context, in *pb.GetRequest, out *pb.GetResponse) error {
- u := fmt.Sprintf(
- "%v%v/%v",
- h.baseURL,
- url.QueryEscape(in.GetGroup()),
- url.QueryEscape(in.GetKey()),
- )
- req, err := http.NewRequest("GET", u, nil)
- if err != nil {
- return err
- }
- tr := http.DefaultTransport
- if h.transport != nil {
- tr = h.transport(context)
- }
- res, err := tr.RoundTrip(req)
- if err != nil {
- return err
- }
- defer res.Body.Close()
- if res.StatusCode != http.StatusOK {
- return fmt.Errorf("server returned: %v", res.Status)
- }
- b := bufferPool.Get().(*bytes.Buffer)
- b.Reset()
- defer bufferPool.Put(b)
- _, err = io.Copy(b, res.Body)
- if err != nil {
- return fmt.Errorf("reading response body: %v", err)
- }
- err = proto.Unmarshal(b.Bytes(), out)
- if err != nil {
- return fmt.Errorf("decoding response body: %v", err)
- }
- return nil
-}
diff --git a/vendor/github.com/golang/groupcache/http_test.go b/vendor/github.com/golang/groupcache/http_test.go
deleted file mode 100644
index b42edd7f0..000000000
--- a/vendor/github.com/golang/groupcache/http_test.go
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
-Copyright 2013 Google Inc.
-
-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 groupcache
-
-import (
- "errors"
- "flag"
- "log"
- "net"
- "net/http"
- "os"
- "os/exec"
- "strconv"
- "strings"
- "sync"
- "testing"
- "time"
-)
-
-var (
- peerAddrs = flag.String("test_peer_addrs", "", "Comma-separated list of peer addresses; used by TestHTTPPool")
- peerIndex = flag.Int("test_peer_index", -1, "Index of which peer this child is; used by TestHTTPPool")
- peerChild = flag.Bool("test_peer_child", false, "True if running as a child process; used by TestHTTPPool")
-)
-
-func TestHTTPPool(t *testing.T) {
- if *peerChild {
- beChildForTestHTTPPool()
- os.Exit(0)
- }
-
- const (
- nChild = 4
- nGets = 100
- )
-
- var childAddr []string
- for i := 0; i < nChild; i++ {
- childAddr = append(childAddr, pickFreeAddr(t))
- }
-
- var cmds []*exec.Cmd
- var wg sync.WaitGroup
- for i := 0; i < nChild; i++ {
- cmd := exec.Command(os.Args[0],
- "--test.run=TestHTTPPool",
- "--test_peer_child",
- "--test_peer_addrs="+strings.Join(childAddr, ","),
- "--test_peer_index="+strconv.Itoa(i),
- )
- cmds = append(cmds, cmd)
- wg.Add(1)
- if err := cmd.Start(); err != nil {
- t.Fatal("failed to start child process: ", err)
- }
- go awaitAddrReady(t, childAddr[i], &wg)
- }
- defer func() {
- for i := 0; i < nChild; i++ {
- if cmds[i].Process != nil {
- cmds[i].Process.Kill()
- }
- }
- }()
- wg.Wait()
-
- // Use a dummy self address so that we don't handle gets in-process.
- p := NewHTTPPool("should-be-ignored")
- p.Set(addrToURL(childAddr)...)
-
- // Dummy getter function. Gets should go to children only.
- // The only time this process will handle a get is when the
- // children can't be contacted for some reason.
- getter := GetterFunc(func(ctx Context, key string, dest Sink) error {
- return errors.New("parent getter called; something's wrong")
- })
- g := NewGroup("httpPoolTest", 1<<20, getter)
-
- for _, key := range testKeys(nGets) {
- var value string
- if err := g.Get(nil, key, StringSink(&value)); err != nil {
- t.Fatal(err)
- }
- if suffix := ":" + key; !strings.HasSuffix(value, suffix) {
- t.Errorf("Get(%q) = %q, want value ending in %q", key, value, suffix)
- }
- t.Logf("Get key=%q, value=%q (peer:key)", key, value)
- }
-}
-
-func testKeys(n int) (keys []string) {
- keys = make([]string, n)
- for i := range keys {
- keys[i] = strconv.Itoa(i)
- }
- return
-}
-
-func beChildForTestHTTPPool() {
- addrs := strings.Split(*peerAddrs, ",")
-
- p := NewHTTPPool("http://" + addrs[*peerIndex])
- p.Set(addrToURL(addrs)...)
-
- getter := GetterFunc(func(ctx Context, key string, dest Sink) error {
- dest.SetString(strconv.Itoa(*peerIndex) + ":" + key)
- return nil
- })
- NewGroup("httpPoolTest", 1<<20, getter)
-
- log.Fatal(http.ListenAndServe(addrs[*peerIndex], p))
-}
-
-// This is racy. Another process could swoop in and steal the port between the
-// call to this function and the next listen call. Should be okay though.
-// The proper way would be to pass the l.File() as ExtraFiles to the child
-// process, and then close your copy once the child starts.
-func pickFreeAddr(t *testing.T) string {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatal(err)
- }
- defer l.Close()
- return l.Addr().String()
-}
-
-func addrToURL(addr []string) []string {
- url := make([]string, len(addr))
- for i := range addr {
- url[i] = "http://" + addr[i]
- }
- return url
-}
-
-func awaitAddrReady(t *testing.T, addr string, wg *sync.WaitGroup) {
- defer wg.Done()
- const max = 1 * time.Second
- tries := 0
- for {
- tries++
- c, err := net.Dial("tcp", addr)
- if err == nil {
- c.Close()
- return
- }
- delay := time.Duration(tries) * 25 * time.Millisecond
- if delay > max {
- delay = max
- }
- time.Sleep(delay)
- }
-}
diff --git a/vendor/github.com/golang/groupcache/lru/lru.go b/vendor/github.com/golang/groupcache/lru/lru.go
deleted file mode 100644
index cdfe2991f..000000000
--- a/vendor/github.com/golang/groupcache/lru/lru.go
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
-Copyright 2013 Google Inc.
-
-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 lru implements an LRU cache.
-package lru
-
-import "container/list"
-
-// Cache is an LRU cache. It is not safe for concurrent access.
-type Cache struct {
- // MaxEntries is the maximum number of cache entries before
- // an item is evicted. Zero means no limit.
- MaxEntries int
-
- // OnEvicted optionally specificies a callback function to be
- // executed when an entry is purged from the cache.
- OnEvicted func(key Key, value interface{})
-
- ll *list.List
- cache map[interface{}]*list.Element
-}
-
-// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
-type Key interface{}
-
-type entry struct {
- key Key
- value interface{}
-}
-
-// New creates a new Cache.
-// If maxEntries is zero, the cache has no limit and it's assumed
-// that eviction is done by the caller.
-func New(maxEntries int) *Cache {
- return &Cache{
- MaxEntries: maxEntries,
- ll: list.New(),
- cache: make(map[interface{}]*list.Element),
- }
-}
-
-// Add adds a value to the cache.
-func (c *Cache) Add(key Key, value interface{}) {
- if c.cache == nil {
- c.cache = make(map[interface{}]*list.Element)
- c.ll = list.New()
- }
- if ee, ok := c.cache[key]; ok {
- c.ll.MoveToFront(ee)
- ee.Value.(*entry).value = value
- return
- }
- ele := c.ll.PushFront(&entry{key, value})
- c.cache[key] = ele
- if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
- c.RemoveOldest()
- }
-}
-
-// Get looks up a key's value from the cache.
-func (c *Cache) Get(key Key) (value interface{}, ok bool) {
- if c.cache == nil {
- return
- }
- if ele, hit := c.cache[key]; hit {
- c.ll.MoveToFront(ele)
- return ele.Value.(*entry).value, true
- }
- return
-}
-
-// Remove removes the provided key from the cache.
-func (c *Cache) Remove(key Key) {
- if c.cache == nil {
- return
- }
- if ele, hit := c.cache[key]; hit {
- c.removeElement(ele)
- }
-}
-
-// RemoveOldest removes the oldest item from the cache.
-func (c *Cache) RemoveOldest() {
- if c.cache == nil {
- return
- }
- ele := c.ll.Back()
- if ele != nil {
- c.removeElement(ele)
- }
-}
-
-func (c *Cache) removeElement(e *list.Element) {
- c.ll.Remove(e)
- kv := e.Value.(*entry)
- delete(c.cache, kv.key)
- if c.OnEvicted != nil {
- c.OnEvicted(kv.key, kv.value)
- }
-}
-
-// Len returns the number of items in the cache.
-func (c *Cache) Len() int {
- if c.cache == nil {
- return 0
- }
- return c.ll.Len()
-}
diff --git a/vendor/github.com/golang/groupcache/lru/lru_test.go b/vendor/github.com/golang/groupcache/lru/lru_test.go
deleted file mode 100644
index 98a2656e8..000000000
--- a/vendor/github.com/golang/groupcache/lru/lru_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
-Copyright 2013 Google Inc.
-
-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 lru
-
-import (
- "testing"
-)
-
-type simpleStruct struct {
- int
- string
-}
-
-type complexStruct struct {
- int
- simpleStruct
-}
-
-var getTests = []struct {
- name string
- keyToAdd interface{}
- keyToGet interface{}
- expectedOk bool
-}{
- {"string_hit", "myKey", "myKey", true},
- {"string_miss", "myKey", "nonsense", false},
- {"simple_struct_hit", simpleStruct{1, "two"}, simpleStruct{1, "two"}, true},
- {"simeple_struct_miss", simpleStruct{1, "two"}, simpleStruct{0, "noway"}, false},
- {"complex_struct_hit", complexStruct{1, simpleStruct{2, "three"}},
- complexStruct{1, simpleStruct{2, "three"}}, true},
-}
-
-func TestGet(t *testing.T) {
- for _, tt := range getTests {
- lru := New(0)
- lru.Add(tt.keyToAdd, 1234)
- val, ok := lru.Get(tt.keyToGet)
- if ok != tt.expectedOk {
- t.Fatalf("%s: cache hit = %v; want %v", tt.name, ok, !ok)
- } else if ok && val != 1234 {
- t.Fatalf("%s expected get to return 1234 but got %v", tt.name, val)
- }
- }
-}
-
-func TestRemove(t *testing.T) {
- lru := New(0)
- lru.Add("myKey", 1234)
- if val, ok := lru.Get("myKey"); !ok {
- t.Fatal("TestRemove returned no match")
- } else if val != 1234 {
- t.Fatalf("TestRemove failed. Expected %d, got %v", 1234, val)
- }
-
- lru.Remove("myKey")
- if _, ok := lru.Get("myKey"); ok {
- t.Fatal("TestRemove returned a removed entry")
- }
-}
diff --git a/vendor/github.com/golang/groupcache/peers.go b/vendor/github.com/golang/groupcache/peers.go
deleted file mode 100644
index a74a79b8f..000000000
--- a/vendor/github.com/golang/groupcache/peers.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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.
-*/
-
-// peers.go defines how processes find and communicate with their peers.
-
-package groupcache
-
-import (
- pb "github.com/golang/groupcache/groupcachepb"
-)
-
-// Context is an opaque value passed through calls to the
-// ProtoGetter. It may be nil if your ProtoGetter implementation does
-// not require a context.
-type Context interface{}
-
-// ProtoGetter is the interface that must be implemented by a peer.
-type ProtoGetter interface {
- Get(context Context, in *pb.GetRequest, out *pb.GetResponse) error
-}
-
-// PeerPicker is the interface that must be implemented to locate
-// the peer that owns a specific key.
-type PeerPicker interface {
- // PickPeer returns the peer that owns the specific key
- // and true to indicate that a remote peer was nominated.
- // It returns nil, false if the key owner is the current peer.
- PickPeer(key string) (peer ProtoGetter, ok bool)
-}
-
-// NoPeers is an implementation of PeerPicker that never finds a peer.
-type NoPeers struct{}
-
-func (NoPeers) PickPeer(key string) (peer ProtoGetter, ok bool) { return }
-
-var (
- portPicker func() PeerPicker
-)
-
-// RegisterPeerPicker registers the peer initialization function.
-// It is called once, when the first group is created.
-func RegisterPeerPicker(fn func() PeerPicker) {
- if portPicker != nil {
- panic("RegisterPeerPicker called more than once")
- }
- portPicker = fn
-}
-
-func getPeers() PeerPicker {
- if portPicker == nil {
- return NoPeers{}
- }
- pk := portPicker()
- if pk == nil {
- pk = NoPeers{}
- }
- return pk
-}
diff --git a/vendor/github.com/golang/groupcache/singleflight/singleflight.go b/vendor/github.com/golang/groupcache/singleflight/singleflight.go
deleted file mode 100644
index ff2c2ee4f..000000000
--- a/vendor/github.com/golang/groupcache/singleflight/singleflight.go
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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 singleflight provides a duplicate function call suppression
-// mechanism.
-package singleflight
-
-import "sync"
-
-// call is an in-flight or completed Do call
-type call struct {
- wg sync.WaitGroup
- val interface{}
- err error
-}
-
-// Group represents a class of work and forms a namespace in which
-// units of work can be executed with duplicate suppression.
-type Group struct {
- mu sync.Mutex // protects m
- m map[string]*call // lazily initialized
-}
-
-// Do executes and returns the results of the given function, making
-// sure that only one execution is in-flight for a given key at a
-// time. If a duplicate comes in, the duplicate caller waits for the
-// original to complete and receives the same results.
-func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
- g.mu.Lock()
- if g.m == nil {
- g.m = make(map[string]*call)
- }
- if c, ok := g.m[key]; ok {
- g.mu.Unlock()
- c.wg.Wait()
- return c.val, c.err
- }
- c := new(call)
- c.wg.Add(1)
- g.m[key] = c
- g.mu.Unlock()
-
- c.val, c.err = fn()
- c.wg.Done()
-
- g.mu.Lock()
- delete(g.m, key)
- g.mu.Unlock()
-
- return c.val, c.err
-}
diff --git a/vendor/github.com/golang/groupcache/singleflight/singleflight_test.go b/vendor/github.com/golang/groupcache/singleflight/singleflight_test.go
deleted file mode 100644
index 47b4d3dc0..000000000
--- a/vendor/github.com/golang/groupcache/singleflight/singleflight_test.go
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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 singleflight
-
-import (
- "errors"
- "fmt"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-)
-
-func TestDo(t *testing.T) {
- var g Group
- v, err := g.Do("key", func() (interface{}, error) {
- return "bar", nil
- })
- if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
- t.Errorf("Do = %v; want %v", got, want)
- }
- if err != nil {
- t.Errorf("Do error = %v", err)
- }
-}
-
-func TestDoErr(t *testing.T) {
- var g Group
- someErr := errors.New("Some error")
- v, err := g.Do("key", func() (interface{}, error) {
- return nil, someErr
- })
- if err != someErr {
- t.Errorf("Do error = %v; want someErr", err)
- }
- if v != nil {
- t.Errorf("unexpected non-nil value %#v", v)
- }
-}
-
-func TestDoDupSuppress(t *testing.T) {
- var g Group
- c := make(chan string)
- var calls int32
- fn := func() (interface{}, error) {
- atomic.AddInt32(&calls, 1)
- return <-c, nil
- }
-
- const n = 10
- var wg sync.WaitGroup
- for i := 0; i < n; i++ {
- wg.Add(1)
- go func() {
- v, err := g.Do("key", fn)
- if err != nil {
- t.Errorf("Do error: %v", err)
- }
- if v.(string) != "bar" {
- t.Errorf("got %q; want %q", v, "bar")
- }
- wg.Done()
- }()
- }
- time.Sleep(100 * time.Millisecond) // let goroutines above block
- c <- "bar"
- wg.Wait()
- if got := atomic.LoadInt32(&calls); got != 1 {
- t.Errorf("number of calls = %d; want 1", got)
- }
-}
diff --git a/vendor/github.com/golang/groupcache/sinks.go b/vendor/github.com/golang/groupcache/sinks.go
deleted file mode 100644
index cb42b41b4..000000000
--- a/vendor/github.com/golang/groupcache/sinks.go
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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 groupcache
-
-import (
- "errors"
-
- "github.com/golang/protobuf/proto"
-)
-
-// A Sink receives data from a Get call.
-//
-// Implementation of Getter must call exactly one of the Set methods
-// on success.
-type Sink interface {
- // SetString sets the value to s.
- SetString(s string) error
-
- // SetBytes sets the value to the contents of v.
- // The caller retains ownership of v.
- SetBytes(v []byte) error
-
- // SetProto sets the value to the encoded version of m.
- // The caller retains ownership of m.
- SetProto(m proto.Message) error
-
- // view returns a frozen view of the bytes for caching.
- view() (ByteView, error)
-}
-
-func cloneBytes(b []byte) []byte {
- c := make([]byte, len(b))
- copy(c, b)
- return c
-}
-
-func setSinkView(s Sink, v ByteView) error {
- // A viewSetter is a Sink that can also receive its value from
- // a ByteView. This is a fast path to minimize copies when the
- // item was already cached locally in memory (where it's
- // cached as a ByteView)
- type viewSetter interface {
- setView(v ByteView) error
- }
- if vs, ok := s.(viewSetter); ok {
- return vs.setView(v)
- }
- if v.b != nil {
- return s.SetBytes(v.b)
- }
- return s.SetString(v.s)
-}
-
-// StringSink returns a Sink that populates the provided string pointer.
-func StringSink(sp *string) Sink {
- return &stringSink{sp: sp}
-}
-
-type stringSink struct {
- sp *string
- v ByteView
- // TODO(bradfitz): track whether any Sets were called.
-}
-
-func (s *stringSink) view() (ByteView, error) {
- // TODO(bradfitz): return an error if no Set was called
- return s.v, nil
-}
-
-func (s *stringSink) SetString(v string) error {
- s.v.b = nil
- s.v.s = v
- *s.sp = v
- return nil
-}
-
-func (s *stringSink) SetBytes(v []byte) error {
- return s.SetString(string(v))
-}
-
-func (s *stringSink) SetProto(m proto.Message) error {
- b, err := proto.Marshal(m)
- if err != nil {
- return err
- }
- s.v.b = b
- *s.sp = string(b)
- return nil
-}
-
-// ByteViewSink returns a Sink that populates a ByteView.
-func ByteViewSink(dst *ByteView) Sink {
- if dst == nil {
- panic("nil dst")
- }
- return &byteViewSink{dst: dst}
-}
-
-type byteViewSink struct {
- dst *ByteView
-
- // if this code ever ends up tracking that at least one set*
- // method was called, don't make it an error to call set
- // methods multiple times. Lorry's payload.go does that, and
- // it makes sense. The comment at the top of this file about
- // "exactly one of the Set methods" is overly strict. We
- // really care about at least once (in a handler), but if
- // multiple handlers fail (or multiple functions in a program
- // using a Sink), it's okay to re-use the same one.
-}
-
-func (s *byteViewSink) setView(v ByteView) error {
- *s.dst = v
- return nil
-}
-
-func (s *byteViewSink) view() (ByteView, error) {
- return *s.dst, nil
-}
-
-func (s *byteViewSink) SetProto(m proto.Message) error {
- b, err := proto.Marshal(m)
- if err != nil {
- return err
- }
- *s.dst = ByteView{b: b}
- return nil
-}
-
-func (s *byteViewSink) SetBytes(b []byte) error {
- *s.dst = ByteView{b: cloneBytes(b)}
- return nil
-}
-
-func (s *byteViewSink) SetString(v string) error {
- *s.dst = ByteView{s: v}
- return nil
-}
-
-// ProtoSink returns a sink that unmarshals binary proto values into m.
-func ProtoSink(m proto.Message) Sink {
- return &protoSink{
- dst: m,
- }
-}
-
-type protoSink struct {
- dst proto.Message // authorative value
- typ string
-
- v ByteView // encoded
-}
-
-func (s *protoSink) view() (ByteView, error) {
- return s.v, nil
-}
-
-func (s *protoSink) SetBytes(b []byte) error {
- err := proto.Unmarshal(b, s.dst)
- if err != nil {
- return err
- }
- s.v.b = cloneBytes(b)
- s.v.s = ""
- return nil
-}
-
-func (s *protoSink) SetString(v string) error {
- b := []byte(v)
- err := proto.Unmarshal(b, s.dst)
- if err != nil {
- return err
- }
- s.v.b = b
- s.v.s = ""
- return nil
-}
-
-func (s *protoSink) SetProto(m proto.Message) error {
- b, err := proto.Marshal(m)
- if err != nil {
- return err
- }
- // TODO(bradfitz): optimize for same-task case more and write
- // right through? would need to document ownership rules at
- // the same time. but then we could just assign *dst = *m
- // here. This works for now:
- err = proto.Unmarshal(b, s.dst)
- if err != nil {
- return err
- }
- s.v.b = b
- s.v.s = ""
- return nil
-}
-
-// AllocatingByteSliceSink returns a Sink that allocates
-// a byte slice to hold the received value and assigns
-// it to *dst. The memory is not retained by groupcache.
-func AllocatingByteSliceSink(dst *[]byte) Sink {
- return &allocBytesSink{dst: dst}
-}
-
-type allocBytesSink struct {
- dst *[]byte
- v ByteView
-}
-
-func (s *allocBytesSink) view() (ByteView, error) {
- return s.v, nil
-}
-
-func (s *allocBytesSink) setView(v ByteView) error {
- if v.b != nil {
- *s.dst = cloneBytes(v.b)
- } else {
- *s.dst = []byte(v.s)
- }
- s.v = v
- return nil
-}
-
-func (s *allocBytesSink) SetProto(m proto.Message) error {
- b, err := proto.Marshal(m)
- if err != nil {
- return err
- }
- return s.setBytesOwned(b)
-}
-
-func (s *allocBytesSink) SetBytes(b []byte) error {
- return s.setBytesOwned(cloneBytes(b))
-}
-
-func (s *allocBytesSink) setBytesOwned(b []byte) error {
- if s.dst == nil {
- return errors.New("nil AllocatingByteSliceSink *[]byte dst")
- }
- *s.dst = cloneBytes(b) // another copy, protecting the read-only s.v.b view
- s.v.b = b
- s.v.s = ""
- return nil
-}
-
-func (s *allocBytesSink) SetString(v string) error {
- if s.dst == nil {
- return errors.New("nil AllocatingByteSliceSink *[]byte dst")
- }
- *s.dst = []byte(v)
- s.v.b = nil
- s.v.s = v
- return nil
-}
-
-// TruncatingByteSliceSink returns a Sink that writes up to len(*dst)
-// bytes to *dst. If more bytes are available, they're silently
-// truncated. If fewer bytes are available than len(*dst), *dst
-// is shrunk to fit the number of bytes available.
-func TruncatingByteSliceSink(dst *[]byte) Sink {
- return &truncBytesSink{dst: dst}
-}
-
-type truncBytesSink struct {
- dst *[]byte
- v ByteView
-}
-
-func (s *truncBytesSink) view() (ByteView, error) {
- return s.v, nil
-}
-
-func (s *truncBytesSink) SetProto(m proto.Message) error {
- b, err := proto.Marshal(m)
- if err != nil {
- return err
- }
- return s.setBytesOwned(b)
-}
-
-func (s *truncBytesSink) SetBytes(b []byte) error {
- return s.setBytesOwned(cloneBytes(b))
-}
-
-func (s *truncBytesSink) setBytesOwned(b []byte) error {
- if s.dst == nil {
- return errors.New("nil TruncatingByteSliceSink *[]byte dst")
- }
- n := copy(*s.dst, b)
- if n < len(*s.dst) {
- *s.dst = (*s.dst)[:n]
- }
- s.v.b = b
- s.v.s = ""
- return nil
-}
-
-func (s *truncBytesSink) SetString(v string) error {
- if s.dst == nil {
- return errors.New("nil TruncatingByteSliceSink *[]byte dst")
- }
- n := copy(*s.dst, v)
- if n < len(*s.dst) {
- *s.dst = (*s.dst)[:n]
- }
- s.v.b = nil
- s.v.s = v
- return nil
-}
diff --git a/vendor/github.com/golang/groupcache/testpb/test.pb.go b/vendor/github.com/golang/groupcache/testpb/test.pb.go
deleted file mode 100644
index 038040d15..000000000
--- a/vendor/github.com/golang/groupcache/testpb/test.pb.go
+++ /dev/null
@@ -1,235 +0,0 @@
-// Code generated by protoc-gen-go.
-// source: test.proto
-// DO NOT EDIT!
-
-package testpb
-
-import proto "github.com/golang/protobuf/proto"
-import json "encoding/json"
-import math "math"
-
-// Reference proto, json, and math imports to suppress error if they are not otherwise used.
-var _ = proto.Marshal
-var _ = &json.SyntaxError{}
-var _ = math.Inf
-
-type TestMessage struct {
- Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
- City *string `protobuf:"bytes,2,opt,name=city" json:"city,omitempty"`
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *TestMessage) Reset() { *m = TestMessage{} }
-func (m *TestMessage) String() string { return proto.CompactTextString(m) }
-func (*TestMessage) ProtoMessage() {}
-
-func (m *TestMessage) GetName() string {
- if m != nil && m.Name != nil {
- return *m.Name
- }
- return ""
-}
-
-func (m *TestMessage) GetCity() string {
- if m != nil && m.City != nil {
- return *m.City
- }
- return ""
-}
-
-type TestRequest struct {
- Lower *string `protobuf:"bytes,1,req,name=lower" json:"lower,omitempty"`
- RepeatCount *int32 `protobuf:"varint,2,opt,name=repeat_count,def=1" json:"repeat_count,omitempty"`
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *TestRequest) Reset() { *m = TestRequest{} }
-func (m *TestRequest) String() string { return proto.CompactTextString(m) }
-func (*TestRequest) ProtoMessage() {}
-
-const Default_TestRequest_RepeatCount int32 = 1
-
-func (m *TestRequest) GetLower() string {
- if m != nil && m.Lower != nil {
- return *m.Lower
- }
- return ""
-}
-
-func (m *TestRequest) GetRepeatCount() int32 {
- if m != nil && m.RepeatCount != nil {
- return *m.RepeatCount
- }
- return Default_TestRequest_RepeatCount
-}
-
-type TestResponse struct {
- Value *string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *TestResponse) Reset() { *m = TestResponse{} }
-func (m *TestResponse) String() string { return proto.CompactTextString(m) }
-func (*TestResponse) ProtoMessage() {}
-
-func (m *TestResponse) GetValue() string {
- if m != nil && m.Value != nil {
- return *m.Value
- }
- return ""
-}
-
-type CacheStats struct {
- Items *int64 `protobuf:"varint,1,opt,name=items" json:"items,omitempty"`
- Bytes *int64 `protobuf:"varint,2,opt,name=bytes" json:"bytes,omitempty"`
- Gets *int64 `protobuf:"varint,3,opt,name=gets" json:"gets,omitempty"`
- Hits *int64 `protobuf:"varint,4,opt,name=hits" json:"hits,omitempty"`
- Evicts *int64 `protobuf:"varint,5,opt,name=evicts" json:"evicts,omitempty"`
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *CacheStats) Reset() { *m = CacheStats{} }
-func (m *CacheStats) String() string { return proto.CompactTextString(m) }
-func (*CacheStats) ProtoMessage() {}
-
-func (m *CacheStats) GetItems() int64 {
- if m != nil && m.Items != nil {
- return *m.Items
- }
- return 0
-}
-
-func (m *CacheStats) GetBytes() int64 {
- if m != nil && m.Bytes != nil {
- return *m.Bytes
- }
- return 0
-}
-
-func (m *CacheStats) GetGets() int64 {
- if m != nil && m.Gets != nil {
- return *m.Gets
- }
- return 0
-}
-
-func (m *CacheStats) GetHits() int64 {
- if m != nil && m.Hits != nil {
- return *m.Hits
- }
- return 0
-}
-
-func (m *CacheStats) GetEvicts() int64 {
- if m != nil && m.Evicts != nil {
- return *m.Evicts
- }
- return 0
-}
-
-type StatsResponse struct {
- Gets *int64 `protobuf:"varint,1,opt,name=gets" json:"gets,omitempty"`
- CacheHits *int64 `protobuf:"varint,12,opt,name=cache_hits" json:"cache_hits,omitempty"`
- Fills *int64 `protobuf:"varint,2,opt,name=fills" json:"fills,omitempty"`
- TotalAlloc *uint64 `protobuf:"varint,3,opt,name=total_alloc" json:"total_alloc,omitempty"`
- MainCache *CacheStats `protobuf:"bytes,4,opt,name=main_cache" json:"main_cache,omitempty"`
- HotCache *CacheStats `protobuf:"bytes,5,opt,name=hot_cache" json:"hot_cache,omitempty"`
- ServerIn *int64 `protobuf:"varint,6,opt,name=server_in" json:"server_in,omitempty"`
- Loads *int64 `protobuf:"varint,8,opt,name=loads" json:"loads,omitempty"`
- PeerLoads *int64 `protobuf:"varint,9,opt,name=peer_loads" json:"peer_loads,omitempty"`
- PeerErrors *int64 `protobuf:"varint,10,opt,name=peer_errors" json:"peer_errors,omitempty"`
- LocalLoads *int64 `protobuf:"varint,11,opt,name=local_loads" json:"local_loads,omitempty"`
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *StatsResponse) Reset() { *m = StatsResponse{} }
-func (m *StatsResponse) String() string { return proto.CompactTextString(m) }
-func (*StatsResponse) ProtoMessage() {}
-
-func (m *StatsResponse) GetGets() int64 {
- if m != nil && m.Gets != nil {
- return *m.Gets
- }
- return 0
-}
-
-func (m *StatsResponse) GetCacheHits() int64 {
- if m != nil && m.CacheHits != nil {
- return *m.CacheHits
- }
- return 0
-}
-
-func (m *StatsResponse) GetFills() int64 {
- if m != nil && m.Fills != nil {
- return *m.Fills
- }
- return 0
-}
-
-func (m *StatsResponse) GetTotalAlloc() uint64 {
- if m != nil && m.TotalAlloc != nil {
- return *m.TotalAlloc
- }
- return 0
-}
-
-func (m *StatsResponse) GetMainCache() *CacheStats {
- if m != nil {
- return m.MainCache
- }
- return nil
-}
-
-func (m *StatsResponse) GetHotCache() *CacheStats {
- if m != nil {
- return m.HotCache
- }
- return nil
-}
-
-func (m *StatsResponse) GetServerIn() int64 {
- if m != nil && m.ServerIn != nil {
- return *m.ServerIn
- }
- return 0
-}
-
-func (m *StatsResponse) GetLoads() int64 {
- if m != nil && m.Loads != nil {
- return *m.Loads
- }
- return 0
-}
-
-func (m *StatsResponse) GetPeerLoads() int64 {
- if m != nil && m.PeerLoads != nil {
- return *m.PeerLoads
- }
- return 0
-}
-
-func (m *StatsResponse) GetPeerErrors() int64 {
- if m != nil && m.PeerErrors != nil {
- return *m.PeerErrors
- }
- return 0
-}
-
-func (m *StatsResponse) GetLocalLoads() int64 {
- if m != nil && m.LocalLoads != nil {
- return *m.LocalLoads
- }
- return 0
-}
-
-type Empty struct {
- XXX_unrecognized []byte `json:"-"`
-}
-
-func (m *Empty) Reset() { *m = Empty{} }
-func (m *Empty) String() string { return proto.CompactTextString(m) }
-func (*Empty) ProtoMessage() {}
-
-func init() {
-}
diff --git a/vendor/github.com/golang/groupcache/testpb/test.proto b/vendor/github.com/golang/groupcache/testpb/test.proto
deleted file mode 100644
index b9dc6c9a0..000000000
--- a/vendor/github.com/golang/groupcache/testpb/test.proto
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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.
-*/
-
-syntax = "proto2";
-
-package testpb;
-
-message TestMessage {
- optional string name = 1;
- optional string city = 2;
-}
-
-message TestRequest {
- required string lower = 1; // to be returned upper case
- optional int32 repeat_count = 2 [default = 1]; // .. this many times
-}
-
-message TestResponse {
- optional string value = 1;
-}
-
-message CacheStats {
- optional int64 items = 1;
- optional int64 bytes = 2;
- optional int64 gets = 3;
- optional int64 hits = 4;
- optional int64 evicts = 5;
-}
-
-message StatsResponse {
- optional int64 gets = 1;
- optional int64 cache_hits = 12;
- optional int64 fills = 2;
- optional uint64 total_alloc = 3;
- optional CacheStats main_cache = 4;
- optional CacheStats hot_cache = 5;
- optional int64 server_in = 6;
- optional int64 loads = 8;
- optional int64 peer_loads = 9;
- optional int64 peer_errors = 10;
- optional int64 local_loads = 11;
-}
-
-message Empty {}
-
-service GroupCacheTest {
- rpc InitPeers(Empty) returns (Empty) {};
- rpc Get(TestRequest) returns (TestResponse) {};
- rpc GetStats(Empty) returns (StatsResponse) {};
-}
diff --git a/vendor/github.com/gorilla/context/.travis.yml b/vendor/github.com/gorilla/context/.travis.yml
index 24882fc7b..6f440f1e4 100644
--- a/vendor/github.com/gorilla/context/.travis.yml
+++ b/vendor/github.com/gorilla/context/.travis.yml
@@ -7,6 +7,7 @@ matrix:
- go: 1.4
- go: 1.5
- go: 1.6
+ - go: 1.7
- go: tip
allow_failures:
- go: tip
diff --git a/vendor/github.com/gorilla/context/README.md b/vendor/github.com/gorilla/context/README.md
index c60a31b05..08f86693b 100644
--- a/vendor/github.com/gorilla/context/README.md
+++ b/vendor/github.com/gorilla/context/README.md
@@ -4,4 +4,7 @@ context
gorilla/context is a general purpose registry for global request variables.
+> Note: gorilla/context, having been born well before `context.Context` existed, does not play well
+> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`.
+
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
diff --git a/vendor/github.com/gorilla/context/context_test.go b/vendor/github.com/gorilla/context/context_test.go
index 9814c501e..d70e91a23 100644
--- a/vendor/github.com/gorilla/context/context_test.go
+++ b/vendor/github.com/gorilla/context/context_test.go
@@ -69,7 +69,7 @@ func TestContext(t *testing.T) {
// GetAllOk() for empty request
values, ok = GetAllOk(emptyR)
- assertEqual(value, nil)
+ assertEqual(len(values), 0)
assertEqual(ok, false)
// Delete()
diff --git a/vendor/github.com/gorilla/context/doc.go b/vendor/github.com/gorilla/context/doc.go
index 73c740031..448d1bfca 100644
--- a/vendor/github.com/gorilla/context/doc.go
+++ b/vendor/github.com/gorilla/context/doc.go
@@ -5,6 +5,12 @@
/*
Package context stores values shared during a request lifetime.
+Note: gorilla/context, having been born well before `context.Context` existed,
+does not play well > with the shallow copying of the request that
+[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext)
+(added to net/http Go 1.7 onwards) performs. You should either use *just*
+gorilla/context, or moving forward, the new `http.Request.Context()`.
+
For example, a router can set variables extracted from the URL and later
application handlers can access those values, or it can be used to store
sessions values to be saved at the end of a request. There are several
diff --git a/vendor/github.com/gorilla/handlers/.travis.yml b/vendor/github.com/gorilla/handlers/.travis.yml
index 66435ac0b..783020996 100644
--- a/vendor/github.com/gorilla/handlers/.travis.yml
+++ b/vendor/github.com/gorilla/handlers/.travis.yml
@@ -7,11 +7,12 @@ matrix:
- go: 1.5
- go: 1.6
- go: tip
- allow_failures:
- - go: tip
+
+install:
+ - go get golang.org/x/tools/cmd/vet
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d .)
- - go vet $(go list ./... | grep -v /vendor/)
+ - go tool vet .
- go test -v -race ./...
diff --git a/vendor/github.com/gorilla/handlers/cors.go b/vendor/github.com/gorilla/handlers/cors.go
index 1f92d1ad4..d4229a5d9 100644
--- a/vendor/github.com/gorilla/handlers/cors.go
+++ b/vendor/github.com/gorilla/handlers/cors.go
@@ -112,9 +112,6 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set(corsAllowOriginHeader, origin)
- if r.Method == corsOptionMethod {
- return
- }
ch.h.ServeHTTP(w, r)
}
diff --git a/vendor/github.com/gorilla/handlers/cors_test.go b/vendor/github.com/gorilla/handlers/cors_test.go
index c63913eee..ff7eebf48 100644
--- a/vendor/github.com/gorilla/handlers/cors_test.go
+++ b/vendor/github.com/gorilla/handlers/cors_test.go
@@ -104,24 +104,6 @@ func TestCORSHandlerInvalidRequestMethodForPreflightMethodNotAllowed(t *testing.
}
}
-func TestCORSHandlerOptionsRequestMustNotBePassedToNextHandler(t *testing.T) {
- r := newRequest("OPTIONS", "http://www.example.com/")
- r.Header.Set("Origin", r.URL.String())
- r.Header.Set(corsRequestMethodHeader, "GET")
-
- rr := httptest.NewRecorder()
-
- testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- t.Fatal("Options request must not be passed to next handler")
- })
-
- CORS()(testHandler).ServeHTTP(rr, r)
-
- if status := rr.Code; status != http.StatusOK {
- t.Fatalf("bad status: got %v want %v", status, http.StatusOK)
- }
-}
-
func TestCORSHandlerAllowedMethodForPreflight(t *testing.T) {
r := newRequest("OPTIONS", "http://www.example.com/")
r.Header.Set("Origin", r.URL.String())
diff --git a/vendor/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml
index f4084bd81..4dcdacb65 100644
--- a/vendor/github.com/gorilla/mux/.travis.yml
+++ b/vendor/github.com/gorilla/mux/.travis.yml
@@ -10,6 +10,9 @@ matrix:
- go: 1.6
- go: tip
+install:
+ - go get golang.org/x/tools/cmd/vet
+
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d .)
diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go
index 94f5ddd9c..fbb7f19ad 100644
--- a/vendor/github.com/gorilla/mux/mux.go
+++ b/vendor/github.com/gorilla/mux/mux.go
@@ -48,8 +48,6 @@ type Router struct {
namedRoutes map[string]*Route
// See Router.StrictSlash(). This defines the flag for new routes.
strictSlash bool
- // See Router.SkipClean(). This defines the flag for new routes.
- skipClean bool
// If true, do not clear the request context after handling the request
KeepContext bool
}
@@ -75,21 +73,19 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
// When there is a match, the route variables can be retrieved calling
// mux.Vars(request).
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- if !r.skipClean {
- // Clean path to canonical form and redirect.
- if p := cleanPath(req.URL.Path); p != req.URL.Path {
-
- // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
- // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
- // http://code.google.com/p/go/issues/detail?id=5252
- url := *req.URL
- url.Path = p
- p = url.String()
-
- w.Header().Set("Location", p)
- w.WriteHeader(http.StatusMovedPermanently)
- return
- }
+ // Clean path to canonical form and redirect.
+ if p := cleanPath(req.URL.Path); p != req.URL.Path {
+
+ // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
+ // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
+ // http://code.google.com/p/go/issues/detail?id=5252
+ url := *req.URL
+ url.Path = p
+ p = url.String()
+
+ w.Header().Set("Location", p)
+ w.WriteHeader(http.StatusMovedPermanently)
+ return
}
var match RouteMatch
var handler http.Handler
@@ -137,19 +133,6 @@ func (r *Router) StrictSlash(value bool) *Router {
return r
}
-// SkipClean defines the path cleaning behaviour for new routes. The initial
-// value is false. Users should be careful about which routes are not cleaned
-//
-// When true, if the route path is "/path//to", it will remain with the double
-// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
-//
-// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
-// become /fetch/http/xkcd.com/534
-func (r *Router) SkipClean(value bool) *Router {
- r.skipClean = value
- return r
-}
-
// ----------------------------------------------------------------------------
// parentRoute
// ----------------------------------------------------------------------------
@@ -187,7 +170,7 @@ func (r *Router) buildVars(m map[string]string) map[string]string {
// NewRoute registers an empty route.
func (r *Router) NewRoute() *Route {
- route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean}
+ route := &Route{parent: r, strictSlash: r.strictSlash}
r.routes = append(r.routes, route)
return route
}
@@ -374,7 +357,6 @@ func cleanPath(p string) string {
if p[len(p)-1] == '/' && np != "/" {
np += "/"
}
-
return np
}
diff --git a/vendor/github.com/gorilla/mux/mux_test.go b/vendor/github.com/gorilla/mux/mux_test.go
index 777d063c0..a44d03f80 100644
--- a/vendor/github.com/gorilla/mux/mux_test.go
+++ b/vendor/github.com/gorilla/mux/mux_test.go
@@ -1386,24 +1386,6 @@ func Test301Redirect(t *testing.T) {
}
}
-func TestSkipClean(t *testing.T) {
- func1 := func(w http.ResponseWriter, r *http.Request) {}
- func2 := func(w http.ResponseWriter, r *http.Request) {}
-
- r := NewRouter()
- r.SkipClean(true)
- r.HandleFunc("/api/", func2).Name("func2")
- r.HandleFunc("/", func1).Name("func1")
-
- req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
- res := NewRecorder()
- r.ServeHTTP(res, req)
-
- if len(res.HeaderMap["Location"]) != 0 {
- t.Errorf("Shouldn't redirect since skip clean is disabled")
- }
-}
-
// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
func TestSubrouterHeader(t *testing.T) {
expected := "func1 response"
diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go
index 6c53f9f1d..bf92af261 100644
--- a/vendor/github.com/gorilla/mux/route.go
+++ b/vendor/github.com/gorilla/mux/route.go
@@ -26,9 +26,6 @@ type Route struct {
// If true, when the path pattern is "/path/", accessing "/path" will
// redirect to the former and vice versa.
strictSlash bool
- // If true, when the path pattern is "/path//to", accessing "/path//to"
- // will not redirect
- skipClean bool
// If true, this route never matches: it is only used to build URLs.
buildOnly bool
// The name used to build URLs.
@@ -39,10 +36,6 @@ type Route struct {
buildVarsFunc BuildVarsFunc
}
-func (r *Route) SkipClean() bool {
- return r.skipClean
-}
-
// Match matches the route against the request.
func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
if r.buildOnly || r.err != nil {
diff --git a/vendor/github.com/hashicorp/golang-lru/.gitignore b/vendor/github.com/hashicorp/golang-lru/.gitignore
new file mode 100644
index 000000000..836562412
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/.gitignore
@@ -0,0 +1,23 @@
+# 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
diff --git a/vendor/github.com/hashicorp/golang-lru/2q.go b/vendor/github.com/hashicorp/golang-lru/2q.go
new file mode 100644
index 000000000..337d96329
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/2q.go
@@ -0,0 +1,212 @@
+package lru
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/hashicorp/golang-lru/simplelru"
+)
+
+const (
+ // Default2QRecentRatio is the ratio of the 2Q cache dedicated
+ // to recently added entries that have only been accessed once.
+ Default2QRecentRatio = 0.25
+
+ // Default2QGhostEntries is the default ratio of ghost
+ // entries kept to track entries recently evicted
+ Default2QGhostEntries = 0.50
+)
+
+// TwoQueueCache is a thread-safe fixed size 2Q cache.
+// 2Q is an enhancement over the standard LRU cache
+// in that it tracks both frequently and recently used
+// entries separately. This avoids a burst in access to new
+// entries from evicting frequently used entries. It adds some
+// additional tracking overhead to the standard LRU cache, and is
+// computationally about 2x the cost, and adds some metadata over
+// head. The ARCCache is similar, but does not require setting any
+// parameters.
+type TwoQueueCache struct {
+ size int
+ recentSize int
+
+ recent *simplelru.LRU
+ frequent *simplelru.LRU
+ recentEvict *simplelru.LRU
+ lock sync.RWMutex
+}
+
+// New2Q creates a new TwoQueueCache using the default
+// values for the parameters.
+func New2Q(size int) (*TwoQueueCache, error) {
+ return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries)
+}
+
+// New2QParams creates a new TwoQueueCache using the provided
+// parameter values.
+func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) {
+ if size <= 0 {
+ return nil, fmt.Errorf("invalid size")
+ }
+ if recentRatio < 0.0 || recentRatio > 1.0 {
+ return nil, fmt.Errorf("invalid recent ratio")
+ }
+ if ghostRatio < 0.0 || ghostRatio > 1.0 {
+ return nil, fmt.Errorf("invalid ghost ratio")
+ }
+
+ // Determine the sub-sizes
+ recentSize := int(float64(size) * recentRatio)
+ evictSize := int(float64(size) * ghostRatio)
+
+ // Allocate the LRUs
+ recent, err := simplelru.NewLRU(size, nil)
+ if err != nil {
+ return nil, err
+ }
+ frequent, err := simplelru.NewLRU(size, nil)
+ if err != nil {
+ return nil, err
+ }
+ recentEvict, err := simplelru.NewLRU(evictSize, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize the cache
+ c := &TwoQueueCache{
+ size: size,
+ recentSize: recentSize,
+ recent: recent,
+ frequent: frequent,
+ recentEvict: recentEvict,
+ }
+ return c, nil
+}
+
+func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ // Check if this is a frequent value
+ if val, ok := c.frequent.Get(key); ok {
+ return val, ok
+ }
+
+ // If the value is contained in recent, then we
+ // promote it to frequent
+ if val, ok := c.recent.Peek(key); ok {
+ c.recent.Remove(key)
+ c.frequent.Add(key, val)
+ return val, ok
+ }
+
+ // No hit
+ return nil, false
+}
+
+func (c *TwoQueueCache) Add(key, value interface{}) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ // Check if the value is frequently used already,
+ // and just update the value
+ if c.frequent.Contains(key) {
+ c.frequent.Add(key, value)
+ return
+ }
+
+ // Check if the value is recently used, and promote
+ // the value into the frequent list
+ if c.recent.Contains(key) {
+ c.recent.Remove(key)
+ c.frequent.Add(key, value)
+ return
+ }
+
+ // If the value was recently evicted, add it to the
+ // frequently used list
+ if c.recentEvict.Contains(key) {
+ c.ensureSpace(true)
+ c.recentEvict.Remove(key)
+ c.frequent.Add(key, value)
+ return
+ }
+
+ // Add to the recently seen list
+ c.ensureSpace(false)
+ c.recent.Add(key, value)
+ return
+}
+
+// ensureSpace is used to ensure we have space in the cache
+func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
+ // If we have space, nothing to do
+ recentLen := c.recent.Len()
+ freqLen := c.frequent.Len()
+ if recentLen+freqLen < c.size {
+ return
+ }
+
+ // If the recent buffer is larger than
+ // the target, evict from there
+ if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
+ k, _, _ := c.recent.RemoveOldest()
+ c.recentEvict.Add(k, nil)
+ return
+ }
+
+ // Remove from the frequent list otherwise
+ c.frequent.RemoveOldest()
+}
+
+func (c *TwoQueueCache) Len() int {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.recent.Len() + c.frequent.Len()
+}
+
+func (c *TwoQueueCache) Keys() []interface{} {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ k1 := c.frequent.Keys()
+ k2 := c.recent.Keys()
+ return append(k1, k2...)
+}
+
+func (c *TwoQueueCache) Remove(key interface{}) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ if c.frequent.Remove(key) {
+ return
+ }
+ if c.recent.Remove(key) {
+ return
+ }
+ if c.recentEvict.Remove(key) {
+ return
+ }
+}
+
+func (c *TwoQueueCache) Purge() {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ c.recent.Purge()
+ c.frequent.Purge()
+ c.recentEvict.Purge()
+}
+
+func (c *TwoQueueCache) Contains(key interface{}) bool {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.frequent.Contains(key) || c.recent.Contains(key)
+}
+
+func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ if val, ok := c.frequent.Peek(key); ok {
+ return val, ok
+ }
+ return c.recent.Peek(key)
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/2q_test.go b/vendor/github.com/hashicorp/golang-lru/2q_test.go
new file mode 100644
index 000000000..1b0f35181
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/2q_test.go
@@ -0,0 +1,306 @@
+package lru
+
+import (
+ "math/rand"
+ "testing"
+)
+
+func Benchmark2Q_Rand(b *testing.B) {
+ l, err := New2Q(8192)
+ if err != nil {
+ b.Fatalf("err: %v", err)
+ }
+
+ trace := make([]int64, b.N*2)
+ for i := 0; i < b.N*2; i++ {
+ trace[i] = rand.Int63() % 32768
+ }
+
+ b.ResetTimer()
+
+ var hit, miss int
+ for i := 0; i < 2*b.N; i++ {
+ if i%2 == 0 {
+ l.Add(trace[i], trace[i])
+ } else {
+ _, ok := l.Get(trace[i])
+ if ok {
+ hit++
+ } else {
+ miss++
+ }
+ }
+ }
+ b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
+}
+
+func Benchmark2Q_Freq(b *testing.B) {
+ l, err := New2Q(8192)
+ if err != nil {
+ b.Fatalf("err: %v", err)
+ }
+
+ trace := make([]int64, b.N*2)
+ for i := 0; i < b.N*2; i++ {
+ if i%2 == 0 {
+ trace[i] = rand.Int63() % 16384
+ } else {
+ trace[i] = rand.Int63() % 32768
+ }
+ }
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ l.Add(trace[i], trace[i])
+ }
+ var hit, miss int
+ for i := 0; i < b.N; i++ {
+ _, ok := l.Get(trace[i])
+ if ok {
+ hit++
+ } else {
+ miss++
+ }
+ }
+ b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
+}
+
+func Test2Q_RandomOps(t *testing.T) {
+ size := 128
+ l, err := New2Q(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ n := 200000
+ for i := 0; i < n; i++ {
+ key := rand.Int63() % 512
+ r := rand.Int63()
+ switch r % 3 {
+ case 0:
+ l.Add(key, key)
+ case 1:
+ l.Get(key)
+ case 2:
+ l.Remove(key)
+ }
+
+ if l.recent.Len()+l.frequent.Len() > size {
+ t.Fatalf("bad: recent: %d freq: %d",
+ l.recent.Len(), l.frequent.Len())
+ }
+ }
+}
+
+func Test2Q_Get_RecentToFrequent(t *testing.T) {
+ l, err := New2Q(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ // Touch all the entries, should be in t1
+ for i := 0; i < 128; i++ {
+ l.Add(i, i)
+ }
+ if n := l.recent.Len(); n != 128 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Get should upgrade to t2
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("missing: %d", i)
+ }
+ }
+ if n := l.recent.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 128 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Get be from t2
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("missing: %d", i)
+ }
+ }
+ if n := l.recent.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 128 {
+ t.Fatalf("bad: %d", n)
+ }
+}
+
+func Test2Q_Add_RecentToFrequent(t *testing.T) {
+ l, err := New2Q(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ // Add initially to recent
+ l.Add(1, 1)
+ if n := l.recent.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Add should upgrade to frequent
+ l.Add(1, 1)
+ if n := l.recent.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Add should remain in frequent
+ l.Add(1, 1)
+ if n := l.recent.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+}
+
+func Test2Q_Add_RecentEvict(t *testing.T) {
+ l, err := New2Q(4)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ // Add 1,2,3,4,5 -> Evict 1
+ l.Add(1, 1)
+ l.Add(2, 2)
+ l.Add(3, 3)
+ l.Add(4, 4)
+ l.Add(5, 5)
+ if n := l.recent.Len(); n != 4 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.recentEvict.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Pull in the recently evicted
+ l.Add(1, 1)
+ if n := l.recent.Len(); n != 3 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.recentEvict.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Add 6, should cause another recent evict
+ l.Add(6, 6)
+ if n := l.recent.Len(); n != 3 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.recentEvict.Len(); n != 2 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.frequent.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+}
+
+func Test2Q(t *testing.T) {
+ l, err := New2Q(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ for i := 0; i < 256; i++ {
+ l.Add(i, i)
+ }
+ if l.Len() != 128 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+
+ for i, k := range l.Keys() {
+ if v, ok := l.Get(k); !ok || v != k || v != i+128 {
+ t.Fatalf("bad key: %v", k)
+ }
+ }
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if ok {
+ t.Fatalf("should be evicted")
+ }
+ }
+ for i := 128; i < 256; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("should not be evicted")
+ }
+ }
+ for i := 128; i < 192; i++ {
+ l.Remove(i)
+ _, ok := l.Get(i)
+ if ok {
+ t.Fatalf("should be deleted")
+ }
+ }
+
+ l.Purge()
+ if l.Len() != 0 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+ if _, ok := l.Get(200); ok {
+ t.Fatalf("should contain nothing")
+ }
+}
+
+// Test that Contains doesn't update recent-ness
+func Test2Q_Contains(t *testing.T) {
+ l, err := New2Q(2)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if !l.Contains(1) {
+ t.Errorf("1 should be contained")
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("Contains should not have updated recent-ness of 1")
+ }
+}
+
+// Test that Peek doesn't update recent-ness
+func Test2Q_Peek(t *testing.T) {
+ l, err := New2Q(2)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if v, ok := l.Peek(1); !ok || v != 1 {
+ t.Errorf("1 should be set to 1: %v, %v", v, ok)
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("should not have updated recent-ness of 1")
+ }
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/LICENSE b/vendor/github.com/hashicorp/golang-lru/LICENSE
new file mode 100644
index 000000000..be2cc4dfb
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/LICENSE
@@ -0,0 +1,362 @@
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. "Contributor"
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the terms of
+ a Secondary License.
+
+1.6. "Executable Form"
+
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+
+ means a work that combines Covered Software with other material, in a
+ separate file or files, that is not Covered Software.
+
+1.8. "License"
+
+ means this document.
+
+1.9. "Licensable"
+
+ means having the right to grant, to the maximum extent possible, whether
+ at the time of the initial grant or subsequently, any and all of the
+ rights conveyed by this License.
+
+1.10. "Modifications"
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. "Patent Claims" of a Contributor
+
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the License,
+ by the making, using, selling, offering for sale, having made, import,
+ or transfer of either its Contributions or its Contributor Version.
+
+1.12. "Secondary License"
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. "Source Code Form"
+
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution
+ become effective for each Contribution on the date the Contributor first
+ distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under
+ this License. No additional rights or licenses will be implied from the
+ distribution or licensing of Covered Software under this License.
+ Notwithstanding Section 2.1(b) above, no patent license is granted by a
+ Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+ This License does not grant any rights in the trademarks, service marks,
+ or logos of any Contributor (except as may be necessary to comply with
+ the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this
+ License (see Section 10.2) or under the terms of a Secondary License (if
+ permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its
+ Contributions are its original creation(s) or it has sufficient rights to
+ grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under
+ applicable copyright doctrines of fair use, fair dealing, or other
+ equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under
+ the terms of this License. You must inform recipients that the Source
+ Code Form of the Covered Software is governed by the terms of this
+ License, and how they can obtain a copy of this License. You may not
+ attempt to alter or restrict the recipients' rights in the Source Code
+ Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter the
+ recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for
+ the Covered Software. If the Larger Work is a combination of Covered
+ Software with a work governed by one or more Secondary Licenses, and the
+ Covered Software is not Incompatible With Secondary Licenses, this
+ License permits You to additionally distribute such Covered Software
+ under the terms of such Secondary License(s), so that the recipient of
+ the Larger Work may, at their option, further distribute the Covered
+ Software under the terms of either this License or such Secondary
+ License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices
+ (including copyright notices, patent notices, disclaimers of warranty, or
+ limitations of liability) contained within the Source Code Form of the
+ Covered Software, except that You may alter any license notices to the
+ extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on
+ behalf of any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity, or liability obligation is offered by
+ You alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute,
+ judicial order, or regulation then You must: (a) comply with the terms of
+ this License to the maximum extent possible; and (b) describe the
+ limitations and the code they affect. Such description must be placed in a
+ text file included with all distributions of the Covered Software under
+ this License. Except to the extent prohibited by statute or regulation,
+ such description must be sufficiently detailed for a recipient of ordinary
+ skill to be able to understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing
+ basis, if such Contributor fails to notify You of the non-compliance by
+ some reasonable means prior to 60 days after You have come back into
+ compliance. Moreover, Your grants from a particular Contributor are
+ reinstated on an ongoing basis if such Contributor notifies You of the
+ non-compliance by some reasonable means, this is the first time You have
+ received notice of non-compliance with this License from such
+ Contributor, and You become compliant prior to 30 days after Your receipt
+ of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions,
+ counter-claims, and cross-claims) alleging that a Contributor Version
+ directly or indirectly infringes any patent, then the rights granted to
+ You by any and all Contributors for the Covered Software under Section
+ 2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an "as is" basis,
+ without warranty of any kind, either expressed, implied, or statutory,
+ including, without limitation, warranties that the Covered Software is free
+ of defects, merchantable, fit for a particular purpose or non-infringing.
+ The entire risk as to the quality and performance of the Covered Software
+ is with You. Should any Covered Software prove defective in any respect,
+ You (not any Contributor) assume the cost of any necessary servicing,
+ repair, or correction. This disclaimer of warranty constitutes an essential
+ part of this License. No use of any Covered Software is authorized under
+ this License except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from
+ such party's negligence to the extent applicable law prohibits such
+ limitation. Some jurisdictions do not allow the exclusion or limitation of
+ incidental or consequential damages, so this exclusion and limitation may
+ not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts
+ of a jurisdiction where the defendant maintains its principal place of
+ business and such litigation shall be governed by laws of that
+ jurisdiction, without reference to its conflict-of-law provisions. Nothing
+ in this Section shall prevent a party's ability to bring cross-claims or
+ counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. Any law or regulation which provides that
+ the language of a contract shall be construed against the drafter shall not
+ be used to construe this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version
+ of the License under which You originally received the Covered Software,
+ or under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a
+ modified version of this License if you rename the license and remove
+ any references to the name of the license steward (except to note that
+ such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+ Licenses If You choose to distribute Source Code Form that is
+ Incompatible With Secondary Licenses under the terms of this version of
+ the License, the notice described in Exhibit B of this License must be
+ attached.
+
+Exhibit A - Source Code Form License Notice
+
+ This Source Code Form is subject to the
+ terms of the Mozilla Public License, v.
+ 2.0. If a copy of the MPL was not
+ distributed with this file, You can
+ obtain one at
+ http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file,
+then You may include the notice in a location (such as a LICENSE file in a
+relevant directory) where a recipient would be likely to look for such a
+notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+
+ This Source Code Form is "Incompatible
+ With Secondary Licenses", as defined by
+ the Mozilla Public License, v. 2.0.
diff --git a/vendor/github.com/hashicorp/golang-lru/README.md b/vendor/github.com/hashicorp/golang-lru/README.md
new file mode 100644
index 000000000..33e58cfaf
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/README.md
@@ -0,0 +1,25 @@
+golang-lru
+==========
+
+This provides the `lru` package which implements a fixed-size
+thread safe LRU cache. It is based on the cache in Groupcache.
+
+Documentation
+=============
+
+Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru)
+
+Example
+=======
+
+Using the LRU is very simple:
+
+```go
+l, _ := New(128)
+for i := 0; i < 256; i++ {
+ l.Add(i, nil)
+}
+if l.Len() != 128 {
+ panic(fmt.Sprintf("bad len: %v", l.Len()))
+}
+```
diff --git a/vendor/github.com/hashicorp/golang-lru/arc.go b/vendor/github.com/hashicorp/golang-lru/arc.go
new file mode 100644
index 000000000..a2a252817
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/arc.go
@@ -0,0 +1,257 @@
+package lru
+
+import (
+ "sync"
+
+ "github.com/hashicorp/golang-lru/simplelru"
+)
+
+// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC).
+// ARC is an enhancement over the standard LRU cache in that tracks both
+// frequency and recency of use. This avoids a burst in access to new
+// entries from evicting the frequently used older entries. It adds some
+// additional tracking overhead to a standard LRU cache, computationally
+// it is roughly 2x the cost, and the extra memory overhead is linear
+// with the size of the cache. ARC has been patented by IBM, but is
+// similar to the TwoQueueCache (2Q) which requires setting parameters.
+type ARCCache struct {
+ size int // Size is the total capacity of the cache
+ p int // P is the dynamic preference towards T1 or T2
+
+ t1 *simplelru.LRU // T1 is the LRU for recently accessed items
+ b1 *simplelru.LRU // B1 is the LRU for evictions from t1
+
+ t2 *simplelru.LRU // T2 is the LRU for frequently accessed items
+ b2 *simplelru.LRU // B2 is the LRU for evictions from t2
+
+ lock sync.RWMutex
+}
+
+// NewARC creates an ARC of the given size
+func NewARC(size int) (*ARCCache, error) {
+ // Create the sub LRUs
+ b1, err := simplelru.NewLRU(size, nil)
+ if err != nil {
+ return nil, err
+ }
+ b2, err := simplelru.NewLRU(size, nil)
+ if err != nil {
+ return nil, err
+ }
+ t1, err := simplelru.NewLRU(size, nil)
+ if err != nil {
+ return nil, err
+ }
+ t2, err := simplelru.NewLRU(size, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize the ARC
+ c := &ARCCache{
+ size: size,
+ p: 0,
+ t1: t1,
+ b1: b1,
+ t2: t2,
+ b2: b2,
+ }
+ return c, nil
+}
+
+// Get looks up a key's value from the cache.
+func (c *ARCCache) Get(key interface{}) (interface{}, bool) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ // Ff the value is contained in T1 (recent), then
+ // promote it to T2 (frequent)
+ if val, ok := c.t1.Peek(key); ok {
+ c.t1.Remove(key)
+ c.t2.Add(key, val)
+ return val, ok
+ }
+
+ // Check if the value is contained in T2 (frequent)
+ if val, ok := c.t2.Get(key); ok {
+ return val, ok
+ }
+
+ // No hit
+ return nil, false
+}
+
+// Add adds a value to the cache.
+func (c *ARCCache) Add(key, value interface{}) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ // Check if the value is contained in T1 (recent), and potentially
+ // promote it to frequent T2
+ if c.t1.Contains(key) {
+ c.t1.Remove(key)
+ c.t2.Add(key, value)
+ return
+ }
+
+ // Check if the value is already in T2 (frequent) and update it
+ if c.t2.Contains(key) {
+ c.t2.Add(key, value)
+ return
+ }
+
+ // Check if this value was recently evicted as part of the
+ // recently used list
+ if c.b1.Contains(key) {
+ // T1 set is too small, increase P appropriately
+ delta := 1
+ b1Len := c.b1.Len()
+ b2Len := c.b2.Len()
+ if b2Len > b1Len {
+ delta = b2Len / b1Len
+ }
+ if c.p+delta >= c.size {
+ c.p = c.size
+ } else {
+ c.p += delta
+ }
+
+ // Potentially need to make room in the cache
+ if c.t1.Len()+c.t2.Len() >= c.size {
+ c.replace(false)
+ }
+
+ // Remove from B1
+ c.b1.Remove(key)
+
+ // Add the key to the frequently used list
+ c.t2.Add(key, value)
+ return
+ }
+
+ // Check if this value was recently evicted as part of the
+ // frequently used list
+ if c.b2.Contains(key) {
+ // T2 set is too small, decrease P appropriately
+ delta := 1
+ b1Len := c.b1.Len()
+ b2Len := c.b2.Len()
+ if b1Len > b2Len {
+ delta = b1Len / b2Len
+ }
+ if delta >= c.p {
+ c.p = 0
+ } else {
+ c.p -= delta
+ }
+
+ // Potentially need to make room in the cache
+ if c.t1.Len()+c.t2.Len() >= c.size {
+ c.replace(true)
+ }
+
+ // Remove from B2
+ c.b2.Remove(key)
+
+ // Add the key to the frequntly used list
+ c.t2.Add(key, value)
+ return
+ }
+
+ // Potentially need to make room in the cache
+ if c.t1.Len()+c.t2.Len() >= c.size {
+ c.replace(false)
+ }
+
+ // Keep the size of the ghost buffers trim
+ if c.b1.Len() > c.size-c.p {
+ c.b1.RemoveOldest()
+ }
+ if c.b2.Len() > c.p {
+ c.b2.RemoveOldest()
+ }
+
+ // Add to the recently seen list
+ c.t1.Add(key, value)
+ return
+}
+
+// replace is used to adaptively evict from either T1 or T2
+// based on the current learned value of P
+func (c *ARCCache) replace(b2ContainsKey bool) {
+ t1Len := c.t1.Len()
+ if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) {
+ k, _, ok := c.t1.RemoveOldest()
+ if ok {
+ c.b1.Add(k, nil)
+ }
+ } else {
+ k, _, ok := c.t2.RemoveOldest()
+ if ok {
+ c.b2.Add(k, nil)
+ }
+ }
+}
+
+// Len returns the number of cached entries
+func (c *ARCCache) Len() int {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.t1.Len() + c.t2.Len()
+}
+
+// Keys returns all the cached keys
+func (c *ARCCache) Keys() []interface{} {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ k1 := c.t1.Keys()
+ k2 := c.t2.Keys()
+ return append(k1, k2...)
+}
+
+// Remove is used to purge a key from the cache
+func (c *ARCCache) Remove(key interface{}) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ if c.t1.Remove(key) {
+ return
+ }
+ if c.t2.Remove(key) {
+ return
+ }
+ if c.b1.Remove(key) {
+ return
+ }
+ if c.b2.Remove(key) {
+ return
+ }
+}
+
+// Purge is used to clear the cache
+func (c *ARCCache) Purge() {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ c.t1.Purge()
+ c.t2.Purge()
+ c.b1.Purge()
+ c.b2.Purge()
+}
+
+// Contains is used to check if the cache contains a key
+// without updating recency or frequency.
+func (c *ARCCache) Contains(key interface{}) bool {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.t1.Contains(key) || c.t2.Contains(key)
+}
+
+// Peek is used to inspect the cache value of a key
+// without updating recency or frequency.
+func (c *ARCCache) Peek(key interface{}) (interface{}, bool) {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ if val, ok := c.t1.Peek(key); ok {
+ return val, ok
+ }
+ return c.t2.Peek(key)
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/arc_test.go b/vendor/github.com/hashicorp/golang-lru/arc_test.go
new file mode 100644
index 000000000..e2d9b68c6
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/arc_test.go
@@ -0,0 +1,377 @@
+package lru
+
+import (
+ "math/rand"
+ "testing"
+ "time"
+)
+
+func init() {
+ rand.Seed(time.Now().Unix())
+}
+
+func BenchmarkARC_Rand(b *testing.B) {
+ l, err := NewARC(8192)
+ if err != nil {
+ b.Fatalf("err: %v", err)
+ }
+
+ trace := make([]int64, b.N*2)
+ for i := 0; i < b.N*2; i++ {
+ trace[i] = rand.Int63() % 32768
+ }
+
+ b.ResetTimer()
+
+ var hit, miss int
+ for i := 0; i < 2*b.N; i++ {
+ if i%2 == 0 {
+ l.Add(trace[i], trace[i])
+ } else {
+ _, ok := l.Get(trace[i])
+ if ok {
+ hit++
+ } else {
+ miss++
+ }
+ }
+ }
+ b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
+}
+
+func BenchmarkARC_Freq(b *testing.B) {
+ l, err := NewARC(8192)
+ if err != nil {
+ b.Fatalf("err: %v", err)
+ }
+
+ trace := make([]int64, b.N*2)
+ for i := 0; i < b.N*2; i++ {
+ if i%2 == 0 {
+ trace[i] = rand.Int63() % 16384
+ } else {
+ trace[i] = rand.Int63() % 32768
+ }
+ }
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ l.Add(trace[i], trace[i])
+ }
+ var hit, miss int
+ for i := 0; i < b.N; i++ {
+ _, ok := l.Get(trace[i])
+ if ok {
+ hit++
+ } else {
+ miss++
+ }
+ }
+ b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
+}
+
+func TestARC_RandomOps(t *testing.T) {
+ size := 128
+ l, err := NewARC(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ n := 200000
+ for i := 0; i < n; i++ {
+ key := rand.Int63() % 512
+ r := rand.Int63()
+ switch r % 3 {
+ case 0:
+ l.Add(key, key)
+ case 1:
+ l.Get(key)
+ case 2:
+ l.Remove(key)
+ }
+
+ if l.t1.Len()+l.t2.Len() > size {
+ t.Fatalf("bad: t1: %d t2: %d b1: %d b2: %d p: %d",
+ l.t1.Len(), l.t2.Len(), l.b1.Len(), l.b2.Len(), l.p)
+ }
+ if l.b1.Len()+l.b2.Len() > size {
+ t.Fatalf("bad: t1: %d t2: %d b1: %d b2: %d p: %d",
+ l.t1.Len(), l.t2.Len(), l.b1.Len(), l.b2.Len(), l.p)
+ }
+ }
+}
+
+func TestARC_Get_RecentToFrequent(t *testing.T) {
+ l, err := NewARC(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ // Touch all the entries, should be in t1
+ for i := 0; i < 128; i++ {
+ l.Add(i, i)
+ }
+ if n := l.t1.Len(); n != 128 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Get should upgrade to t2
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("missing: %d", i)
+ }
+ }
+ if n := l.t1.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 128 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Get be from t2
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("missing: %d", i)
+ }
+ }
+ if n := l.t1.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 128 {
+ t.Fatalf("bad: %d", n)
+ }
+}
+
+func TestARC_Add_RecentToFrequent(t *testing.T) {
+ l, err := NewARC(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ // Add initially to t1
+ l.Add(1, 1)
+ if n := l.t1.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Add should upgrade to t2
+ l.Add(1, 1)
+ if n := l.t1.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Add should remain in t2
+ l.Add(1, 1)
+ if n := l.t1.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+}
+
+func TestARC_Adaptive(t *testing.T) {
+ l, err := NewARC(4)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ // Fill t1
+ for i := 0; i < 4; i++ {
+ l.Add(i, i)
+ }
+ if n := l.t1.Len(); n != 4 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Move to t2
+ l.Get(0)
+ l.Get(1)
+ if n := l.t2.Len(); n != 2 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Evict from t1
+ l.Add(4, 4)
+ if n := l.b1.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Current state
+ // t1 : (MRU) [4, 3] (LRU)
+ // t2 : (MRU) [1, 0] (LRU)
+ // b1 : (MRU) [2] (LRU)
+ // b2 : (MRU) [] (LRU)
+
+ // Add 2, should cause hit on b1
+ l.Add(2, 2)
+ if n := l.b1.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+ if l.p != 1 {
+ t.Fatalf("bad: %d", l.p)
+ }
+ if n := l.t2.Len(); n != 3 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Current state
+ // t1 : (MRU) [4] (LRU)
+ // t2 : (MRU) [2, 1, 0] (LRU)
+ // b1 : (MRU) [3] (LRU)
+ // b2 : (MRU) [] (LRU)
+
+ // Add 4, should migrate to t2
+ l.Add(4, 4)
+ if n := l.t1.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 4 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Current state
+ // t1 : (MRU) [] (LRU)
+ // t2 : (MRU) [4, 2, 1, 0] (LRU)
+ // b1 : (MRU) [3] (LRU)
+ // b2 : (MRU) [] (LRU)
+
+ // Add 4, should evict to b2
+ l.Add(5, 5)
+ if n := l.t1.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 3 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.b2.Len(); n != 1 {
+ t.Fatalf("bad: %d", n)
+ }
+
+ // Current state
+ // t1 : (MRU) [5] (LRU)
+ // t2 : (MRU) [4, 2, 1] (LRU)
+ // b1 : (MRU) [3] (LRU)
+ // b2 : (MRU) [0] (LRU)
+
+ // Add 0, should decrease p
+ l.Add(0, 0)
+ if n := l.t1.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.t2.Len(); n != 4 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.b1.Len(); n != 2 {
+ t.Fatalf("bad: %d", n)
+ }
+ if n := l.b2.Len(); n != 0 {
+ t.Fatalf("bad: %d", n)
+ }
+ if l.p != 0 {
+ t.Fatalf("bad: %d", l.p)
+ }
+
+ // Current state
+ // t1 : (MRU) [] (LRU)
+ // t2 : (MRU) [0, 4, 2, 1] (LRU)
+ // b1 : (MRU) [5, 3] (LRU)
+ // b2 : (MRU) [0] (LRU)
+}
+
+func TestARC(t *testing.T) {
+ l, err := NewARC(128)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ for i := 0; i < 256; i++ {
+ l.Add(i, i)
+ }
+ if l.Len() != 128 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+
+ for i, k := range l.Keys() {
+ if v, ok := l.Get(k); !ok || v != k || v != i+128 {
+ t.Fatalf("bad key: %v", k)
+ }
+ }
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if ok {
+ t.Fatalf("should be evicted")
+ }
+ }
+ for i := 128; i < 256; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("should not be evicted")
+ }
+ }
+ for i := 128; i < 192; i++ {
+ l.Remove(i)
+ _, ok := l.Get(i)
+ if ok {
+ t.Fatalf("should be deleted")
+ }
+ }
+
+ l.Purge()
+ if l.Len() != 0 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+ if _, ok := l.Get(200); ok {
+ t.Fatalf("should contain nothing")
+ }
+}
+
+// Test that Contains doesn't update recent-ness
+func TestARC_Contains(t *testing.T) {
+ l, err := NewARC(2)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if !l.Contains(1) {
+ t.Errorf("1 should be contained")
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("Contains should not have updated recent-ness of 1")
+ }
+}
+
+// Test that Peek doesn't update recent-ness
+func TestARC_Peek(t *testing.T) {
+ l, err := NewARC(2)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if v, ok := l.Peek(1); !ok || v != 1 {
+ t.Errorf("1 should be set to 1: %v, %v", v, ok)
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("should not have updated recent-ness of 1")
+ }
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/lru.go b/vendor/github.com/hashicorp/golang-lru/lru.go
new file mode 100644
index 000000000..a6285f989
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/lru.go
@@ -0,0 +1,114 @@
+// This package provides a simple LRU cache. It is based on the
+// LRU implementation in groupcache:
+// https://github.com/golang/groupcache/tree/master/lru
+package lru
+
+import (
+ "sync"
+
+ "github.com/hashicorp/golang-lru/simplelru"
+)
+
+// Cache is a thread-safe fixed size LRU cache.
+type Cache struct {
+ lru *simplelru.LRU
+ lock sync.RWMutex
+}
+
+// New creates an LRU of the given size
+func New(size int) (*Cache, error) {
+ return NewWithEvict(size, nil)
+}
+
+// NewWithEvict constructs a fixed size cache with the given eviction
+// callback.
+func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
+ lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted))
+ if err != nil {
+ return nil, err
+ }
+ c := &Cache{
+ lru: lru,
+ }
+ return c, nil
+}
+
+// Purge is used to completely clear the cache
+func (c *Cache) Purge() {
+ c.lock.Lock()
+ c.lru.Purge()
+ c.lock.Unlock()
+}
+
+// Add adds a value to the cache. Returns true if an eviction occurred.
+func (c *Cache) Add(key, value interface{}) bool {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ return c.lru.Add(key, value)
+}
+
+// Get looks up a key's value from the cache.
+func (c *Cache) Get(key interface{}) (interface{}, bool) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ return c.lru.Get(key)
+}
+
+// Check if a key is in the cache, without updating the recent-ness
+// or deleting it for being stale.
+func (c *Cache) Contains(key interface{}) bool {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.lru.Contains(key)
+}
+
+// Returns the key value (or undefined if not found) without updating
+// the "recently used"-ness of the key.
+func (c *Cache) Peek(key interface{}) (interface{}, bool) {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.lru.Peek(key)
+}
+
+// ContainsOrAdd checks if a key is in the cache without updating the
+// recent-ness or deleting it for being stale, and if not, adds the value.
+// Returns whether found and whether an eviction occurred.
+func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if c.lru.Contains(key) {
+ return true, false
+ } else {
+ evict := c.lru.Add(key, value)
+ return false, evict
+ }
+}
+
+// Remove removes the provided key from the cache.
+func (c *Cache) Remove(key interface{}) {
+ c.lock.Lock()
+ c.lru.Remove(key)
+ c.lock.Unlock()
+}
+
+// RemoveOldest removes the oldest item from the cache.
+func (c *Cache) RemoveOldest() {
+ c.lock.Lock()
+ c.lru.RemoveOldest()
+ c.lock.Unlock()
+}
+
+// Keys returns a slice of the keys in the cache, from oldest to newest.
+func (c *Cache) Keys() []interface{} {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.lru.Keys()
+}
+
+// Len returns the number of items in the cache.
+func (c *Cache) Len() int {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return c.lru.Len()
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/lru_test.go b/vendor/github.com/hashicorp/golang-lru/lru_test.go
new file mode 100644
index 000000000..2b31218b0
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/lru_test.go
@@ -0,0 +1,221 @@
+package lru
+
+import (
+ "math/rand"
+ "testing"
+)
+
+func BenchmarkLRU_Rand(b *testing.B) {
+ l, err := New(8192)
+ if err != nil {
+ b.Fatalf("err: %v", err)
+ }
+
+ trace := make([]int64, b.N*2)
+ for i := 0; i < b.N*2; i++ {
+ trace[i] = rand.Int63() % 32768
+ }
+
+ b.ResetTimer()
+
+ var hit, miss int
+ for i := 0; i < 2*b.N; i++ {
+ if i%2 == 0 {
+ l.Add(trace[i], trace[i])
+ } else {
+ _, ok := l.Get(trace[i])
+ if ok {
+ hit++
+ } else {
+ miss++
+ }
+ }
+ }
+ b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
+}
+
+func BenchmarkLRU_Freq(b *testing.B) {
+ l, err := New(8192)
+ if err != nil {
+ b.Fatalf("err: %v", err)
+ }
+
+ trace := make([]int64, b.N*2)
+ for i := 0; i < b.N*2; i++ {
+ if i%2 == 0 {
+ trace[i] = rand.Int63() % 16384
+ } else {
+ trace[i] = rand.Int63() % 32768
+ }
+ }
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ l.Add(trace[i], trace[i])
+ }
+ var hit, miss int
+ for i := 0; i < b.N; i++ {
+ _, ok := l.Get(trace[i])
+ if ok {
+ hit++
+ } else {
+ miss++
+ }
+ }
+ b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
+}
+
+func TestLRU(t *testing.T) {
+ evictCounter := 0
+ onEvicted := func(k interface{}, v interface{}) {
+ if k != v {
+ t.Fatalf("Evict values not equal (%v!=%v)", k, v)
+ }
+ evictCounter += 1
+ }
+ l, err := NewWithEvict(128, onEvicted)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ for i := 0; i < 256; i++ {
+ l.Add(i, i)
+ }
+ if l.Len() != 128 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+
+ if evictCounter != 128 {
+ t.Fatalf("bad evict count: %v", evictCounter)
+ }
+
+ for i, k := range l.Keys() {
+ if v, ok := l.Get(k); !ok || v != k || v != i+128 {
+ t.Fatalf("bad key: %v", k)
+ }
+ }
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if ok {
+ t.Fatalf("should be evicted")
+ }
+ }
+ for i := 128; i < 256; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("should not be evicted")
+ }
+ }
+ for i := 128; i < 192; i++ {
+ l.Remove(i)
+ _, ok := l.Get(i)
+ if ok {
+ t.Fatalf("should be deleted")
+ }
+ }
+
+ l.Get(192) // expect 192 to be last key in l.Keys()
+
+ for i, k := range l.Keys() {
+ if (i < 63 && k != i+193) || (i == 63 && k != 192) {
+ t.Fatalf("out of order key: %v", k)
+ }
+ }
+
+ l.Purge()
+ if l.Len() != 0 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+ if _, ok := l.Get(200); ok {
+ t.Fatalf("should contain nothing")
+ }
+}
+
+// test that Add returns true/false if an eviction occurred
+func TestLRUAdd(t *testing.T) {
+ evictCounter := 0
+ onEvicted := func(k interface{}, v interface{}) {
+ evictCounter += 1
+ }
+
+ l, err := NewWithEvict(1, onEvicted)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ if l.Add(1, 1) == true || evictCounter != 0 {
+ t.Errorf("should not have an eviction")
+ }
+ if l.Add(2, 2) == false || evictCounter != 1 {
+ t.Errorf("should have an eviction")
+ }
+}
+
+// test that Contains doesn't update recent-ness
+func TestLRUContains(t *testing.T) {
+ l, err := New(2)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if !l.Contains(1) {
+ t.Errorf("1 should be contained")
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("Contains should not have updated recent-ness of 1")
+ }
+}
+
+// test that Contains doesn't update recent-ness
+func TestLRUContainsOrAdd(t *testing.T) {
+ l, err := New(2)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ contains, evict := l.ContainsOrAdd(1, 1)
+ if !contains {
+ t.Errorf("1 should be contained")
+ }
+ if evict {
+ t.Errorf("nothing should be evicted here")
+ }
+
+ l.Add(3, 3)
+ contains, evict = l.ContainsOrAdd(1, 1)
+ if contains {
+ t.Errorf("1 should not have been contained")
+ }
+ if !evict {
+ t.Errorf("an eviction should have occurred")
+ }
+ if !l.Contains(1) {
+ t.Errorf("now 1 should be contained")
+ }
+}
+
+// test that Peek doesn't update recent-ness
+func TestLRUPeek(t *testing.T) {
+ l, err := New(2)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if v, ok := l.Peek(1); !ok || v != 1 {
+ t.Errorf("1 should be set to 1: %v, %v", v, ok)
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("should not have updated recent-ness of 1")
+ }
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
new file mode 100644
index 000000000..cb416b394
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
@@ -0,0 +1,160 @@
+package simplelru
+
+import (
+ "container/list"
+ "errors"
+)
+
+// EvictCallback is used to get a callback when a cache entry is evicted
+type EvictCallback func(key interface{}, value interface{})
+
+// LRU implements a non-thread safe fixed size LRU cache
+type LRU struct {
+ size int
+ evictList *list.List
+ items map[interface{}]*list.Element
+ onEvict EvictCallback
+}
+
+// entry is used to hold a value in the evictList
+type entry struct {
+ key interface{}
+ value interface{}
+}
+
+// NewLRU constructs an LRU of the given size
+func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
+ if size <= 0 {
+ return nil, errors.New("Must provide a positive size")
+ }
+ c := &LRU{
+ size: size,
+ evictList: list.New(),
+ items: make(map[interface{}]*list.Element),
+ onEvict: onEvict,
+ }
+ return c, nil
+}
+
+// Purge is used to completely clear the cache
+func (c *LRU) Purge() {
+ for k, v := range c.items {
+ if c.onEvict != nil {
+ c.onEvict(k, v.Value.(*entry).value)
+ }
+ delete(c.items, k)
+ }
+ c.evictList.Init()
+}
+
+// Add adds a value to the cache. Returns true if an eviction occurred.
+func (c *LRU) Add(key, value interface{}) bool {
+ // Check for existing item
+ if ent, ok := c.items[key]; ok {
+ c.evictList.MoveToFront(ent)
+ ent.Value.(*entry).value = value
+ return false
+ }
+
+ // Add new item
+ ent := &entry{key, value}
+ entry := c.evictList.PushFront(ent)
+ c.items[key] = entry
+
+ evict := c.evictList.Len() > c.size
+ // Verify size not exceeded
+ if evict {
+ c.removeOldest()
+ }
+ return evict
+}
+
+// Get looks up a key's value from the cache.
+func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
+ if ent, ok := c.items[key]; ok {
+ c.evictList.MoveToFront(ent)
+ return ent.Value.(*entry).value, true
+ }
+ return
+}
+
+// Check if a key is in the cache, without updating the recent-ness
+// or deleting it for being stale.
+func (c *LRU) Contains(key interface{}) (ok bool) {
+ _, ok = c.items[key]
+ return ok
+}
+
+// Returns the key value (or undefined if not found) without updating
+// the "recently used"-ness of the key.
+func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
+ if ent, ok := c.items[key]; ok {
+ return ent.Value.(*entry).value, true
+ }
+ return nil, ok
+}
+
+// Remove removes the provided key from the cache, returning if the
+// key was contained.
+func (c *LRU) Remove(key interface{}) bool {
+ if ent, ok := c.items[key]; ok {
+ c.removeElement(ent)
+ return true
+ }
+ return false
+}
+
+// RemoveOldest removes the oldest item from the cache.
+func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
+ ent := c.evictList.Back()
+ if ent != nil {
+ c.removeElement(ent)
+ kv := ent.Value.(*entry)
+ return kv.key, kv.value, true
+ }
+ return nil, nil, false
+}
+
+// GetOldest returns the oldest entry
+func (c *LRU) GetOldest() (interface{}, interface{}, bool) {
+ ent := c.evictList.Back()
+ if ent != nil {
+ kv := ent.Value.(*entry)
+ return kv.key, kv.value, true
+ }
+ return nil, nil, false
+}
+
+// Keys returns a slice of the keys in the cache, from oldest to newest.
+func (c *LRU) Keys() []interface{} {
+ keys := make([]interface{}, len(c.items))
+ i := 0
+ for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
+ keys[i] = ent.Value.(*entry).key
+ i++
+ }
+ return keys
+}
+
+// Len returns the number of items in the cache.
+func (c *LRU) Len() int {
+ return c.evictList.Len()
+}
+
+// removeOldest removes the oldest item from the cache.
+func (c *LRU) removeOldest() {
+ ent := c.evictList.Back()
+ if ent != nil {
+ c.removeElement(ent)
+ }
+}
+
+// removeElement is used to remove a given list element from the cache
+func (c *LRU) removeElement(e *list.Element) {
+ c.evictList.Remove(e)
+ kv := e.Value.(*entry)
+ delete(c.items, kv.key)
+ if c.onEvict != nil {
+ c.onEvict(kv.key, kv.value)
+ }
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go
new file mode 100644
index 000000000..a958934f6
--- /dev/null
+++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go
@@ -0,0 +1,167 @@
+package simplelru
+
+import "testing"
+
+func TestLRU(t *testing.T) {
+ evictCounter := 0
+ onEvicted := func(k interface{}, v interface{}) {
+ if k != v {
+ t.Fatalf("Evict values not equal (%v!=%v)", k, v)
+ }
+ evictCounter += 1
+ }
+ l, err := NewLRU(128, onEvicted)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ for i := 0; i < 256; i++ {
+ l.Add(i, i)
+ }
+ if l.Len() != 128 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+
+ if evictCounter != 128 {
+ t.Fatalf("bad evict count: %v", evictCounter)
+ }
+
+ for i, k := range l.Keys() {
+ if v, ok := l.Get(k); !ok || v != k || v != i+128 {
+ t.Fatalf("bad key: %v", k)
+ }
+ }
+ for i := 0; i < 128; i++ {
+ _, ok := l.Get(i)
+ if ok {
+ t.Fatalf("should be evicted")
+ }
+ }
+ for i := 128; i < 256; i++ {
+ _, ok := l.Get(i)
+ if !ok {
+ t.Fatalf("should not be evicted")
+ }
+ }
+ for i := 128; i < 192; i++ {
+ ok := l.Remove(i)
+ if !ok {
+ t.Fatalf("should be contained")
+ }
+ ok = l.Remove(i)
+ if ok {
+ t.Fatalf("should not be contained")
+ }
+ _, ok = l.Get(i)
+ if ok {
+ t.Fatalf("should be deleted")
+ }
+ }
+
+ l.Get(192) // expect 192 to be last key in l.Keys()
+
+ for i, k := range l.Keys() {
+ if (i < 63 && k != i+193) || (i == 63 && k != 192) {
+ t.Fatalf("out of order key: %v", k)
+ }
+ }
+
+ l.Purge()
+ if l.Len() != 0 {
+ t.Fatalf("bad len: %v", l.Len())
+ }
+ if _, ok := l.Get(200); ok {
+ t.Fatalf("should contain nothing")
+ }
+}
+
+func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
+ l, err := NewLRU(128, nil)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+ for i := 0; i < 256; i++ {
+ l.Add(i, i)
+ }
+ k, _, ok := l.GetOldest()
+ if !ok {
+ t.Fatalf("missing")
+ }
+ if k.(int) != 128 {
+ t.Fatalf("bad: %v", k)
+ }
+
+ k, _, ok = l.RemoveOldest()
+ if !ok {
+ t.Fatalf("missing")
+ }
+ if k.(int) != 128 {
+ t.Fatalf("bad: %v", k)
+ }
+
+ k, _, ok = l.RemoveOldest()
+ if !ok {
+ t.Fatalf("missing")
+ }
+ if k.(int) != 129 {
+ t.Fatalf("bad: %v", k)
+ }
+}
+
+// Test that Add returns true/false if an eviction occurred
+func TestLRU_Add(t *testing.T) {
+ evictCounter := 0
+ onEvicted := func(k interface{}, v interface{}) {
+ evictCounter += 1
+ }
+
+ l, err := NewLRU(1, onEvicted)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ if l.Add(1, 1) == true || evictCounter != 0 {
+ t.Errorf("should not have an eviction")
+ }
+ if l.Add(2, 2) == false || evictCounter != 1 {
+ t.Errorf("should have an eviction")
+ }
+}
+
+// Test that Contains doesn't update recent-ness
+func TestLRU_Contains(t *testing.T) {
+ l, err := NewLRU(2, nil)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if !l.Contains(1) {
+ t.Errorf("1 should be contained")
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("Contains should not have updated recent-ness of 1")
+ }
+}
+
+// Test that Peek doesn't update recent-ness
+func TestLRU_Peek(t *testing.T) {
+ l, err := NewLRU(2, nil)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+
+ l.Add(1, 1)
+ l.Add(2, 2)
+ if v, ok := l.Peek(1); !ok || v != 1 {
+ t.Errorf("1 should be set to 1: %v, %v", v, ok)
+ }
+
+ l.Add(3, 3)
+ if l.Contains(1) {
+ t.Errorf("should not have updated recent-ness of 1")
+ }
+}
diff --git a/vendor/github.com/jehiah/go-strftime/.gitignore b/vendor/github.com/jehiah/go-strftime/.gitignore
new file mode 100644
index 000000000..00268614f
--- /dev/null
+++ b/vendor/github.com/jehiah/go-strftime/.gitignore
@@ -0,0 +1,22 @@
+# 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
diff --git a/vendor/github.com/lib/pq/.travis.sh b/vendor/github.com/lib/pq/.travis.sh
new file mode 100755
index 000000000..ebf447030
--- /dev/null
+++ b/vendor/github.com/lib/pq/.travis.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+set -eu
+
+client_configure() {
+ sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key
+}
+
+pgdg_repository() {
+ local sourcelist='sources.list.d/postgresql.list'
+
+ curl -sS 'https://www.postgresql.org/media/keys/ACCC4CF8.asc' | sudo apt-key add -
+ echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION | sudo tee "/etc/apt/$sourcelist"
+ sudo apt-get -o Dir::Etc::sourcelist="$sourcelist" -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0' update
+}
+
+postgresql_configure() {
+ sudo tee /etc/postgresql/$PGVERSION/main/pg_hba.conf > /dev/null <<-config
+ local all all trust
+ hostnossl all pqgossltest 127.0.0.1/32 reject
+ hostnossl all pqgosslcert 127.0.0.1/32 reject
+ hostssl all pqgossltest 127.0.0.1/32 trust
+ hostssl all pqgosslcert 127.0.0.1/32 cert
+ host all all 127.0.0.1/32 trust
+ hostnossl all pqgossltest ::1/128 reject
+ hostnossl all pqgosslcert ::1/128 reject
+ hostssl all pqgossltest ::1/128 trust
+ hostssl all pqgosslcert ::1/128 cert
+ host all all ::1/128 trust
+ config
+
+ xargs sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ <<-certificates
+ certs/root.crt
+ certs/server.crt
+ certs/server.key
+ certificates
+
+ sort -VCu <<-versions ||
+ $PGVERSION
+ 9.2
+ versions
+ sudo tee -a /etc/postgresql/$PGVERSION/main/postgresql.conf > /dev/null <<-config
+ ssl_ca_file = 'root.crt'
+ ssl_cert_file = 'server.crt'
+ ssl_key_file = 'server.key'
+ config
+
+ echo 127.0.0.1 postgres | sudo tee -a /etc/hosts > /dev/null
+
+ sudo service postgresql restart
+}
+
+postgresql_install() {
+ xargs sudo apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confnew' install <<-packages
+ postgresql-$PGVERSION
+ postgresql-server-dev-$PGVERSION
+ postgresql-contrib-$PGVERSION
+ packages
+}
+
+postgresql_uninstall() {
+ sudo service postgresql stop
+ xargs sudo apt-get -y --purge remove <<-packages
+ libpq-dev
+ libpq5
+ postgresql
+ postgresql-client-common
+ postgresql-common
+ packages
+ sudo rm -rf /var/lib/postgresql
+}
+
+$1
diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml
index d63afcb9f..d7b60f899 100644
--- a/vendor/github.com/lib/pq/.travis.yml
+++ b/vendor/github.com/lib/pq/.travis.yml
@@ -1,43 +1,12 @@
language: go
go:
- - 1.4
- 1.5
- 1.6
+ - 1.7
- tip
-before_install:
- - psql --version
- - sudo /etc/init.d/postgresql stop
- - sudo apt-get -y --purge remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common
- - sudo rm -rf /var/lib/postgresql
- - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
- - sudo sh -c "echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list"
- - sudo apt-get update -qq
- - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::="--force-confnew" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION
- - sudo chmod 777 /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "local all postgres trust" > /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "local all all trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgossltest 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgosslcert 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgossltest 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgosslcert 127.0.0.1/32 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "host all all 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgossltest ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgosslcert ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgossltest ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgosslcert ::1/128 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "host all all ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ certs/server.key certs/server.crt certs/root.crt
- - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_cert_file = 'server.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_key_file = 'server.key'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_ca_file = 'root.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- - sudo sh -c "echo 127.0.0.1 postgres >> /etc/hosts"
- - sudo ls -l /var/lib/postgresql/$PGVERSION/main/
- - sudo cat /etc/postgresql/$PGVERSION/main/postgresql.conf
- - sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key
- - sudo /etc/init.d/postgresql restart
- - go get golang.org/x/tools/cmd/goimports
+sudo: true
env:
global:
@@ -46,23 +15,29 @@ env:
- PQSSLCERTTEST_PATH=$PWD/certs
- PGHOST=127.0.0.1
matrix:
- - PGVERSION=9.5 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.4 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.3 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.2 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.1 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.5 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.4 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.3 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.2 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.1 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=no
+ - PGVERSION=9.5
+ - PGVERSION=9.4
+ - PGVERSION=9.3
+ - PGVERSION=9.2
+ - PGVERSION=9.1
+ - PGVERSION=9.0
-script:
- - result=$(goimports -d -e $(find -name \*.go)); test -z "$result" || (echo "$result" && false) && go vet ./... && go test -v ./...
+before_install:
+ - ./.travis.sh postgresql_uninstall
+ - ./.travis.sh pgdg_repository
+ - ./.travis.sh postgresql_install
+ - ./.travis.sh postgresql_configure
+ - ./.travis.sh client_configure
+ - go get golang.org/x/tools/cmd/goimports
before_script:
- - psql -c 'create database pqgotest' -U postgres
- - psql -c 'create user pqgossltest' -U postgres
- - psql -c 'create user pqgosslcert' -U postgres
+ - createdb pqgotest
+ - createuser -DRS pqgossltest
+ - createuser -DRS pqgosslcert
+
+script:
+ - >
+ goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
+ - go vet ./...
+ - PQTEST_BINARY_PARAMETERS=no go test -v ./...
+ - PQTEST_BINARY_PARAMETERS=yes go test -v ./...
diff --git a/vendor/github.com/lib/pq/README.md b/vendor/github.com/lib/pq/README.md
index 148451e80..5eb9e1445 100644
--- a/vendor/github.com/lib/pq/README.md
+++ b/vendor/github.com/lib/pq/README.md
@@ -85,7 +85,7 @@ code still exists in here.
* Keith Rarick (kr)
* Kir Shatrov (kirs)
* Lann Martin (lann)
-* Maciek Sakrejda (deafbybeheading)
+* Maciek Sakrejda (uhoh-itsmaciek)
* Marc Brinkmann (mbr)
* Marko Tiikkaja (johto)
* Matt Newberry (MattNewberry)
diff --git a/vendor/github.com/lib/pq/array.go b/vendor/github.com/lib/pq/array.go
new file mode 100644
index 000000000..27eb07a9e
--- /dev/null
+++ b/vendor/github.com/lib/pq/array.go
@@ -0,0 +1,727 @@
+package pq
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "encoding/hex"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+var typeByteSlice = reflect.TypeOf([]byte{})
+var typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
+var typeSqlScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
+
+// Array returns the optimal driver.Valuer and sql.Scanner for an array or
+// slice of any dimension.
+//
+// For example:
+// db.Query(`SELECT * FROM t WHERE id = ANY($1)`, pq.Array([]int{235, 401}))
+//
+// var x []sql.NullInt64
+// db.QueryRow('SELECT ARRAY[235, 401]').Scan(pq.Array(&x))
+//
+// Scanning multi-dimensional arrays is not supported. Arrays where the lower
+// bound is not one (such as `[0:0]={1}') are not supported.
+func Array(a interface{}) interface {
+ driver.Valuer
+ sql.Scanner
+} {
+ switch a := a.(type) {
+ case []bool:
+ return (*BoolArray)(&a)
+ case []float64:
+ return (*Float64Array)(&a)
+ case []int64:
+ return (*Int64Array)(&a)
+ case []string:
+ return (*StringArray)(&a)
+
+ case *[]bool:
+ return (*BoolArray)(a)
+ case *[]float64:
+ return (*Float64Array)(a)
+ case *[]int64:
+ return (*Int64Array)(a)
+ case *[]string:
+ return (*StringArray)(a)
+ }
+
+ return GenericArray{a}
+}
+
+// ArrayDelimiter may be optionally implemented by driver.Valuer or sql.Scanner
+// to override the array delimiter used by GenericArray.
+type ArrayDelimiter interface {
+ // ArrayDelimiter returns the delimiter character(s) for this element's type.
+ ArrayDelimiter() string
+}
+
+// BoolArray represents a one-dimensional array of the PostgreSQL boolean type.
+type BoolArray []bool
+
+// Scan implements the sql.Scanner interface.
+func (a *BoolArray) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to BoolArray", src)
+}
+
+func (a *BoolArray) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "BoolArray")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(BoolArray, len(elems))
+ for i, v := range elems {
+ if len(v) != 1 {
+ return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
+ }
+ switch v[0] {
+ case 't':
+ b[i] = true
+ case 'f':
+ b[i] = false
+ default:
+ return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a BoolArray) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be exactly two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1+2*n)
+
+ for i := 0; i < n; i++ {
+ b[2*i] = ','
+ if a[i] {
+ b[1+2*i] = 't'
+ } else {
+ b[1+2*i] = 'f'
+ }
+ }
+
+ b[0] = '{'
+ b[2*n] = '}'
+
+ return string(b), nil
+ }
+
+ return "{}", nil
+}
+
+// ByteaArray represents a one-dimensional array of the PostgreSQL bytea type.
+type ByteaArray [][]byte
+
+// Scan implements the sql.Scanner interface.
+func (a *ByteaArray) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to ByteaArray", src)
+}
+
+func (a *ByteaArray) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "ByteaArray")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(ByteaArray, len(elems))
+ for i, v := range elems {
+ b[i], err = parseBytea(v)
+ if err != nil {
+ return fmt.Errorf("could not parse bytea array index %d: %s", i, err.Error())
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface. It uses the "hex" format which
+// is only supported on PostgreSQL 9.0 or newer.
+func (a ByteaArray) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, 2*N bytes of quotes,
+ // 3*N bytes of hex formatting, and N-1 bytes of delimiters.
+ size := 1 + 6*n
+ for _, x := range a {
+ size += hex.EncodedLen(len(x))
+ }
+
+ b := make([]byte, size)
+
+ for i, s := 0, b; i < n; i++ {
+ o := copy(s, `,"\\x`)
+ o += hex.Encode(s[o:], a[i])
+ s[o] = '"'
+ s = s[o+1:]
+ }
+
+ b[0] = '{'
+ b[size-1] = '}'
+
+ return string(b), nil
+ }
+
+ return "{}", nil
+}
+
+// Float64Array represents a one-dimensional array of the PostgreSQL double
+// precision type.
+type Float64Array []float64
+
+// Scan implements the sql.Scanner interface.
+func (a *Float64Array) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to Float64Array", src)
+}
+
+func (a *Float64Array) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "Float64Array")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(Float64Array, len(elems))
+ for i, v := range elems {
+ if b[i], err = strconv.ParseFloat(string(v), 64); err != nil {
+ return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a Float64Array) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1, 1+2*n)
+ b[0] = '{'
+
+ b = strconv.AppendFloat(b, a[0], 'f', -1, 64)
+ for i := 1; i < n; i++ {
+ b = append(b, ',')
+ b = strconv.AppendFloat(b, a[i], 'f', -1, 64)
+ }
+
+ return string(append(b, '}')), nil
+ }
+
+ return "{}", nil
+}
+
+// GenericArray implements the driver.Valuer and sql.Scanner interfaces for
+// an array or slice of any dimension.
+type GenericArray struct{ A interface{} }
+
+func (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]byte, reflect.Value) error, string) {
+ var assign func([]byte, reflect.Value) error
+ var del = ","
+
+ // TODO calculate the assign function for other types
+ // TODO repeat this section on the element type of arrays or slices (multidimensional)
+ {
+ if reflect.PtrTo(rt).Implements(typeSqlScanner) {
+ // dest is always addressable because it is an element of a slice.
+ assign = func(src []byte, dest reflect.Value) (err error) {
+ ss := dest.Addr().Interface().(sql.Scanner)
+ if src == nil {
+ err = ss.Scan(nil)
+ } else {
+ err = ss.Scan(src)
+ }
+ return
+ }
+ goto FoundType
+ }
+
+ assign = func([]byte, reflect.Value) error {
+ return fmt.Errorf("pq: scanning to %s is not implemented; only sql.Scanner", rt)
+ }
+ }
+
+FoundType:
+
+ if ad, ok := reflect.Zero(rt).Interface().(ArrayDelimiter); ok {
+ del = ad.ArrayDelimiter()
+ }
+
+ return rt, assign, del
+}
+
+// Scan implements the sql.Scanner interface.
+func (a GenericArray) Scan(src interface{}) error {
+ dpv := reflect.ValueOf(a.A)
+ switch {
+ case dpv.Kind() != reflect.Ptr:
+ return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
+ case dpv.IsNil():
+ return fmt.Errorf("pq: destination %T is nil", a.A)
+ }
+
+ dv := dpv.Elem()
+ switch dv.Kind() {
+ case reflect.Slice:
+ case reflect.Array:
+ default:
+ return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
+ }
+
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src, dv)
+ case string:
+ return a.scanBytes([]byte(src), dv)
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type())
+}
+
+func (a GenericArray) scanBytes(src []byte, dv reflect.Value) error {
+ dtype, assign, del := a.evaluateDestination(dv.Type().Elem())
+ dims, elems, err := parseArray(src, []byte(del))
+ if err != nil {
+ return err
+ }
+
+ // TODO allow multidimensional
+
+ if len(dims) > 1 {
+ return fmt.Errorf("pq: scanning from multidimensional ARRAY%s is not implemented",
+ strings.Replace(fmt.Sprint(dims), " ", "][", -1))
+ }
+
+ // Treat a zero-dimensional array like an array with a single dimension of zero.
+ if len(dims) == 0 {
+ dims = append(dims, 0)
+ }
+
+ for i, rt := 0, dv.Type(); i < len(dims); i, rt = i+1, rt.Elem() {
+ switch rt.Kind() {
+ case reflect.Slice:
+ case reflect.Array:
+ if rt.Len() != dims[i] {
+ return fmt.Errorf("pq: cannot convert ARRAY%s to %s",
+ strings.Replace(fmt.Sprint(dims), " ", "][", -1), dv.Type())
+ }
+ default:
+ // TODO handle multidimensional
+ }
+ }
+
+ values := reflect.MakeSlice(reflect.SliceOf(dtype), len(elems), len(elems))
+ for i, e := range elems {
+ if err := assign(e, values.Index(i)); err != nil {
+ return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
+ }
+ }
+
+ // TODO handle multidimensional
+
+ switch dv.Kind() {
+ case reflect.Slice:
+ dv.Set(values.Slice(0, dims[0]))
+ case reflect.Array:
+ for i := 0; i < dims[0]; i++ {
+ dv.Index(i).Set(values.Index(i))
+ }
+ }
+
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a GenericArray) Value() (driver.Value, error) {
+ if a.A == nil {
+ return nil, nil
+ }
+
+ rv := reflect.ValueOf(a.A)
+
+ if k := rv.Kind(); k != reflect.Array && k != reflect.Slice {
+ return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A)
+ }
+
+ if n := rv.Len(); n > 0 {
+ // There will be at least two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 0, 1+2*n)
+
+ b, _, err := appendArray(b, rv, n)
+ return string(b), err
+ }
+
+ return "{}", nil
+}
+
+// Int64Array represents a one-dimensional array of the PostgreSQL integer types.
+type Int64Array []int64
+
+// Scan implements the sql.Scanner interface.
+func (a *Int64Array) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to Int64Array", src)
+}
+
+func (a *Int64Array) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "Int64Array")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(Int64Array, len(elems))
+ for i, v := range elems {
+ if b[i], err = strconv.ParseInt(string(v), 10, 64); err != nil {
+ return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a Int64Array) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1, 1+2*n)
+ b[0] = '{'
+
+ b = strconv.AppendInt(b, a[0], 10)
+ for i := 1; i < n; i++ {
+ b = append(b, ',')
+ b = strconv.AppendInt(b, a[i], 10)
+ }
+
+ return string(append(b, '}')), nil
+ }
+
+ return "{}", nil
+}
+
+// StringArray represents a one-dimensional array of the PostgreSQL character types.
+type StringArray []string
+
+// Scan implements the sql.Scanner interface.
+func (a *StringArray) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to StringArray", src)
+}
+
+func (a *StringArray) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "StringArray")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(StringArray, len(elems))
+ for i, v := range elems {
+ if b[i] = string(v); v == nil {
+ return fmt.Errorf("pq: parsing array element index %d: cannot convert nil to string", i)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a StringArray) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, 2*N bytes of quotes,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1, 1+3*n)
+ b[0] = '{'
+
+ b = appendArrayQuotedBytes(b, []byte(a[0]))
+ for i := 1; i < n; i++ {
+ b = append(b, ',')
+ b = appendArrayQuotedBytes(b, []byte(a[i]))
+ }
+
+ return string(append(b, '}')), nil
+ }
+
+ return "{}", nil
+}
+
+// appendArray appends rv to the buffer, returning the extended buffer and
+// the delimiter used between elements.
+//
+// It panics when n <= 0 or rv's Kind is not reflect.Array nor reflect.Slice.
+func appendArray(b []byte, rv reflect.Value, n int) ([]byte, string, error) {
+ var del string
+ var err error
+
+ b = append(b, '{')
+
+ if b, del, err = appendArrayElement(b, rv.Index(0)); err != nil {
+ return b, del, err
+ }
+
+ for i := 1; i < n; i++ {
+ b = append(b, del...)
+ if b, del, err = appendArrayElement(b, rv.Index(i)); err != nil {
+ return b, del, err
+ }
+ }
+
+ return append(b, '}'), del, nil
+}
+
+// appendArrayElement appends rv to the buffer, returning the extended buffer
+// and the delimiter to use before the next element.
+//
+// When rv's Kind is neither reflect.Array nor reflect.Slice, it is converted
+// using driver.DefaultParameterConverter and the resulting []byte or string
+// is double-quoted.
+//
+// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO
+func appendArrayElement(b []byte, rv reflect.Value) ([]byte, string, error) {
+ if k := rv.Kind(); k == reflect.Array || k == reflect.Slice {
+ if t := rv.Type(); t != typeByteSlice && !t.Implements(typeDriverValuer) {
+ if n := rv.Len(); n > 0 {
+ return appendArray(b, rv, n)
+ }
+
+ return b, "", nil
+ }
+ }
+
+ var del string = ","
+ var err error
+ var iv interface{} = rv.Interface()
+
+ if ad, ok := iv.(ArrayDelimiter); ok {
+ del = ad.ArrayDelimiter()
+ }
+
+ if iv, err = driver.DefaultParameterConverter.ConvertValue(iv); err != nil {
+ return b, del, err
+ }
+
+ switch v := iv.(type) {
+ case nil:
+ return append(b, "NULL"...), del, nil
+ case []byte:
+ return appendArrayQuotedBytes(b, v), del, nil
+ case string:
+ return appendArrayQuotedBytes(b, []byte(v)), del, nil
+ }
+
+ b, err = appendValue(b, iv)
+ return b, del, err
+}
+
+func appendArrayQuotedBytes(b, v []byte) []byte {
+ b = append(b, '"')
+ for {
+ i := bytes.IndexAny(v, `"\`)
+ if i < 0 {
+ b = append(b, v...)
+ break
+ }
+ if i > 0 {
+ b = append(b, v[:i]...)
+ }
+ b = append(b, '\\', v[i])
+ v = v[i+1:]
+ }
+ return append(b, '"')
+}
+
+func appendValue(b []byte, v driver.Value) ([]byte, error) {
+ return append(b, encode(nil, v, 0)...), nil
+}
+
+// parseArray extracts the dimensions and elements of an array represented in
+// text format. Only representations emitted by the backend are supported.
+// Notably, whitespace around brackets and delimiters is significant, and NULL
+// is case-sensitive.
+//
+// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO
+func parseArray(src, del []byte) (dims []int, elems [][]byte, err error) {
+ var depth, i int
+
+ if len(src) < 1 || src[0] != '{' {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '{', 0)
+ }
+
+Open:
+ for i < len(src) {
+ switch src[i] {
+ case '{':
+ depth++
+ i++
+ case '}':
+ elems = make([][]byte, 0)
+ goto Close
+ default:
+ break Open
+ }
+ }
+ dims = make([]int, i)
+
+Element:
+ for i < len(src) {
+ switch src[i] {
+ case '{':
+ depth++
+ dims[depth-1] = 0
+ i++
+ case '"':
+ var elem = []byte{}
+ var escape bool
+ for i++; i < len(src); i++ {
+ if escape {
+ elem = append(elem, src[i])
+ escape = false
+ } else {
+ switch src[i] {
+ default:
+ elem = append(elem, src[i])
+ case '\\':
+ escape = true
+ case '"':
+ elems = append(elems, elem)
+ i++
+ break Element
+ }
+ }
+ }
+ default:
+ for start := i; i < len(src); i++ {
+ if bytes.HasPrefix(src[i:], del) || src[i] == '}' {
+ elem := src[start:i]
+ if len(elem) == 0 {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
+ }
+ if bytes.Equal(elem, []byte("NULL")) {
+ elem = nil
+ }
+ elems = append(elems, elem)
+ break Element
+ }
+ }
+ }
+ }
+
+ for i < len(src) {
+ if bytes.HasPrefix(src[i:], del) {
+ dims[depth-1]++
+ i += len(del)
+ goto Element
+ } else if src[i] == '}' {
+ dims[depth-1]++
+ depth--
+ i++
+ } else {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
+ }
+ }
+
+Close:
+ for i < len(src) {
+ if src[i] == '}' && depth > 0 {
+ depth--
+ i++
+ } else {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
+ }
+ }
+ if depth > 0 {
+ err = fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '}', i)
+ }
+ if err == nil {
+ for _, d := range dims {
+ if (len(elems) % d) != 0 {
+ err = fmt.Errorf("pq: multidimensional arrays must have elements with matching dimensions")
+ }
+ }
+ }
+ return
+}
+
+func scanLinearArray(src, del []byte, typ string) (elems [][]byte, err error) {
+ dims, elems, err := parseArray(src, del)
+ if err != nil {
+ return nil, err
+ }
+ if len(dims) > 1 {
+ return nil, fmt.Errorf("pq: cannot convert ARRAY%s to %s", strings.Replace(fmt.Sprint(dims), " ", "][", -1), typ)
+ }
+ return elems, err
+}
diff --git a/vendor/github.com/lib/pq/array_test.go b/vendor/github.com/lib/pq/array_test.go
new file mode 100644
index 000000000..96402fd4a
--- /dev/null
+++ b/vendor/github.com/lib/pq/array_test.go
@@ -0,0 +1,1153 @@
+package pq
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "math/rand"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestParseArray(t *testing.T) {
+ for _, tt := range []struct {
+ input string
+ delim string
+ dims []int
+ elems [][]byte
+ }{
+ {`{}`, `,`, nil, [][]byte{}},
+ {`{NULL}`, `,`, []int{1}, [][]byte{nil}},
+ {`{a}`, `,`, []int{1}, [][]byte{{'a'}}},
+ {`{a,b}`, `,`, []int{2}, [][]byte{{'a'}, {'b'}}},
+ {`{{a,b}}`, `,`, []int{1, 2}, [][]byte{{'a'}, {'b'}}},
+ {`{{a},{b}}`, `,`, []int{2, 1}, [][]byte{{'a'}, {'b'}}},
+ {`{{{a,b},{c,d},{e,f}}}`, `,`, []int{1, 3, 2}, [][]byte{
+ {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
+ }},
+ {`{""}`, `,`, []int{1}, [][]byte{{}}},
+ {`{","}`, `,`, []int{1}, [][]byte{{','}}},
+ {`{",",","}`, `,`, []int{2}, [][]byte{{','}, {','}}},
+ {`{{",",","}}`, `,`, []int{1, 2}, [][]byte{{','}, {','}}},
+ {`{{","},{","}}`, `,`, []int{2, 1}, [][]byte{{','}, {','}}},
+ {`{{{",",","},{",",","},{",",","}}}`, `,`, []int{1, 3, 2}, [][]byte{
+ {','}, {','}, {','}, {','}, {','}, {','},
+ }},
+ {`{"\"}"}`, `,`, []int{1}, [][]byte{{'"', '}'}}},
+ {`{"\"","\""}`, `,`, []int{2}, [][]byte{{'"'}, {'"'}}},
+ {`{{"\"","\""}}`, `,`, []int{1, 2}, [][]byte{{'"'}, {'"'}}},
+ {`{{"\""},{"\""}}`, `,`, []int{2, 1}, [][]byte{{'"'}, {'"'}}},
+ {`{{{"\"","\""},{"\"","\""},{"\"","\""}}}`, `,`, []int{1, 3, 2}, [][]byte{
+ {'"'}, {'"'}, {'"'}, {'"'}, {'"'}, {'"'},
+ }},
+ {`{axyzb}`, `xyz`, []int{2}, [][]byte{{'a'}, {'b'}}},
+ } {
+ dims, elems, err := parseArray([]byte(tt.input), []byte(tt.delim))
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %q", tt.input, err)
+ }
+ if !reflect.DeepEqual(dims, tt.dims) {
+ t.Errorf("Expected %v dimensions for %q, got %v", tt.dims, tt.input, dims)
+ }
+ if !reflect.DeepEqual(elems, tt.elems) {
+ t.Errorf("Expected %v elements for %q, got %v", tt.elems, tt.input, elems)
+ }
+ }
+}
+
+func TestParseArrayError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "expected '{' at offset 0"},
+ {`x`, "expected '{' at offset 0"},
+ {`}`, "expected '{' at offset 0"},
+ {`{`, "expected '}' at offset 1"},
+ {`{{}`, "expected '}' at offset 3"},
+ {`{}}`, "unexpected '}' at offset 2"},
+ {`{,}`, "unexpected ',' at offset 1"},
+ {`{,x}`, "unexpected ',' at offset 1"},
+ {`{x,}`, "unexpected '}' at offset 3"},
+ {`{""x}`, "unexpected 'x' at offset 3"},
+ {`{{a},{b,c}}`, "multidimensional arrays must have elements with matching dimensions"},
+ } {
+ _, _, err := parseArray([]byte(tt.input), []byte{','})
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ }
+}
+
+func TestArrayScanner(t *testing.T) {
+ var s sql.Scanner
+
+ s = Array(&[]bool{})
+ if _, ok := s.(*BoolArray); !ok {
+ t.Errorf("Expected *BoolArray, got %T", s)
+ }
+
+ s = Array(&[]float64{})
+ if _, ok := s.(*Float64Array); !ok {
+ t.Errorf("Expected *Float64Array, got %T", s)
+ }
+
+ s = Array(&[]int64{})
+ if _, ok := s.(*Int64Array); !ok {
+ t.Errorf("Expected *Int64Array, got %T", s)
+ }
+
+ s = Array(&[]string{})
+ if _, ok := s.(*StringArray); !ok {
+ t.Errorf("Expected *StringArray, got %T", s)
+ }
+
+ for _, tt := range []interface{}{
+ &[]sql.Scanner{},
+ &[][]bool{},
+ &[][]float64{},
+ &[][]int64{},
+ &[][]string{},
+ } {
+ s = Array(tt)
+ if _, ok := s.(GenericArray); !ok {
+ t.Errorf("Expected GenericArray for %T, got %T", tt, s)
+ }
+ }
+}
+
+func TestArrayValuer(t *testing.T) {
+ var v driver.Valuer
+
+ v = Array([]bool{})
+ if _, ok := v.(*BoolArray); !ok {
+ t.Errorf("Expected *BoolArray, got %T", v)
+ }
+
+ v = Array([]float64{})
+ if _, ok := v.(*Float64Array); !ok {
+ t.Errorf("Expected *Float64Array, got %T", v)
+ }
+
+ v = Array([]int64{})
+ if _, ok := v.(*Int64Array); !ok {
+ t.Errorf("Expected *Int64Array, got %T", v)
+ }
+
+ v = Array([]string{})
+ if _, ok := v.(*StringArray); !ok {
+ t.Errorf("Expected *StringArray, got %T", v)
+ }
+
+ for _, tt := range []interface{}{
+ nil,
+ []driver.Value{},
+ [][]bool{},
+ [][]float64{},
+ [][]int64{},
+ [][]string{},
+ } {
+ v = Array(tt)
+ if _, ok := v.(GenericArray); !ok {
+ t.Errorf("Expected GenericArray for %T, got %T", tt, v)
+ }
+ }
+}
+
+func TestBoolArrayScanUnsupported(t *testing.T) {
+ var arr BoolArray
+ err := arr.Scan(1)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from int")
+ }
+ if !strings.Contains(err.Error(), "int to BoolArray") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var BoolArrayStringTests = []struct {
+ str string
+ arr BoolArray
+}{
+ {`{}`, BoolArray{}},
+ {`{t}`, BoolArray{true}},
+ {`{f,t}`, BoolArray{false, true}},
+}
+
+func TestBoolArrayScanBytes(t *testing.T) {
+ for _, tt := range BoolArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := BoolArray{true, true, true}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkBoolArrayScanBytes(b *testing.B) {
+ var a BoolArray
+ var x interface{} = []byte(`{t,f,t,f,t,f,t,f,t,f}`)
+
+ for i := 0; i < b.N; i++ {
+ a = BoolArray{}
+ a.Scan(x)
+ }
+}
+
+func TestBoolArrayScanString(t *testing.T) {
+ for _, tt := range BoolArrayStringTests {
+ arr := BoolArray{true, true, true}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestBoolArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{t},{f}}`, "cannot convert ARRAY[2][1] to BoolArray"},
+ {`{NULL}`, `could not parse boolean array index 0: invalid boolean ""`},
+ {`{a}`, `could not parse boolean array index 0: invalid boolean "a"`},
+ {`{t,b}`, `could not parse boolean array index 1: invalid boolean "b"`},
+ {`{t,f,cd}`, `could not parse boolean array index 2: invalid boolean "cd"`},
+ } {
+ arr := BoolArray{true, true, true}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, BoolArray{true, true, true}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestBoolArrayValue(t *testing.T) {
+ result, err := BoolArray(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = BoolArray([]bool{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = BoolArray([]bool{false, true, false}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{f,t,f}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkBoolArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([]bool, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Intn(2) == 0
+ }
+ a := BoolArray(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestByteaArrayScanUnsupported(t *testing.T) {
+ var arr ByteaArray
+ err := arr.Scan(1)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from int")
+ }
+ if !strings.Contains(err.Error(), "int to ByteaArray") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var ByteaArrayStringTests = []struct {
+ str string
+ arr ByteaArray
+}{
+ {`{}`, ByteaArray{}},
+ {`{NULL}`, ByteaArray{nil}},
+ {`{"\\xfeff"}`, ByteaArray{{'\xFE', '\xFF'}}},
+ {`{"\\xdead","\\xbeef"}`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}},
+}
+
+func TestByteaArrayScanBytes(t *testing.T) {
+ for _, tt := range ByteaArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := ByteaArray{{2}, {6}, {0, 0}}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkByteaArrayScanBytes(b *testing.B) {
+ var a ByteaArray
+ var x interface{} = []byte(`{"\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff"}`)
+
+ for i := 0; i < b.N; i++ {
+ a = ByteaArray{}
+ a.Scan(x)
+ }
+}
+
+func TestByteaArrayScanString(t *testing.T) {
+ for _, tt := range ByteaArrayStringTests {
+ arr := ByteaArray{{2}, {6}, {0, 0}}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestByteaArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{"\\xfeff"},{"\\xbeef"}}`, "cannot convert ARRAY[2][1] to ByteaArray"},
+ {`{"\\abc"}`, "could not parse bytea array index 0: could not parse bytea value"},
+ } {
+ arr := ByteaArray{{2}, {6}, {0, 0}}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, ByteaArray{{2}, {6}, {0, 0}}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestByteaArrayValue(t *testing.T) {
+ result, err := ByteaArray(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = ByteaArray([][]byte{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = ByteaArray([][]byte{{'\xDE', '\xAD', '\xBE', '\xEF'}, {'\xFE', '\xFF'}, {}}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{"\\xdeadbeef","\\xfeff","\\x"}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkByteaArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([][]byte, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = make([]byte, len(x))
+ for j := 0; j < len(x); j++ {
+ x[i][j] = byte(rand.Int())
+ }
+ }
+ a := ByteaArray(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestFloat64ArrayScanUnsupported(t *testing.T) {
+ var arr Float64Array
+ err := arr.Scan(true)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from bool")
+ }
+ if !strings.Contains(err.Error(), "bool to Float64Array") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var Float64ArrayStringTests = []struct {
+ str string
+ arr Float64Array
+}{
+ {`{}`, Float64Array{}},
+ {`{1.2}`, Float64Array{1.2}},
+ {`{3.456,7.89}`, Float64Array{3.456, 7.89}},
+ {`{3,1,2}`, Float64Array{3, 1, 2}},
+}
+
+func TestFloat64ArrayScanBytes(t *testing.T) {
+ for _, tt := range Float64ArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := Float64Array{5, 5, 5}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkFloat64ArrayScanBytes(b *testing.B) {
+ var a Float64Array
+ var x interface{} = []byte(`{1.2,3.4,5.6,7.8,9.01,2.34,5.67,8.90,1.234,5.678}`)
+
+ for i := 0; i < b.N; i++ {
+ a = Float64Array{}
+ a.Scan(x)
+ }
+}
+
+func TestFloat64ArrayScanString(t *testing.T) {
+ for _, tt := range Float64ArrayStringTests {
+ arr := Float64Array{5, 5, 5}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestFloat64ArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{5.6},{7.8}}`, "cannot convert ARRAY[2][1] to Float64Array"},
+ {`{NULL}`, "parsing array element index 0:"},
+ {`{a}`, "parsing array element index 0:"},
+ {`{5.6,a}`, "parsing array element index 1:"},
+ {`{5.6,7.8,a}`, "parsing array element index 2:"},
+ } {
+ arr := Float64Array{5, 5, 5}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, Float64Array{5, 5, 5}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestFloat64ArrayValue(t *testing.T) {
+ result, err := Float64Array(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = Float64Array([]float64{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = Float64Array([]float64{1.2, 3.4, 5.6}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{1.2,3.4,5.6}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkFloat64ArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([]float64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.NormFloat64()
+ }
+ a := Float64Array(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestInt64ArrayScanUnsupported(t *testing.T) {
+ var arr Int64Array
+ err := arr.Scan(true)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from bool")
+ }
+ if !strings.Contains(err.Error(), "bool to Int64Array") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var Int64ArrayStringTests = []struct {
+ str string
+ arr Int64Array
+}{
+ {`{}`, Int64Array{}},
+ {`{12}`, Int64Array{12}},
+ {`{345,678}`, Int64Array{345, 678}},
+}
+
+func TestInt64ArrayScanBytes(t *testing.T) {
+ for _, tt := range Int64ArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := Int64Array{5, 5, 5}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkInt64ArrayScanBytes(b *testing.B) {
+ var a Int64Array
+ var x interface{} = []byte(`{1,2,3,4,5,6,7,8,9,0}`)
+
+ for i := 0; i < b.N; i++ {
+ a = Int64Array{}
+ a.Scan(x)
+ }
+}
+
+func TestInt64ArrayScanString(t *testing.T) {
+ for _, tt := range Int64ArrayStringTests {
+ arr := Int64Array{5, 5, 5}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestInt64ArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{5},{6}}`, "cannot convert ARRAY[2][1] to Int64Array"},
+ {`{NULL}`, "parsing array element index 0:"},
+ {`{a}`, "parsing array element index 0:"},
+ {`{5,a}`, "parsing array element index 1:"},
+ {`{5,6,a}`, "parsing array element index 2:"},
+ } {
+ arr := Int64Array{5, 5, 5}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, Int64Array{5, 5, 5}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestInt64ArrayValue(t *testing.T) {
+ result, err := Int64Array(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = Int64Array([]int64{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = Int64Array([]int64{1, 2, 3}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{1,2,3}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkInt64ArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([]int64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Int63()
+ }
+ a := Int64Array(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestStringArrayScanUnsupported(t *testing.T) {
+ var arr StringArray
+ err := arr.Scan(true)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from bool")
+ }
+ if !strings.Contains(err.Error(), "bool to StringArray") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var StringArrayStringTests = []struct {
+ str string
+ arr StringArray
+}{
+ {`{}`, StringArray{}},
+ {`{t}`, StringArray{"t"}},
+ {`{f,1}`, StringArray{"f", "1"}},
+ {`{"a\\b","c d",","}`, StringArray{"a\\b", "c d", ","}},
+}
+
+func TestStringArrayScanBytes(t *testing.T) {
+ for _, tt := range StringArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := StringArray{"x", "x", "x"}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkStringArrayScanBytes(b *testing.B) {
+ var a StringArray
+ var x interface{} = []byte(`{a,b,c,d,e,f,g,h,i,j}`)
+ var y interface{} = []byte(`{"\a","\b","\c","\d","\e","\f","\g","\h","\i","\j"}`)
+
+ for i := 0; i < b.N; i++ {
+ a = StringArray{}
+ a.Scan(x)
+ a = StringArray{}
+ a.Scan(y)
+ }
+}
+
+func TestStringArrayScanString(t *testing.T) {
+ for _, tt := range StringArrayStringTests {
+ arr := StringArray{"x", "x", "x"}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestStringArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{a},{b}}`, "cannot convert ARRAY[2][1] to StringArray"},
+ {`{NULL}`, "parsing array element index 0: cannot convert nil to string"},
+ {`{a,NULL}`, "parsing array element index 1: cannot convert nil to string"},
+ {`{a,b,NULL}`, "parsing array element index 2: cannot convert nil to string"},
+ } {
+ arr := StringArray{"x", "x", "x"}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, StringArray{"x", "x", "x"}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestStringArrayValue(t *testing.T) {
+ result, err := StringArray(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = StringArray([]string{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = StringArray([]string{`a`, `\b`, `c"`, `d,e`}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{"a","\\b","c\"","d,e"}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkStringArrayValue(b *testing.B) {
+ x := make([]string, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = strings.Repeat(`abc"def\ghi`, 5)
+ }
+ a := StringArray(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestGenericArrayScanUnsupported(t *testing.T) {
+ var s string
+ var ss []string
+
+ for _, tt := range []struct {
+ src, dest interface{}
+ err string
+ }{
+ {nil, nil, "destination <nil> is not a pointer to array or slice"},
+ {nil, true, "destination bool is not a pointer to array or slice"},
+ {nil, &s, "destination *string is not a pointer to array or slice"},
+ {nil, ss, "destination []string is not a pointer to array or slice"},
+ {true, &ss, "bool to []string"},
+ {`{{x}}`, &ss, "multidimensional ARRAY[1][1] is not implemented"},
+ {`{{x},{x}}`, &ss, "multidimensional ARRAY[2][1] is not implemented"},
+ {`{x}`, &ss, "scanning to string is not implemented"},
+ } {
+ err := GenericArray{tt.dest}.Scan(tt.src)
+
+ if err == nil {
+ t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err)
+ }
+ }
+}
+
+func TestGenericArrayScanScannerArrayBytes(t *testing.T) {
+ src, expected, nsa := []byte(`{NULL,abc,"\""}`),
+ [3]sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}},
+ [3]sql.NullString{{String: ``, Valid: true}, {}, {}}
+
+ if err := (GenericArray{&nsa}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nsa, expected) {
+ t.Errorf("Expected %v, got %v", expected, nsa)
+ }
+}
+
+func TestGenericArrayScanScannerArrayString(t *testing.T) {
+ src, expected, nsa := `{NULL,"\"",xyz}`,
+ [3]sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}},
+ [3]sql.NullString{{String: ``, Valid: true}, {}, {}}
+
+ if err := (GenericArray{&nsa}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nsa, expected) {
+ t.Errorf("Expected %v, got %v", expected, nsa)
+ }
+}
+
+func TestGenericArrayScanScannerSliceBytes(t *testing.T) {
+ src, expected, nss := []byte(`{NULL,abc,"\""}`),
+ []sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}},
+ []sql.NullString{{String: ``, Valid: true}, {}, {}, {}, {}}
+
+ if err := (GenericArray{&nss}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nss, expected) {
+ t.Errorf("Expected %v, got %v", expected, nss)
+ }
+}
+
+func BenchmarkGenericArrayScanScannerSliceBytes(b *testing.B) {
+ var a GenericArray
+ var x interface{} = []byte(`{a,b,c,d,e,f,g,h,i,j}`)
+ var y interface{} = []byte(`{"\a","\b","\c","\d","\e","\f","\g","\h","\i","\j"}`)
+
+ for i := 0; i < b.N; i++ {
+ a = GenericArray{new([]sql.NullString)}
+ a.Scan(x)
+ a = GenericArray{new([]sql.NullString)}
+ a.Scan(y)
+ }
+}
+
+func TestGenericArrayScanScannerSliceString(t *testing.T) {
+ src, expected, nss := `{NULL,"\"",xyz}`,
+ []sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}},
+ []sql.NullString{{String: ``, Valid: true}, {}, {}}
+
+ if err := (GenericArray{&nss}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nss, expected) {
+ t.Errorf("Expected %v, got %v", expected, nss)
+ }
+}
+
+type TildeNullInt64 struct{ sql.NullInt64 }
+
+func (TildeNullInt64) ArrayDelimiter() string { return "~" }
+
+func TestGenericArrayScanDelimiter(t *testing.T) {
+ src, expected, tnis := `{12~NULL~76}`,
+ []TildeNullInt64{{sql.NullInt64{Int64: 12, Valid: true}}, {}, {sql.NullInt64{Int64: 76, Valid: true}}},
+ []TildeNullInt64{{sql.NullInt64{Int64: 0, Valid: true}}, {}}
+
+ if err := (GenericArray{&tnis}).Scan(src); err != nil {
+ t.Fatalf("Expected no error for %#v, got %v", src, err)
+ }
+ if !reflect.DeepEqual(tnis, expected) {
+ t.Errorf("Expected %v for %#v, got %v", expected, src, tnis)
+ }
+}
+
+func TestGenericArrayScanErrors(t *testing.T) {
+ var sa [1]string
+ var nis []sql.NullInt64
+ var pss *[]string
+
+ for _, tt := range []struct {
+ src, dest interface{}
+ err string
+ }{
+ {nil, pss, "destination *[]string is nil"},
+ {`{`, &sa, "unable to parse"},
+ {`{}`, &sa, "cannot convert ARRAY[0] to [1]string"},
+ {`{x,x}`, &sa, "cannot convert ARRAY[2] to [1]string"},
+ {`{x}`, &nis, `parsing array element index 0: converting`},
+ } {
+ err := GenericArray{tt.dest}.Scan(tt.src)
+
+ if err == nil {
+ t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err)
+ }
+ }
+}
+
+func TestGenericArrayValueUnsupported(t *testing.T) {
+ _, err := GenericArray{true}.Value()
+
+ if err == nil {
+ t.Fatal("Expected error for bool")
+ }
+ if !strings.Contains(err.Error(), "bool to array") {
+ t.Errorf("Expected type to be mentioned, got %q", err)
+ }
+}
+
+type ByteArrayValuer [1]byte
+type ByteSliceValuer []byte
+type FuncArrayValuer struct {
+ delimiter func() string
+ value func() (driver.Value, error)
+}
+
+func (a ByteArrayValuer) Value() (driver.Value, error) { return a[:], nil }
+func (b ByteSliceValuer) Value() (driver.Value, error) { return []byte(b), nil }
+func (f FuncArrayValuer) ArrayDelimiter() string { return f.delimiter() }
+func (f FuncArrayValuer) Value() (driver.Value, error) { return f.value() }
+
+func TestGenericArrayValue(t *testing.T) {
+ result, err := GenericArray{nil}.Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ Tilde := func(v driver.Value) FuncArrayValuer {
+ return FuncArrayValuer{
+ func() string { return "~" },
+ func() (driver.Value, error) { return v, nil }}
+ }
+
+ for _, tt := range []struct {
+ result string
+ input interface{}
+ }{
+ {`{}`, []bool{}},
+ {`{true}`, []bool{true}},
+ {`{true,false}`, []bool{true, false}},
+ {`{true,false}`, [2]bool{true, false}},
+
+ {`{}`, [][]int{{}}},
+ {`{}`, [][]int{{}, {}}},
+ {`{{1}}`, [][]int{{1}}},
+ {`{{1},{2}}`, [][]int{{1}, {2}}},
+ {`{{1,2},{3,4}}`, [][]int{{1, 2}, {3, 4}}},
+ {`{{1,2},{3,4}}`, [2][2]int{{1, 2}, {3, 4}}},
+
+ {`{"a","\\b","c\"","d,e"}`, []string{`a`, `\b`, `c"`, `d,e`}},
+ {`{"a","\\b","c\"","d,e"}`, [][]byte{{'a'}, {'\\', 'b'}, {'c', '"'}, {'d', ',', 'e'}}},
+
+ {`{NULL}`, []*int{nil}},
+ {`{0,NULL}`, []*int{new(int), nil}},
+
+ {`{NULL}`, []sql.NullString{{}}},
+ {`{"\"",NULL}`, []sql.NullString{{String: `"`, Valid: true}, {}}},
+
+ {`{"a","b"}`, []ByteArrayValuer{{'a'}, {'b'}}},
+ {`{{"a","b"},{"c","d"}}`, [][]ByteArrayValuer{{{'a'}, {'b'}}, {{'c'}, {'d'}}}},
+
+ {`{"e","f"}`, []ByteSliceValuer{{'e'}, {'f'}}},
+ {`{{"e","f"},{"g","h"}}`, [][]ByteSliceValuer{{{'e'}, {'f'}}, {{'g'}, {'h'}}}},
+
+ {`{1~2}`, []FuncArrayValuer{Tilde(int64(1)), Tilde(int64(2))}},
+ {`{{1~2}~{3~4}}`, [][]FuncArrayValuer{{Tilde(int64(1)), Tilde(int64(2))}, {Tilde(int64(3)), Tilde(int64(4))}}},
+ } {
+ result, err := GenericArray{tt.input}.Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.input, err)
+ }
+ if !reflect.DeepEqual(result, tt.result) {
+ t.Errorf("Expected %q for %q, got %q", tt.result, tt.input, result)
+ }
+ }
+}
+
+func TestGenericArrayValueErrors(t *testing.T) {
+ var v []interface{}
+
+ v = []interface{}{func() {}}
+ if _, err := (GenericArray{v}).Value(); err == nil {
+ t.Errorf("Expected error for %q, got nil", v)
+ }
+
+ v = []interface{}{nil, func() {}}
+ if _, err := (GenericArray{v}).Value(); err == nil {
+ t.Errorf("Expected error for %q, got nil", v)
+ }
+}
+
+func BenchmarkGenericArrayValueBools(b *testing.B) {
+ rand.Seed(1)
+ x := make([]bool, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Intn(2) == 0
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueFloat64s(b *testing.B) {
+ rand.Seed(1)
+ x := make([]float64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.NormFloat64()
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueInt64s(b *testing.B) {
+ rand.Seed(1)
+ x := make([]int64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Int63()
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueByteSlices(b *testing.B) {
+ x := make([][]byte, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = bytes.Repeat([]byte(`abc"def\ghi`), 5)
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueStrings(b *testing.B) {
+ x := make([]string, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = strings.Repeat(`abc"def\ghi`, 5)
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestArrayScanBackend(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ for _, tt := range []struct {
+ s string
+ d sql.Scanner
+ e interface{}
+ }{
+ {`ARRAY[true, false]`, new(BoolArray), &BoolArray{true, false}},
+ {`ARRAY[E'\\xdead', E'\\xbeef']`, new(ByteaArray), &ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}},
+ {`ARRAY[1.2, 3.4]`, new(Float64Array), &Float64Array{1.2, 3.4}},
+ {`ARRAY[1, 2, 3]`, new(Int64Array), &Int64Array{1, 2, 3}},
+ {`ARRAY['a', E'\\b', 'c"', 'd,e']`, new(StringArray), &StringArray{`a`, `\b`, `c"`, `d,e`}},
+ } {
+ err := db.QueryRow(`SELECT ` + tt.s).Scan(tt.d)
+ if err != nil {
+ t.Errorf("Expected no error when scanning %s into %T, got %v", tt.s, tt.d, err)
+ }
+ if !reflect.DeepEqual(tt.d, tt.e) {
+ t.Errorf("Expected %v when scanning %s into %T, got %v", tt.e, tt.s, tt.d, tt.d)
+ }
+ }
+}
+
+func TestArrayValueBackend(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ for _, tt := range []struct {
+ s string
+ v driver.Valuer
+ }{
+ {`ARRAY[true, false]`, BoolArray{true, false}},
+ {`ARRAY[E'\\xdead', E'\\xbeef']`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}},
+ {`ARRAY[1.2, 3.4]`, Float64Array{1.2, 3.4}},
+ {`ARRAY[1, 2, 3]`, Int64Array{1, 2, 3}},
+ {`ARRAY['a', E'\\b', 'c"', 'd,e']`, StringArray{`a`, `\b`, `c"`, `d,e`}},
+ } {
+ var x int
+ err := db.QueryRow(`SELECT 1 WHERE `+tt.s+` <> $1`, tt.v).Scan(&x)
+ if err != sql.ErrNoRows {
+ t.Errorf("Expected %v to equal %s, got %v", tt.v, tt.s, err)
+ }
+ }
+}
diff --git a/vendor/github.com/lib/pq/certs/bogus_root.crt b/vendor/github.com/lib/pq/certs/bogus_root.crt
new file mode 100644
index 000000000..1239db3a4
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/bogus_root.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBjCCAe6gAwIBAgIQSnDYp/Naet9HOZljF5PuwDANBgkqhkiG9w0BAQsFADAr
+MRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENvY2tyb2FjaCBDQTAeFw0x
+NjAyMDcxNjQ0MzdaFw0xNzAyMDYxNjQ0MzdaMCsxEjAQBgNVBAoTCUNvY2tyb2Fj
+aDEVMBMGA1UEAxMMQ29ja3JvYWNoIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAxdln3/UdgP7ayA/G1kT7upjLe4ERwQjYQ25q0e1+vgsB5jhiirxJ
+e0+WkhhYu/mwoSAXzvlsbZ2PWFyfdanZeD/Lh6SvIeWXVVaPcWVWL1TEcoN2jr5+
+E85MMHmbbmaT2he8s6br2tM/UZxyTQ2XRprIzApbDssyw1c0Yufcpu3C6267FLEl
+IfcWrzDhnluFhthhtGXv3ToD8IuMScMC5qlKBXtKmD1B5x14ngO/ecNJ+OlEi0HU
+mavK4KWgI2rDXRZ2EnCpyTZdkc3kkRnzKcg653oOjMDRZdrhfIrha+Jq38ACsUmZ
+Su7Sp5jkIHOCO8Zg+l6GKVSq37dKMapD8wIDAQABoyYwJDAOBgNVHQ8BAf8EBAMC
+AuQwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAQEAwZ2Tu0Yu
+rrSVdMdoPEjT1IZd+5OhM/SLzL0ddtvTithRweLHsw2lDQYlXFqr24i3UGZJQ1sp
+cqSrNwswgLUQT3vWyTjmM51HEb2vMYWKmjZ+sBQYAUP1CadrN/+OTfNGnlF1+B4w
+IXOzh7EvQmJJnNybLe4a/aRvj1NE2n8Z898B76SVU9WbfKKz8VwLzuIPDqkKcZda
+lMy5yzthyztV9YjcWs2zVOUGZvGdAhDrvZuUq6mSmxrBEvR2LBOggmVf3tGRT+Ls
+lW7c9Lrva5zLHuqmoPP07A+vuI9a0D1X44jwGDuPWJ5RnTOQ63Uez12mKNjqleHw
+DnkwNanuO8dhAA==
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go
index 336c89449..8e1aee9f0 100644
--- a/vendor/github.com/lib/pq/conn.go
+++ b/vendor/github.com/lib/pq/conn.go
@@ -968,8 +968,23 @@ func (cn *conn) ssl(o values) {
verifyCaOnly := false
tlsConf := tls.Config{}
switch mode := o.Get("sslmode"); mode {
- case "require", "":
+ // "require" is the default.
+ case "", "require":
+ // We must skip TLS's own verification since it requires full
+ // verification since Go 1.3.
tlsConf.InsecureSkipVerify = true
+
+ // From http://www.postgresql.org/docs/current/static/libpq-ssl.html:
+ // Note: For backwards compatibility with earlier versions of PostgreSQL, if a
+ // root CA file exists, the behavior of sslmode=require will be the same as
+ // that of verify-ca, meaning the server certificate is validated against the
+ // CA. Relying on this behavior is discouraged, and applications that need
+ // certificate validation should always use verify-ca or verify-full.
+ if _, err := os.Stat(o.Get("sslrootcert")); err == nil {
+ verifyCaOnly = true
+ } else {
+ o.Set("sslrootcert", "")
+ }
case "verify-ca":
// We must skip TLS's own verification since it requires full
// verification since Go 1.3.
diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go
index 274147c27..592860f8a 100644
--- a/vendor/github.com/lib/pq/conn_test.go
+++ b/vendor/github.com/lib/pq/conn_test.go
@@ -138,6 +138,10 @@ func TestOpenURL(t *testing.T) {
const pgpass_file = "/tmp/pqgotest_pgpass"
func TestPgpass(t *testing.T) {
+ if os.Getenv("TRAVIS") != "true" {
+ t.Skip("not running under Travis, skipping pgpass tests")
+ }
+
testAssert := func(conninfo string, expected string, reason string) {
conn, err := openTestConnConninfo(conninfo)
if err != nil {
@@ -186,7 +190,17 @@ localhost:*:*:*:pass_C
pgpass.Close()
assertPassword := func(extra values, expected string) {
- o := &values{"host": "localhost", "sslmode": "disable", "connect_timeout": "20", "user": "majid", "port": "5432", "extra_float_digits": "2", "dbname": "pqgotest", "client_encoding": "UTF8", "datestyle": "ISO, MDY"}
+ o := &values{
+ "host": "localhost",
+ "sslmode": "disable",
+ "connect_timeout": "20",
+ "user": "majid",
+ "port": "5432",
+ "extra_float_digits": "2",
+ "dbname": "pqgotest",
+ "client_encoding": "UTF8",
+ "datestyle": "ISO, MDY",
+ }
for k, v := range extra {
(*o)[k] = v
}
diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go
index 6681bd3e7..29e8f6ff7 100644
--- a/vendor/github.com/lib/pq/encode.go
+++ b/vendor/github.com/lib/pq/encode.go
@@ -56,10 +56,13 @@ func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) [
}
func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid, f format) interface{} {
- if f == formatBinary {
+ switch f {
+ case formatBinary:
return binaryDecode(parameterStatus, s, typ)
- } else {
+ case formatText:
return textDecode(parameterStatus, s, typ)
+ default:
+ panic("not reached")
}
}
@@ -83,8 +86,14 @@ func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) inter
func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
switch typ {
+ case oid.T_char, oid.T_varchar, oid.T_text:
+ return string(s)
case oid.T_bytea:
- return parseBytea(s)
+ b, err := parseBytea(s)
+ if err != nil {
+ errorf("%s", err)
+ }
+ return b
case oid.T_timestamptz:
return parseTs(parameterStatus.currentLocation, string(s))
case oid.T_timestamp, oid.T_date:
@@ -195,21 +204,21 @@ func mustParse(f string, typ oid.Oid, s []byte) time.Time {
return t
}
-var invalidTimestampErr = errors.New("invalid timestamp")
+var errInvalidTimestamp = errors.New("invalid timestamp")
type timestampParser struct {
err error
}
-func (p *timestampParser) expect(str, char string, pos int) {
+func (p *timestampParser) expect(str string, char byte, pos int) {
if p.err != nil {
return
}
if pos+1 > len(str) {
- p.err = invalidTimestampErr
+ p.err = errInvalidTimestamp
return
}
- if c := str[pos : pos+1]; c != char && p.err == nil {
+ if c := str[pos]; c != char && p.err == nil {
p.err = fmt.Errorf("expected '%v' at position %v; got '%v'", char, pos, c)
}
}
@@ -219,7 +228,7 @@ func (p *timestampParser) mustAtoi(str string, begin int, end int) int {
return 0
}
if begin < 0 || end < 0 || begin > end || end > len(str) {
- p.err = invalidTimestampErr
+ p.err = errInvalidTimestamp
return 0
}
result, err := strconv.Atoi(str[begin:end])
@@ -242,7 +251,7 @@ type locationCache struct {
// about 5% speed could be gained by putting the cache in the connection and
// losing the mutex, at the cost of a small amount of memory and a somewhat
// significant increase in code complexity.
-var globalLocationCache *locationCache = newLocationCache()
+var globalLocationCache = newLocationCache()
func newLocationCache() *locationCache {
return &locationCache{cache: make(map[int]*time.Location)}
@@ -272,26 +281,26 @@ const (
infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive"
)
-/*
- * If EnableInfinityTs is not called, "-infinity" and "infinity" will return
- * []byte("-infinity") and []byte("infinity") respectively, and potentially
- * cause error "sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time",
- * when scanning into a time.Time value.
- *
- * Once EnableInfinityTs has been called, all connections created using this
- * driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
- * "timestamp with time zone" and "date" types to the predefined minimum and
- * maximum times, respectively. When encoding time.Time values, any time which
- * equals or precedes the predefined minimum time will be encoded to
- * "-infinity". Any values at or past the maximum time will similarly be
- * encoded to "infinity".
- *
- *
- * If EnableInfinityTs is called with negative >= positive, it will panic.
- * Calling EnableInfinityTs after a connection has been established results in
- * undefined behavior. If EnableInfinityTs is called more than once, it will
- * panic.
- */
+// EnableInfinityTs controls the handling of Postgres' "-infinity" and
+// "infinity" "timestamp"s.
+//
+// If EnableInfinityTs is not called, "-infinity" and "infinity" will return
+// []byte("-infinity") and []byte("infinity") respectively, and potentially
+// cause error "sql: Scan error on column index 0: unsupported driver -> Scan
+// pair: []uint8 -> *time.Time", when scanning into a time.Time value.
+//
+// Once EnableInfinityTs has been called, all connections created using this
+// driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
+// "timestamp with time zone" and "date" types to the predefined minimum and
+// maximum times, respectively. When encoding time.Time values, any time which
+// equals or precedes the predefined minimum time will be encoded to
+// "-infinity". Any values at or past the maximum time will similarly be
+// encoded to "infinity".
+//
+// If EnableInfinityTs is called with negative >= positive, it will panic.
+// Calling EnableInfinityTs after a connection has been established results in
+// undefined behavior. If EnableInfinityTs is called more than once, it will
+// panic.
func EnableInfinityTs(negative time.Time, positive time.Time) {
if infinityTsEnabled {
panic(infinityTsEnabledAlready)
@@ -335,6 +344,10 @@ func parseTs(currentLocation *time.Location, str string) interface{} {
return t
}
+// ParseTimestamp parses Postgres' text format. It returns a time.Time in
+// currentLocation iff that time's offset agrees with the offset sent from the
+// Postgres server. Otherwise, ParseTimestamp returns a time.Time with the
+// fixed offset offset provided by the Postgres server.
func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, error) {
p := timestampParser{}
@@ -344,18 +357,18 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
year := p.mustAtoi(str, 0, monSep)
daySep := monSep + 3
month := p.mustAtoi(str, monSep+1, daySep)
- p.expect(str, "-", daySep)
+ p.expect(str, '-', daySep)
timeSep := daySep + 3
day := p.mustAtoi(str, daySep+1, timeSep)
var hour, minute, second int
if len(str) > monSep+len("01-01")+1 {
- p.expect(str, " ", timeSep)
+ p.expect(str, ' ', timeSep)
minSep := timeSep + 3
- p.expect(str, ":", minSep)
+ p.expect(str, ':', minSep)
hour = p.mustAtoi(str, timeSep+1, minSep)
secSep := minSep + 3
- p.expect(str, ":", secSep)
+ p.expect(str, ':', secSep)
minute = p.mustAtoi(str, minSep+1, secSep)
secEnd := secSep + 3
second = p.mustAtoi(str, secSep+1, secEnd)
@@ -369,7 +382,7 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
nanoSec := 0
tzOff := 0
- if remainderIdx+1 <= len(str) && str[remainderIdx:remainderIdx+1] == "." {
+ if remainderIdx < len(str) && str[remainderIdx] == '.' {
fracStart := remainderIdx + 1
fracOff := strings.IndexAny(str[fracStart:], "-+ ")
if fracOff < 0 {
@@ -380,25 +393,26 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
remainderIdx += fracOff + 1
}
- if tzStart := remainderIdx; tzStart+1 <= len(str) && (str[tzStart:tzStart+1] == "-" || str[tzStart:tzStart+1] == "+") {
+ if tzStart := remainderIdx; tzStart < len(str) && (str[tzStart] == '-' || str[tzStart] == '+') {
// time zone separator is always '-' or '+' (UTC is +00)
var tzSign int
- if c := str[tzStart : tzStart+1]; c == "-" {
+ switch c := str[tzStart]; c {
+ case '-':
tzSign = -1
- } else if c == "+" {
+ case '+':
tzSign = +1
- } else {
+ default:
return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c)
}
tzHours := p.mustAtoi(str, tzStart+1, tzStart+3)
remainderIdx += 3
var tzMin, tzSec int
- if tzStart+4 <= len(str) && str[tzStart+3:tzStart+4] == ":" {
- tzMin = p.mustAtoi(str, tzStart+4, tzStart+6)
+ if remainderIdx < len(str) && str[remainderIdx] == ':' {
+ tzMin = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
- if tzStart+7 <= len(str) && str[tzStart+6:tzStart+7] == ":" {
- tzSec = p.mustAtoi(str, tzStart+7, tzStart+9)
+ if remainderIdx < len(str) && str[remainderIdx] == ':' {
+ tzSec = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec)
@@ -432,7 +446,7 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
}
// formatTs formats t into a format postgres understands.
-func formatTs(t time.Time) (b []byte) {
+func formatTs(t time.Time) []byte {
if infinityTsEnabled {
// t <= -infinity : ! (t > -infinity)
if !t.After(infinityTsNegative) {
@@ -443,6 +457,11 @@ func formatTs(t time.Time) (b []byte) {
return []byte("infinity")
}
}
+ return FormatTimestamp(t)
+}
+
+// FormatTimestamp formats t into Postgres' text format for timestamps.
+func FormatTimestamp(t time.Time) []byte {
// Need to send dates before 0001 A.D. with " BC" suffix, instead of the
// minus sign preferred by Go.
// Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on
@@ -452,7 +471,7 @@ func formatTs(t time.Time) (b []byte) {
t = t.AddDate((-t.Year())*2+1, 0, 0)
bc = true
}
- b = []byte(t.Format(time.RFC3339Nano))
+ b := []byte(t.Format(time.RFC3339Nano))
_, offset := t.Zone()
offset = offset % 60
@@ -477,14 +496,14 @@ func formatTs(t time.Time) (b []byte) {
// Parse a bytea value received from the server. Both "hex" and the legacy
// "escape" format are supported.
-func parseBytea(s []byte) (result []byte) {
+func parseBytea(s []byte) (result []byte, err error) {
if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) {
// bytea_output = hex
s = s[2:] // trim off leading "\\x"
result = make([]byte, hex.DecodedLen(len(s)))
_, err := hex.Decode(result, s)
if err != nil {
- errorf("%s", err)
+ return nil, err
}
} else {
// bytea_output = escape
@@ -499,11 +518,11 @@ func parseBytea(s []byte) (result []byte) {
// '\\' followed by an octal number
if len(s) < 4 {
- errorf("invalid bytea sequence %v", s)
+ return nil, fmt.Errorf("invalid bytea sequence %v", s)
}
r, err := strconv.ParseInt(string(s[1:4]), 8, 9)
if err != nil {
- errorf("could not parse bytea value: %s", err.Error())
+ return nil, fmt.Errorf("could not parse bytea value: %s", err.Error())
}
result = append(result, byte(r))
s = s[4:]
@@ -521,7 +540,7 @@ func parseBytea(s []byte) (result []byte) {
}
}
- return result
+ return result, nil
}
func encodeBytea(serverVersion int, v []byte) (result []byte) {
diff --git a/vendor/github.com/lib/pq/encode_test.go b/vendor/github.com/lib/pq/encode_test.go
index 984abbc94..1e89f7f6f 100644
--- a/vendor/github.com/lib/pq/encode_test.go
+++ b/vendor/github.com/lib/pq/encode_test.go
@@ -575,6 +575,17 @@ func TestBinaryByteSliceToInt(t *testing.T) {
}
}
+func TestTextDecodeIntoString(t *testing.T) {
+ input := []byte("hello world")
+ want := string(input)
+ for _, typ := range []oid.Oid{oid.T_char, oid.T_varchar, oid.T_text} {
+ got := decode(&parameterStatus{}, input, typ, formatText)
+ if got != want {
+ t.Errorf("invalid string decoding output for %T(%+v), got %v but expected %v", typ, typ, got, want)
+ }
+ }
+}
+
func TestByteaOutputFormatEncoding(t *testing.T) {
input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
want := []byte("\\x5c78000102fffe6162636465666730313233")
diff --git a/vendor/github.com/lib/pq/ssl_test.go b/vendor/github.com/lib/pq/ssl_test.go
index 932b336f5..f70a5fd57 100644
--- a/vendor/github.com/lib/pq/ssl_test.go
+++ b/vendor/github.com/lib/pq/ssl_test.go
@@ -100,6 +100,49 @@ func TestSSLVerifyFull(t *testing.T) {
}
}
+// Test sslmode=require sslrootcert=rootCertPath
+func TestSSLRequireWithRootCert(t *testing.T) {
+ maybeSkipSSLTests(t)
+ // Environment sanity check: should fail without SSL
+ checkSSLSetup(t, "sslmode=disable user=pqgossltest")
+
+ bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
+ bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
+
+ // Not OK according to the bogus CA
+ _, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ _, ok := err.(x509.UnknownAuthorityError)
+ if !ok {
+ t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
+ }
+
+ nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
+ nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
+
+ // No match on Common Name, but that's OK because we're not validating anything.
+ _, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
+ rootCert := "sslrootcert=" + rootCertPath + " "
+
+ // No match on Common Name, but that's OK because we're not validating the CN.
+ _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Everything OK
+ _, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
// Test sslmode=verify-ca
func TestSSLVerifyCA(t *testing.T) {
maybeSkipSSLTests(t)
diff --git a/vendor/github.com/mssola/user_agent/.travis.yml b/vendor/github.com/mssola/user_agent/.travis.yml
index 33c596acb..add0c8a6c 100644
--- a/vendor/github.com/mssola/user_agent/.travis.yml
+++ b/vendor/github.com/mssola/user_agent/.travis.yml
@@ -4,8 +4,6 @@ go:
- 1.1
- 1.2
- 1.3
- - 1.4
- - 1.5
- tip
matrix:
allow_failures:
diff --git a/vendor/github.com/mssola/user_agent/LICENSE b/vendor/github.com/mssola/user_agent/LICENSE
index 2784cdcac..c02b64473 100644
--- a/vendor/github.com/mssola/user_agent/LICENSE
+++ b/vendor/github.com/mssola/user_agent/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2012-2016 Miquel Sabaté Solà
+Copyright (c) 2012-2014 Miquel Sabaté Solà
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/vendor/github.com/mssola/user_agent/README.md b/vendor/github.com/mssola/user_agent/README.md
index 5c5e77276..971902560 100644
--- a/vendor/github.com/mssola/user_agent/README.md
+++ b/vendor/github.com/mssola/user_agent/README.md
@@ -48,4 +48,4 @@ func main() {
}
~~~
-Copyright &copy; 2012-2016 Miquel Sabaté Solà, released under the MIT License.
+Copyright &copy; 2012-2014 Miquel Sabaté Solà, released under the MIT License.
diff --git a/vendor/github.com/mssola/user_agent/all_test.go b/vendor/github.com/mssola/user_agent/all_test.go
index f76f2882f..4f3c03198 100644
--- a/vendor/github.com/mssola/user_agent/all_test.go
+++ b/vendor/github.com/mssola/user_agent/all_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// Copyright (C) 2012-2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.
@@ -10,457 +10,185 @@ import (
)
// Slice that contains all the tests. Each test is contained in a struct
-// that groups the title of the test, the User-Agent string to be tested and the expected value.
+// that groups the name of the test and the User-Agent string to be tested.
var uastrings = []struct {
- title string
- ua string
- expected string
+ name string
+ ua string
}{
// Bots
- {
- title: "GoogleBot",
- ua: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
- expected: "Mozilla:5.0 Browser:Googlebot-2.1 Bot:true Mobile:false",
- },
- {
- title: "GoogleBotSmartphone",
- ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
- expected: "Mozilla:5.0 Browser:Googlebot-2.1 Bot:true Mobile:true",
- },
- {
- title: "BingBot",
- ua: "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)",
- expected: "Mozilla:5.0 Browser:bingbot-2.0 Bot:true Mobile:false",
- },
- {
- title: "BaiduBot",
- ua: "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)",
- expected: "Mozilla:5.0 Browser:Baiduspider-2.0 Bot:true Mobile:false",
- },
- {
- title: "Twitterbot",
- ua: "Twitterbot",
- expected: "Browser:Twitterbot Bot:true Mobile:false",
- },
- {
- title: "YahooBot",
- ua: "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)",
- expected: "Mozilla:5.0 Browser:Yahoo! Slurp Bot:true Mobile:false",
- },
- {
- title: "FacebookExternalHit",
- ua: "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)",
- expected: "Browser:facebookexternalhit-1.1 Bot:true Mobile:false",
- },
- {
- title: "FacebookPlatform",
- ua: "facebookplatform/1.0 (+http://developers.facebook.com)",
- expected: "Browser:facebookplatform-1.0 Bot:true Mobile:false",
- },
- {
- title: "FaceBot",
- ua: "Facebot",
- expected: "Browser:Facebot Bot:true Mobile:false",
- },
- {
- title: "NutchCVS",
- ua: "NutchCVS/0.8-dev (Nutch; http://lucene.apache.org/nutch/bot.html; nutch-agent@lucene.apache.org)",
- expected: "Browser:NutchCVS Bot:true Mobile:false",
- },
- {
- title: "MJ12bot",
- ua: "Mozilla/5.0 (compatible; MJ12bot/v1.2.4; http://www.majestic12.co.uk/bot.php?+)",
- expected: "Mozilla:5.0 Browser:MJ12bot-v1.2.4 Bot:true Mobile:false",
- },
- {
- title: "MJ12bot",
- ua: "MJ12bot/v1.0.8 (http://majestic12.co.uk/bot.php?+)",
- expected: "Browser:MJ12bot Bot:true Mobile:false",
- },
- {
- title: "AhrefsBot",
- ua: "Mozilla/5.0 (compatible; AhrefsBot/4.0; +http://ahrefs.com/robot/)",
- expected: "Mozilla:5.0 Browser:AhrefsBot-4.0 Bot:true Mobile:false",
- },
+ {"GoogleBot", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"},
+ {"GoogleBotSmartphone", "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"},
+ {"BingBot", "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"},
+ {"BaiduBot", "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"},
+ {"Twitterbot", "Twitterbot"},
+ {"YahooBot", "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)"},
+ {"FacebookExternalHit", "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)"},
+ {"FacebookPlatform", "facebookplatform/1.0 (+http://developers.facebook.com)"},
+ {"FaceBot", "Facebot"},
// Internet Explorer
- {
- title: "IE10",
- ua: "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "Tablet",
- ua: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; ARM; Trident/6.0; Touch; .NET4.0E; .NET4.0C; Tablet PC 2.0)",
- expected: "Mozilla:4.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "Touch",
- ua: "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0; Touch)",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "Phone",
- ua: "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; SAMSUNG; SGH-i917)",
- expected: "Mozilla:4.0 Platform:Windows OS:Windows Phone OS 7.0 Browser:Internet Explorer-7.0 Engine:Trident Bot:false Mobile:true",
- },
- {
- title: "IE6",
- ua: "Mozilla/4.0 (compatible; MSIE6.0; Windows NT 5.0; .NET CLR 1.1.4322)",
- expected: "Mozilla:4.0 Platform:Windows OS:Windows 2000 Browser:Internet Explorer-6.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "IE8Compatibility",
- ua: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3; MS-RTC LM 8)",
- expected: "Mozilla:4.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-8.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "IE10Compatibility",
- ua: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3; MS-RTC LM 8)",
- expected: "Mozilla:4.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "IE11Win81",
- ua: "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 8.1 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "IE11Win7",
- ua: "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "IE11b32Win7b64",
- ua: "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "IE11b32Win7b64MDDRJS",
- ua: "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; MDDRJS; rv:11.0) like Gecko",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
- },
- {
- title: "IE11Compatibility",
- ua: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0)",
- expected: "Mozilla:4.0 Platform:Windows OS:Windows 8.1 Browser:Internet Explorer-7.0 Engine:Trident Bot:false Mobile:false",
- },
-
- // Microsoft Edge
- {
- title: "EdgeDesktop",
- ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 10 Browser:Edge-12.10240 Engine:EdgeHTML Bot:false Mobile:false",
- },
- {
- title: "EdgeMobile",
- ua: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; DEVICE INFO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Edge/12.10240",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows Phone 10.0 Browser:Edge-12.10240 Engine:EdgeHTML Bot:false Mobile:true",
- },
+ {"IE10", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"},
+ {"Tablet", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; ARM; Trident/6.0; Touch; .NET4.0E; .NET4.0C; Tablet PC 2.0)"},
+ {"Touch", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0; Touch)"},
+ {"Phone", "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; SAMSUNG; SGH-i917)"},
+ {"IE6", "Mozilla/4.0 (compatible; MSIE6.0; Windows NT 5.0; .NET CLR 1.1.4322)"},
+ {"IE8Compatibility", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3; MS-RTC LM 8)"},
+ {"IE10Compatibility", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3; MS-RTC LM 8)"},
+ {"IE11Win81", "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko"},
+ {"IE11Win7", "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko"},
+ {"IE11b32Win7b64", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"},
+ {"IE11b32Win7b64MDDRJS", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; MDDRJS; rv:11.0) like Gecko"},
+ {"IE11Compatibility", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0)"},
// Gecko
- {
- title: "FirefoxMac",
- ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b8) Gecko/20100101 Firefox/4.0b8",
- expected: "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Browser:Firefox-4.0b8 Engine:Gecko-20100101 Bot:false Mobile:false",
- },
- {
- title: "FirefoxMacLoc",
- ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13",
- expected: "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Localization:en-US Browser:Firefox-3.6.13 Engine:Gecko-20101203 Bot:false Mobile:false",
- },
- {
- title: "FirefoxLinux",
- ua: "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0",
- expected: "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Firefox-17.0 Engine:Gecko-20100101 Bot:false Mobile:false",
- },
- {
- title: "FirefoxWin",
- ua: "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en-US Browser:Firefox-2.0.0.14 Engine:Gecko-20080404 Bot:false Mobile:false",
- },
- {
- title: "Firefox29Win7",
- ua: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Firefox-29.0 Engine:Gecko-20100101 Bot:false Mobile:false",
- },
- {
- title: "CaminoMac",
- ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.14) Gecko/20080409 Camino/1.6 (like Firefox/2.0.0.14)",
- expected: "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X Localization:en Browser:Camino-1.6 Engine:Gecko-20080409 Bot:false Mobile:false",
- },
- {
- title: "Iceweasel",
- ua: "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)",
- expected: "Mozilla:5.0 Platform:X11 OS:Linux i686 Localization:en-US Browser:Iceweasel-2.0 Engine:Gecko-20061024 Bot:false Mobile:false",
- },
- {
- title: "SeaMonkey",
- ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.4) Gecko/20091017 SeaMonkey/2.0",
- expected: "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Localization:en-US Browser:SeaMonkey-2.0 Engine:Gecko-20091017 Bot:false Mobile:false",
- },
- {
- title: "AndroidFirefox",
- ua: "Mozilla/5.0 (Android; Mobile; rv:17.0) Gecko/17.0 Firefox/17.0",
- expected: "Mozilla:5.0 Platform:Mobile OS:Android Browser:Firefox-17.0 Engine:Gecko-17.0 Bot:false Mobile:true",
- },
- {
- title: "AndroidFirefoxTablet",
- ua: "Mozilla/5.0 (Android; Tablet; rv:26.0) Gecko/26.0 Firefox/26.0",
- expected: "Mozilla:5.0 Platform:Tablet OS:Android Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
- },
- {
- title: "FirefoxOS",
- ua: "Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0",
- expected: "Mozilla:5.0 Platform:Mobile OS:FirefoxOS Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
- },
- {
- title: "FirefoxOSTablet",
- ua: "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0",
- expected: "Mozilla:5.0 Platform:Tablet OS:FirefoxOS Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
- },
- {
- title: "FirefoxWinXP",
- ua: "Mozilla/5.0 (Windows NT 5.2; rv:31.0) Gecko/20100101 Firefox/31.0",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows XP x64 Edition Browser:Firefox-31.0 Engine:Gecko-20100101 Bot:false Mobile:false",
- },
- {
- title: "FirefoxMRA",
- ua: "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:24.0) Gecko/20130405 MRA 5.5 (build 02842) Firefox/24.0 (.NET CLR 3.5.30729)",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en-US Browser:Firefox-24.0 Engine:Gecko-20130405 Bot:false Mobile:false",
- },
+ {"FirefoxMac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b8) Gecko/20100101 Firefox/4.0b8"},
+ {"FirefoxMacLoc", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"},
+ {"FirefoxLinux", "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0"},
+ {"FirefoxWin", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14"},
+ {"Firefox29Win7", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0"},
+ {"CaminoMac", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.14) Gecko/20080409 Camino/1.6 (like Firefox/2.0.0.14)"},
+ {"Iceweasel", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)"},
+ {"SeaMonkey", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.4) Gecko/20091017 SeaMonkey/2.0"},
+ {"AndroidFirefox", "Mozilla/5.0 (Android; Mobile; rv:17.0) Gecko/17.0 Firefox/17.0"},
+ {"AndroidFirefoxTablet", "Mozilla/5.0 (Android; Tablet; rv:26.0) Gecko/26.0 Firefox/26.0"},
+ {"FirefoxOS", "Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0"},
+ {"FirefoxOSTablet", "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0"},
+ {"FirefoxWinXP", "Mozilla/5.0 (Windows NT 5.2; rv:31.0) Gecko/20100101 Firefox/31.0"},
+ {"FirefoxMRA", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:24.0) Gecko/20130405 MRA 5.5 (build 02842) Firefox/24.0 (.NET CLR 3.5.30729)"},
// Opera
- {
- title: "OperaMac",
- ua: "Opera/9.27 (Macintosh; Intel Mac OS X; U; en)",
- expected: "Platform:Macintosh OS:Intel Mac OS X Localization:en Browser:Opera-9.27 Engine:Presto Bot:false Mobile:false",
- },
- {
- title: "OperaWin",
- ua: "Opera/9.27 (Windows NT 5.1; U; en)",
- expected: "Platform:Windows OS:Windows XP Localization:en Browser:Opera-9.27 Engine:Presto Bot:false Mobile:false",
- },
- {
- title: "OperaWinNoLocale",
- ua: "Opera/9.80 (Windows NT 5.1) Presto/2.12.388 Version/12.10",
- expected: "Platform:Windows OS:Windows XP Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
- },
- {
- title: "OperaWin2Comment",
- ua: "Opera/9.80 (Windows NT 6.0; WOW64) Presto/2.12.388 Version/12.15",
- expected: "Platform:Windows OS:Windows Vista Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
- },
- {
- title: "OperaMinimal",
- ua: "Opera/9.80",
- expected: "Browser:Opera-9.80 Engine:Presto Bot:false Mobile:false",
- },
- {
- title: "OperaFull",
- ua: "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.10",
- expected: "Platform:Windows OS:Windows Vista Localization:en Browser:Opera-9.80 Engine:Presto-2.2.15 Bot:false Mobile:false",
- },
- {
- title: "OperaLinux",
- ua: "Opera/9.80 (X11; Linux x86_64) Presto/2.12.388 Version/12.10",
- expected: "Platform:X11 OS:Linux x86_64 Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
- },
- {
- title: "OperaAndroid",
- ua: "Opera/9.80 (Android 4.2.1; Linux; Opera Mobi/ADR-1212030829) Presto/2.11.355 Version/12.10",
- expected: "Platform:Android 4.2.1 OS:Linux Browser:Opera-9.80 Engine:Presto-2.11.355 Bot:false Mobile:true",
- },
- {
- title: "OperaNested",
- ua: "Opera/9.80 (Windows NT 5.1; MRA 6.0 (build 5831)) Presto/2.12.388 Version/12.10",
- expected: "Platform:Windows OS:Windows XP Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
- },
- {
- title: "OperaMRA",
- ua: "Opera/9.80 (Windows NT 6.1; U; MRA 5.8 (build 4139); en) Presto/2.9.168 Version/11.50",
- expected: "Platform:Windows OS:Windows 7 Localization:en Browser:Opera-9.80 Engine:Presto-2.9.168 Bot:false Mobile:false",
- },
+ {"OperaMac", "Opera/9.27 (Macintosh; Intel Mac OS X; U; en)"},
+ {"OperaWin", "Opera/9.27 (Windows NT 5.1; U; en)"},
+ {"OperaWinNoLocale", "Opera/9.80 (Windows NT 5.1) Presto/2.12.388 Version/12.10"},
+ {"OperaWin2Comment", "Opera/9.80 (Windows NT 6.0; WOW64) Presto/2.12.388 Version/12.15"},
+ {"OperaMinimal", "Opera/9.80"},
+ {"OperaFull", "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.10"},
+ {"OperaLinux", "Opera/9.80 (X11; Linux x86_64) Presto/2.12.388 Version/12.10"},
+ {"OperaAndroid", "Opera/9.80 (Android 4.2.1; Linux; Opera Mobi/ADR-1212030829) Presto/2.11.355 Version/12.10"},
+ {"OperaNested", "Opera/9.80 (Windows NT 5.1; MRA 6.0 (build 5831)) Presto/2.12.388 Version/12.10"},
+ {"OperaMRA", "Opera/9.80 (Windows NT 6.1; U; MRA 5.8 (build 4139); en) Presto/2.9.168 Version/11.50"},
// Other
- {
- title: "Empty",
- ua: "",
- expected: "Bot:false Mobile:false",
- },
- {
- title: "Nil",
- ua: "nil",
- expected: "Browser:nil Bot:false Mobile:false",
- },
- {
- title: "Compatible",
- ua: "Mozilla/4.0 (compatible)",
- expected: "Browser:Mozilla-4.0 Bot:false Mobile:false",
- },
- {
- title: "Mozilla",
- ua: "Mozilla/5.0",
- expected: "Browser:Mozilla-5.0 Bot:false Mobile:false",
- },
- {
- title: "Amaya",
- ua: "amaya/9.51 libwww/5.4.0",
- expected: "Browser:amaya-9.51 Engine:libwww-5.4.0 Bot:false Mobile:false",
- },
- {
- title: "Rails",
- ua: "Rails Testing",
- expected: "Browser:Rails Engine:Testing Bot:false Mobile:false",
- },
- {
- title: "Python",
- ua: "Python-urllib/2.7",
- expected: "Browser:Python-urllib-2.7 Bot:false Mobile:false",
- },
- {
- title: "Curl",
- ua: "curl/7.28.1",
- expected: "Browser:curl-7.28.1 Bot:false Mobile:false",
- },
+ {"Empty", ""},
+ {"Nil", "nil"},
+ {"Compatible", "Mozilla/4.0 (compatible)"},
+ {"Mozilla", "Mozilla/5.0"},
+ {"Amaya", "amaya/9.51 libwww/5.4.0"},
+ {"Rails", "Rails Testing"},
+ {"Python", "Python-urllib/2.7"},
+ {"Curl", "curl/7.28.1"},
// WebKit
- {
- title: "ChromeLinux",
- ua: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11",
- expected: "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Chrome-23.0.1271.97 Engine:AppleWebKit-537.11 Bot:false Mobile:false",
- },
- {
- title: "ChromeWin7",
- ua: "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Chrome-18.0.1025.168 Engine:AppleWebKit-535.19 Bot:false Mobile:false",
- },
- {
- title: "ChromeMinimal",
- ua: "Mozilla/5.0 AppleWebKit/534.10 Chrome/8.0.552.215 Safari/534.10",
- expected: "Mozilla:5.0 Browser:Chrome-8.0.552.215 Engine:AppleWebKit-534.10 Bot:false Mobile:false",
- },
- {
- title: "ChromeMac",
- ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.231 Safari/534.10",
- expected: "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10_6_5 Localization:en-US Browser:Chrome-8.0.552.231 Engine:AppleWebKit-534.10 Bot:false Mobile:false",
- },
- {
- title: "SafariMac",
- ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16",
- expected: "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10_6_3 Localization:en-us Browser:Safari-5.0 Engine:AppleWebKit-533.16 Bot:false Mobile:false",
- },
- {
- title: "SafariWin",
- ua: "Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8",
- expected: "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en Browser:Safari-4.0dp1 Engine:AppleWebKit-526.9 Bot:false Mobile:false",
- },
- {
- title: "iPhone7",
- ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_3 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B511 Safari/9537.53",
- expected: "Mozilla:5.0 Platform:iPhone OS:CPU iPhone OS 7_0_3 like Mac OS X Browser:Safari-7.0 Engine:AppleWebKit-537.51.1 Bot:false Mobile:true",
- },
- {
- title: "iPhone",
- ua: "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419",
- expected: "Mozilla:5.0 Platform:iPhone OS:CPU like Mac OS X Localization:en Browser:Safari-3.0 Engine:AppleWebKit-420.1 Bot:false Mobile:true",
- },
- {
- title: "iPod",
- ua: "Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419",
- expected: "Mozilla:5.0 Platform:iPod OS:CPU like Mac OS X Localization:en Browser:Safari-3.0 Engine:AppleWebKit-420.1 Bot:false Mobile:true",
- },
- {
- title: "iPad",
- ua: "Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B367 Safari/531.21.10",
- expected: "Mozilla:5.0 Platform:iPad OS:CPU OS 3_2 like Mac OS X Localization:en-us Browser:Safari-4.0.4 Engine:AppleWebKit-531.21.10 Bot:false Mobile:true",
- },
- {
- title: "webOS",
- ua: "Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.1",
- expected: "Mozilla:5.0 Platform:webOS OS:Palm Localization:en-US Browser:webOS-1.0 Engine:AppleWebKit-532.2 Bot:false Mobile:true",
- },
- {
- title: "Android",
- ua: "Mozilla/5.0 (Linux; U; Android 1.5; de-; HTC Magic Build/PLAT-RC33) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 1.5 Localization:de- Browser:Android-3.1.2 Engine:AppleWebKit-528.5+ Bot:false Mobile:true",
- },
- {
- title: "BlackBerry",
- ua: "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, Like Gecko) Version/6.0.0.141 Mobile Safari/534.1+",
- expected: "Mozilla:5.0 Platform:BlackBerry OS:BlackBerry 9800 Localization:en Browser:BlackBerry-6.0.0.141 Engine:AppleWebKit-534.1+ Bot:false Mobile:true",
- },
- {
- title: "BB10",
- ua: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.388 Mobile Safari/537.3+",
- expected: "Mozilla:5.0 Platform:BlackBerry OS:BlackBerry Browser:BlackBerry-10.0.9.388 Engine:AppleWebKit-537.3+ Bot:false Mobile:true",
- },
- {
- title: "Ericsson",
- ua: "Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 Safari/525",
- expected: "Mozilla:5.0 Platform:Symbian OS:SymbianOS/9.4 Browser:Symbian-3.0 Engine:AppleWebKit-525 Bot:false Mobile:true",
- },
- {
- title: "ChromeAndroid",
- ua: "Mozilla/5.0 (Linux; Android 4.2.1; Galaxy Nexus Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 4.2.1 Browser:Chrome-18.0.1025.166 Engine:AppleWebKit-535.19 Bot:false Mobile:true",
- },
- {
- title: "WebkitNoPlatform",
- ua: "Mozilla/5.0 (en-us) AppleWebKit/525.13 (KHTML, like Gecko; Google Web Preview) Version/3.1 Safari/525.13",
- expected: "Mozilla:5.0 Platform:en-us Localization:en-us Browser:Safari-3.1 Engine:AppleWebKit-525.13 Bot:false Mobile:false",
- },
- {
- title: "OperaWebkitMobile",
- ua: "Mozilla/5.0 (Linux; Android 4.2.2; Galaxy Nexus Build/JDQ39) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.58 Mobile Safari/537.31 OPR/14.0.1074.57453",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 4.2.2 Browser:Opera-14.0.1074.57453 Engine:AppleWebKit-537.31 Bot:false Mobile:true",
- },
- {
- title: "OperaWebkitDesktop",
- ua: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.58 Safari/537.31 OPR/14.0.1074.57453",
- expected: "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Opera-14.0.1074.57453 Engine:AppleWebKit-537.31 Bot:false Mobile:false",
- },
- {
- title: "ChromeNothingAfterU",
- ua: "Mozilla/5.0 (Linux; U) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4",
- expected: "Mozilla:5.0 Platform:Linux OS:Linux Browser:Chrome-22.0.1229.79 Engine:AppleWebKit-537.4 Bot:false Mobile:false",
- },
- {
- title: "SafariOnSymbian",
- ua: "Mozilla/5.0 (SymbianOS/9.1; U; [en-us]) AppleWebKit/413 (KHTML, like Gecko) Safari/413",
- expected: "Mozilla:5.0 Platform:Symbian OS:SymbianOS/9.1 Browser:Symbian-413 Engine:AppleWebKit-413 Bot:false Mobile:true",
- },
+ {"ChromeLinux", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11"},
+ {"ChromeWin7", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19"},
+ {"ChromeMinimal", "Mozilla/5.0 AppleWebKit/534.10 Chrome/8.0.552.215 Safari/534.10"},
+ {"ChromeMac", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.231 Safari/534.10"},
+ {"SafariMac", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16"},
+ {"SafariWin", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8"},
+ {"iPhone7", "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_3 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B511 Safari/9537.53"},
+ {"iPhone", "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419"},
+ {"iPod", "Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419"},
+ {"iPad", "Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B367 Safari/531.21.10"},
+ {"webOS", "Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.1"},
+ {"Android", "Mozilla/5.0 (Linux; U; Android 1.5; de-; HTC Magic Build/PLAT-RC33) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1"},
+ {"BlackBerry", "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, Like Gecko) Version/6.0.0.141 Mobile Safari/534.1+"},
+ {"BB10", "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.388 Mobile Safari/537.3+"},
+ {"Ericsson", "Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 Safari/525"},
+ {"ChromeAndroid", "Mozilla/5.0 (Linux; Android 4.2.1; Galaxy Nexus Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19"},
+ {"WebkitNoPlatform", "Mozilla/5.0 (en-us) AppleWebKit/525.13 (KHTML, like Gecko; Google Web Preview) Version/3.1 Safari/525.13"},
+ {"OperaWebkitMobile", "Mozilla/5.0 (Linux; Android 4.2.2; Galaxy Nexus Build/JDQ39) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.58 Mobile Safari/537.31 OPR/14.0.1074.57453"},
+ {"OperaWebkitDesktop", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.58 Safari/537.31 OPR/14.0.1074.57453"},
+ {"ChromeNothingAfterU", "Mozilla/5.0 (Linux; U) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4"},
+ {"SafariOnSymbian", "Mozilla/5.0 (SymbianOS/9.1; U; [en-us]) AppleWebKit/413 (KHTML, like Gecko) Safari/413"},
+}
+
+// Slice of the expected results from the previous slice.
+var expected = []string{
+ // Bots
+ "Mozilla:5.0 Browser:Googlebot-2.1 Bot:true Mobile:false",
+ "Mozilla:5.0 Browser:Googlebot-2.1 Bot:true Mobile:true",
+ "Mozilla:5.0 Browser:bingbot-2.0 Bot:true Mobile:false",
+ "Mozilla:5.0 Browser:Baiduspider-2.0 Bot:true Mobile:false",
+ "Browser:Twitterbot Bot:true Mobile:false",
+ "Mozilla:5.0 Browser:Yahoo! Slurp Bot:true Mobile:false",
+ "Browser:facebookexternalhit-1.1 Bot:true Mobile:false",
+ "Browser:facebookplatform-1.0 Bot:true Mobile:false",
+ "Browser:Facebot Bot:true Mobile:false",
- // Dalvik
- {
- title: "Dalvik - Dell:001DL",
- ua: "Dalvik/1.2.0 (Linux; U; Android 2.2.2; 001DL Build/FRG83G)",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 2.2.2 Bot:false Mobile:true",
- },
- {
- title: "Dalvik - HTC:001HT",
- ua: "Dalvik/1.4.0 (Linux; U; Android 2.3.3; 001HT Build/GRI40)",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 2.3.3 Bot:false Mobile:true",
- },
- {
- title: "Dalvik - ZTE:009Z",
- ua: "Dalvik/1.4.0 (Linux; U; Android 2.3.4; 009Z Build/GINGERBREAD)",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 2.3.4 Bot:false Mobile:true",
- },
- {
- title: "Dalvik - A850",
- ua: "Dalvik/1.6.0 (Linux; U; Android 4.2.2; A850 Build/JDQ39) Configuration/CLDC-1.1; Opera Mini/att/4.2",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 4.2.2 Bot:false Mobile:true",
- },
- {
- title: "Dalvik - Asus:T00Q",
- ua: "Dalvik/1.6.0 (Linux; U; Android 4.4.2; ASUS_T00Q Build/KVT49L)/CLDC-1.1",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 4.4.2 Bot:false Mobile:true",
- },
- {
- title: "Dalvik - W2430",
- ua: "Dalvik/1.6.0 (Linux; U; Android 4.0.4; W2430 Build/IMM76D)014; Profile/MIDP-2.1 Configuration/CLDC-1",
- expected: "Mozilla:5.0 Platform:Linux OS:Android 4.0.4 Bot:false Mobile:true",
- },
+ // Internet Explorer
+ "Mozilla:5.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows Phone OS 7.0 Browser:Internet Explorer-7.0 Engine:Trident Bot:false Mobile:true",
+ "Mozilla:4.0 Platform:Windows OS:Windows 2000 Browser:Internet Explorer-6.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-8.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 8.1 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 8.1 Browser:Internet Explorer-7.0 Engine:Trident Bot:false Mobile:false",
+
+ // Gecko
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Browser:Firefox-4.0b8 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Localization:en-US Browser:Firefox-3.6.13 Engine:Gecko-20101203 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Firefox-17.0 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en-US Browser:Firefox-2.0.0.14 Engine:Gecko-20080404 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Firefox-29.0 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X Localization:en Browser:Camino-1.6 Engine:Gecko-20080409 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:X11 OS:Linux i686 Localization:en-US Browser:Iceweasel-2.0 Engine:Gecko-20061024 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Localization:en-US Browser:SeaMonkey-2.0 Engine:Gecko-20091017 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Mobile OS:Android Browser:Firefox-17.0 Engine:Gecko-17.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Tablet OS:Android Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Mobile OS:FirefoxOS Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Tablet OS:FirefoxOS Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP x64 Edition Browser:Firefox-31.0 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en-US Browser:Firefox-24.0 Engine:Gecko-20130405 Bot:false Mobile:false",
+
+ // Opera
+ "Platform:Macintosh OS:Intel Mac OS X Localization:en Browser:Opera-9.27 Engine:Presto Bot:false Mobile:false",
+ "Platform:Windows OS:Windows XP Localization:en Browser:Opera-9.27 Engine:Presto Bot:false Mobile:false",
+ "Platform:Windows OS:Windows XP Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Platform:Windows OS:Windows Vista Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Browser:Opera-9.80 Engine:Presto Bot:false Mobile:false",
+ "Platform:Windows OS:Windows Vista Localization:en Browser:Opera-9.80 Engine:Presto-2.2.15 Bot:false Mobile:false",
+ "Platform:X11 OS:Linux x86_64 Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Platform:Android 4.2.1 OS:Linux Browser:Opera-9.80 Engine:Presto-2.11.355 Bot:false Mobile:true",
+ "Platform:Windows OS:Windows XP Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Platform:Windows OS:Windows 7 Localization:en Browser:Opera-9.80 Engine:Presto-2.9.168 Bot:false Mobile:false",
+
+ // Other
+ "Bot:false Mobile:false",
+ "Browser:nil Bot:false Mobile:false",
+ "Browser:Mozilla-4.0 Bot:false Mobile:false",
+ "Browser:Mozilla-5.0 Bot:false Mobile:false",
+ "Browser:amaya-9.51 Engine:libwww-5.4.0 Bot:false Mobile:false",
+ "Browser:Rails Engine:Testing Bot:false Mobile:false",
+ "Browser:Python-urllib-2.7 Bot:false Mobile:false",
+ "Browser:curl-7.28.1 Bot:false Mobile:false",
+
+ // WebKit
+ "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Chrome-23.0.1271.97 Engine:AppleWebKit-537.11 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Chrome-18.0.1025.168 Engine:AppleWebKit-535.19 Bot:false Mobile:false",
+ "Mozilla:5.0 Browser:Chrome-8.0.552.215 Engine:AppleWebKit-534.10 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10_6_5 Localization:en-US Browser:Chrome-8.0.552.231 Engine:AppleWebKit-534.10 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10_6_3 Localization:en-us Browser:Safari-5.0 Engine:AppleWebKit-533.16 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en Browser:Safari-4.0dp1 Engine:AppleWebKit-526.9 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:iPhone OS:CPU iPhone OS 7_0_3 like Mac OS X Browser:Safari-7.0 Engine:AppleWebKit-537.51.1 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:iPhone OS:CPU like Mac OS X Localization:en Browser:Safari-3.0 Engine:AppleWebKit-420.1 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:iPod OS:CPU like Mac OS X Localization:en Browser:Safari-3.0 Engine:AppleWebKit-420.1 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:iPad OS:CPU OS 3_2 like Mac OS X Localization:en-us Browser:Safari-4.0.4 Engine:AppleWebKit-531.21.10 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:webOS OS:Palm Localization:en-US Browser:webOS-1.0 Engine:AppleWebKit-532.2 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Linux OS:Android 1.5 Localization:de- Browser:Android-3.1.2 Engine:AppleWebKit-528.5+ Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:BlackBerry OS:BlackBerry 9800 Localization:en Browser:BlackBerry-6.0.0.141 Engine:AppleWebKit-534.1+ Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:BlackBerry OS:BlackBerry Browser:BlackBerry-10.0.9.388 Engine:AppleWebKit-537.3+ Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Symbian OS:SymbianOS/9.4 Browser:Symbian-3.0 Engine:AppleWebKit-525 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Linux OS:Android 4.2.1 Browser:Chrome-18.0.1025.166 Engine:AppleWebKit-535.19 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:en-us Localization:en-us Browser:Safari-3.1 Engine:AppleWebKit-525.13 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Linux OS:Android 4.2.2 Browser:Opera-14.0.1074.57453 Engine:AppleWebKit-537.31 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Opera-14.0.1074.57453 Engine:AppleWebKit-537.31 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Linux OS:Linux Browser:Chrome-22.0.1229.79 Engine:AppleWebKit-537.4 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Symbian OS:SymbianOS/9.1 Browser:Symbian-413 Engine:AppleWebKit-413 Bot:false Mobile:true",
}
// Internal: beautify the UserAgent reference into a string so it can be
@@ -507,11 +235,11 @@ func beautify(ua *UserAgent) (s string) {
// The test suite.
func TestUserAgent(t *testing.T) {
- for _, tt := range uastrings {
+ for i, tt := range uastrings {
ua := New(tt.ua)
got := beautify(ua)
- if tt.expected != got {
- t.Errorf("\nTest %v\ngot: %q\nexpected %q\n", tt.title, got, tt.expected)
+ if expected[i] != got {
+ t.Errorf("Test %v => %q, expected %q", tt.name, got, expected[i])
}
}
}
diff --git a/vendor/github.com/mssola/user_agent/bot.go b/vendor/github.com/mssola/user_agent/bot.go
index 2b118d661..efcab9253 100644
--- a/vendor/github.com/mssola/user_agent/bot.go
+++ b/vendor/github.com/mssola/user_agent/bot.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// Copyright (C) 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.
@@ -9,8 +9,6 @@ import (
"strings"
)
-var botFromSiteRegexp = regexp.MustCompile("http://.+\\.\\w+")
-
// Get the name of the bot from the website that may be in the given comment. If
// there is no website in the comment, then an empty string is returned.
func getFromSite(comment []string) string {
@@ -25,7 +23,8 @@ func getFromSite(comment []string) string {
}
// Pick the site.
- results := botFromSiteRegexp.FindStringSubmatch(comment[idx])
+ re := regexp.MustCompile("http://.+\\.\\w+")
+ results := re.FindStringSubmatch(comment[idx])
if len(results) == 1 {
// If it's a simple comment, just return the name of the site.
if idx == 0 {
@@ -75,8 +74,6 @@ func (p *UserAgent) fixOther(sections []section) {
}
}
-var botRegex = regexp.MustCompile("(?i)(bot|crawler|sp(i|y)der|search|worm|fetch|nutch)")
-
// Check if we're dealing with a bot or with some weird browser. If that is the
// case, the receiver will be modified accordingly.
func (p *UserAgent) checkBot(sections []section) {
@@ -85,8 +82,9 @@ func (p *UserAgent) checkBot(sections []section) {
if len(sections) == 1 && sections[0].name != "Mozilla" {
p.mozilla = ""
- // Check whether the name has some suspicious "bot" or "crawler" in his name.
- if botRegex.Match([]byte(sections[0].name)) {
+ // Check whether the name has some suspicious "bot" in his name.
+ reg, _ := regexp.Compile("(?i)bot")
+ if reg.Match([]byte(sections[0].name)) {
p.setSimple(sections[0].name, "", true)
return
}
diff --git a/vendor/github.com/mssola/user_agent/browser.go b/vendor/github.com/mssola/user_agent/browser.go
index 17a243c36..74fb931ef 100644
--- a/vendor/github.com/mssola/user_agent/browser.go
+++ b/vendor/github.com/mssola/user_agent/browser.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// Copyright (C) 2012-2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.
@@ -9,8 +9,6 @@ import (
"strings"
)
-var ie11Regexp = regexp.MustCompile("^rv:(.+)$")
-
// A struct containing all the information that we might be
// interested from the browser.
type Browser struct {
@@ -36,16 +34,13 @@ func (p *UserAgent) detectBrowser(sections []section) {
slen := len(sections)
if sections[0].name == "Opera" {
+ p.mozilla = ""
p.browser.Name = "Opera"
p.browser.Version = sections[0].version
p.browser.Engine = "Presto"
if slen > 1 {
p.browser.EngineVersion = sections[1].version
}
- } else if sections[0].name == "Dalvik" {
- // When Dalvik VM is in use, there is no browser info attached to ua.
- // Although browser is still a Mozilla/5.0 compatible.
- p.mozilla = "5.0"
} else if slen > 1 {
engine := sections[1]
p.browser.Engine = engine.name
@@ -53,21 +48,13 @@ func (p *UserAgent) detectBrowser(sections []section) {
if slen > 2 {
p.browser.Version = sections[2].version
if engine.name == "AppleWebKit" {
- switch sections[slen-1].name {
- case "Edge":
- p.browser.Name = "Edge"
- p.browser.Version = sections[slen-1].version
- p.browser.Engine = "EdgeHTML"
- p.browser.EngineVersion = ""
- case "OPR":
+ if sections[slen-1].name == "OPR" {
p.browser.Name = "Opera"
p.browser.Version = sections[slen-1].version
- default:
- if sections[2].name == "Chrome" {
- p.browser.Name = "Chrome"
- } else {
- p.browser.Name = "Safari"
- }
+ } else if sections[2].name == "Chrome" {
+ p.browser.Name = "Chrome"
+ } else {
+ p.browser.Name = "Safari"
}
} else if engine.name == "Gecko" {
name := sections[2].name
@@ -80,8 +67,9 @@ func (p *UserAgent) detectBrowser(sections []section) {
// This is the new user agent from Internet Explorer 11.
p.browser.Engine = "Trident"
p.browser.Name = "Internet Explorer"
+ reg, _ := regexp.Compile("^rv:(.+)$")
for _, c := range sections[0].comment {
- version := ie11Regexp.FindStringSubmatch(c)
+ version := reg.FindStringSubmatch(c)
if len(version) > 0 {
p.browser.Version = version[1]
return
diff --git a/vendor/github.com/mssola/user_agent/operating_systems.go b/vendor/github.com/mssola/user_agent/operating_systems.go
index c4720a75a..0b1e93d29 100644
--- a/vendor/github.com/mssola/user_agent/operating_systems.go
+++ b/vendor/github.com/mssola/user_agent/operating_systems.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// Copyright (C) 2012-2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.
@@ -7,12 +7,12 @@ package user_agent
import "strings"
// Normalize the name of the operating system. By now, this just
-// affects to Windows NT.
+// affects to Windows.
//
// Returns a string containing the normalized name for the Operating System.
func normalizeOS(name string) string {
sp := strings.SplitN(name, " ", 3)
- if len(sp) != 3 || sp[1] != "NT" {
+ if len(sp) != 3 {
return name
}
@@ -33,7 +33,7 @@ func normalizeOS(name string) string {
return "Windows 8"
case "6.3":
return "Windows 8.1"
- case "10.0":
+ case "6.4":
return "Windows 10"
}
return name
@@ -193,23 +193,6 @@ func opera(p *UserAgent, comment []string) {
}
}
-// Guess the OS. Android browsers send Dalvik as the user agent in the
-// request header.
-//
-// The first argument p is a reference to the current UserAgent and the second
-// argument is a slice of strings containing the comment.
-func dalvik(p *UserAgent, comment []string) {
- slen := len(comment)
-
- if strings.HasPrefix(comment[0], "Linux") {
- p.platform = comment[0]
- if slen > 2 {
- p.os = comment[2]
- }
- p.mobile = true
- }
-}
-
// Given the comment of the first section of the UserAgent string,
// get the platform.
func getPlatform(comment []string) string {
@@ -255,10 +238,6 @@ func (p *UserAgent) detectOS(s section) {
if len(s.comment) > 0 {
opera(p, s.comment)
}
- } else if s.name == "Dalvik" {
- if len(s.comment) > 0 {
- dalvik(p, s.comment)
- }
} else {
// Check whether this is a bot or just a weird browser.
p.undecided = true
diff --git a/vendor/github.com/mssola/user_agent/user_agent.go b/vendor/github.com/mssola/user_agent/user_agent.go
index 18a673eef..74ddf273c 100644
--- a/vendor/github.com/mssola/user_agent/user_agent.go
+++ b/vendor/github.com/mssola/user_agent/user_agent.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// Copyright (C) 2012-2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.
@@ -8,7 +8,9 @@
// information that has been extracted from a parsed User Agent string.
package user_agent
-import "strings"
+import (
+ "strings"
+)
// A section contains the name of the product, its version and
// an optional comment.
@@ -139,9 +141,7 @@ func (p *UserAgent) Parse(ua string) {
}
if len(sections) > 0 {
- if sections[0].name == "Mozilla" {
- p.mozilla = sections[0].version
- }
+ p.mozilla = sections[0].version
p.detectBrowser(sections)
p.detectOS(sections[0])
@@ -167,8 +167,3 @@ func (p *UserAgent) Bot() bool {
func (p *UserAgent) Mobile() bool {
return p.mobile
}
-
-// Returns the original given user agent.
-func (p *UserAgent) UA() string {
- return p.ua
-}
diff --git a/vendor/github.com/pborman/uuid/CONTRIBUTING.md b/vendor/github.com/pborman/uuid/CONTRIBUTING.md
deleted file mode 100644
index 04fdf09f1..000000000
--- a/vendor/github.com/pborman/uuid/CONTRIBUTING.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# How to contribute
-
-We definitely welcome patches and contribution to this project!
-
-### Legal requirements
-
-In order to protect both you and ourselves, you will need to sign the
-[Contributor License Agreement](https://cla.developers.google.com/clas).
-
-You may have already signed it for other Google projects.
diff --git a/vendor/github.com/pborman/uuid/sql.go b/vendor/github.com/pborman/uuid/sql.go
index d015bfd13..c84f900d5 100644
--- a/vendor/github.com/pborman/uuid/sql.go
+++ b/vendor/github.com/pborman/uuid/sql.go
@@ -5,7 +5,6 @@
package uuid
import (
- "database/sql/driver"
"errors"
"fmt"
)
@@ -57,10 +56,3 @@ func (uuid *UUID) Scan(src interface{}) error {
return nil
}
-
-// Value implements sql.Valuer so that UUIDs can be written to databases
-// transparently. Currently, UUIDs map to strings. Please consult
-// database-specific driver documentation for matching types.
-func (uuid UUID) Value() (driver.Value, error) {
- return uuid.String(), nil
-}
diff --git a/vendor/github.com/pborman/uuid/sql_test.go b/vendor/github.com/pborman/uuid/sql_test.go
index 103095156..4d26392af 100644
--- a/vendor/github.com/pborman/uuid/sql_test.go
+++ b/vendor/github.com/pborman/uuid/sql_test.go
@@ -85,12 +85,3 @@ func TestScan(t *testing.T) {
t.Error("UUID was not nil after scanning empty string")
}
}
-
-func TestValue(t *testing.T) {
- stringTest := "f47ac10b-58cc-0372-8567-0e02b2c3d479"
- uuid := Parse(stringTest)
- val, _ := uuid.Value()
- if val != stringTest {
- t.Error("Value() did not return expected string")
- }
-}
diff --git a/vendor/github.com/pborman/uuid/uuid.go b/vendor/github.com/pborman/uuid/uuid.go
index 82c9e7ee7..c4482cd87 100644
--- a/vendor/github.com/pborman/uuid/uuid.go
+++ b/vendor/github.com/pborman/uuid/uuid.go
@@ -13,20 +13,6 @@ import (
"strings"
)
-// Array is a pass-by-value UUID that can be used as an effecient key in a map.
-type Array [16]byte
-
-// UUID converts uuid into a slice.
-func (uuid Array) UUID() UUID {
- return uuid[:]
-}
-
-// String returns the string representation of uuid,
-// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
-func (uuid Array) String() string {
- return uuid.UUID().String()
-}
-
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
// 4122.
type UUID []byte
@@ -90,17 +76,6 @@ func Equal(uuid1, uuid2 UUID) bool {
return bytes.Equal(uuid1, uuid2)
}
-// Array returns an array representation of uuid that can be used as a map key.
-// Array panics if uuid is not valid.
-func (uuid UUID) Array() Array {
- if len(uuid) != 16 {
- panic("invalid uuid")
- }
- var a Array
- copy(a[:], uuid)
- return a
-}
-
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid.
func (uuid UUID) String() string {
diff --git a/vendor/github.com/pborman/uuid/uuid_test.go b/vendor/github.com/pborman/uuid/uuid_test.go
index cb1cd5cf9..3835cc808 100644
--- a/vendor/github.com/pborman/uuid/uuid_test.go
+++ b/vendor/github.com/pborman/uuid/uuid_test.go
@@ -300,7 +300,7 @@ func TestNode(t *testing.T) {
t.Error("nodeid is all zeros")
}
- id := []byte{1, 2, 3, 4, 5, 6, 7, 8}
+ id := []byte{1,2,3,4,5,6,7,8}
SetNodeID(id)
ni = NodeID()
if !bytes.Equal(ni, id[:6]) {
@@ -431,40 +431,6 @@ func TestBadRand(t *testing.T) {
}
}
-func TestUUID_Array(t *testing.T) {
- expect := Array{
- 0xf4, 0x7a, 0xc1, 0x0b,
- 0x58, 0xcc,
- 0x03, 0x72,
- 0x85, 0x67,
- 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
- }
- uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
- if uuid == nil {
- t.Fatal("invalid uuid")
- }
- if uuid.Array() != expect {
- t.Fatal("invalid array")
- }
-}
-
-func TestArray_UUID(t *testing.T) {
- array := Array{
- 0xf4, 0x7a, 0xc1, 0x0b,
- 0x58, 0xcc,
- 0x03, 0x72,
- 0x85, 0x67,
- 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
- }
- expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
- if expect == nil {
- t.Fatal("invalid uuid")
- }
- if !bytes.Equal(array.UUID(), expect) {
- t.Fatal("invalid uuid")
- }
-}
-
func BenchmarkParse(b *testing.B) {
for i := 0; i < b.N; i++ {
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
@@ -503,41 +469,3 @@ func BenchmarkUUID_URN(b *testing.B) {
}
}
}
-
-func BenchmarkUUID_Array(b *testing.B) {
- expect := Array{
- 0xf4, 0x7a, 0xc1, 0x0b,
- 0x58, 0xcc,
- 0x03, 0x72,
- 0x85, 0x67,
- 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
- }
- uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
- if uuid == nil {
- b.Fatal("invalid uuid")
- }
- for i := 0; i < b.N; i++ {
- if uuid.Array() != expect {
- b.Fatal("invalid array")
- }
- }
-}
-
-func BenchmarkArray_UUID(b *testing.B) {
- array := Array{
- 0xf4, 0x7a, 0xc1, 0x0b,
- 0x58, 0xcc,
- 0x03, 0x72,
- 0x85, 0x67,
- 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
- }
- expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
- if expect == nil {
- b.Fatal("invalid uuid")
- }
- for i := 0; i < b.N; i++ {
- if !bytes.Equal(array.UUID(), expect) {
- b.Fatal("invalid uuid")
- }
- }
-}
diff --git a/vendor/github.com/segmentio/analytics-go/cli/cli.go b/vendor/github.com/segmentio/analytics-go/cli/cli.go
deleted file mode 100644
index 9d1f940c9..000000000
--- a/vendor/github.com/segmentio/analytics-go/cli/cli.go
+++ /dev/null
@@ -1,174 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "log"
- "os"
- "reflect"
-
- "github.com/segmentio/analytics-go"
- "github.com/tj/docopt"
-)
-
-const Usage = `
-Analytics Go CLI
-
-Usage:
- analytics track <event> [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
- analytics screen <name> [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
- analytics page <name> [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
- analytics identify [--traits=<traits>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
- analytics group --groupId=<groupId> [--traits=<traits>] [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
- analytics alias --userId=<userId> --previousId=<previousId> [--traits=<traits>] [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
- analytics -h | --help
- analytics --version
-
-Options:
- -h --help Show this screen.
- --version Show version.
-`
-
-func main() {
- arguments, err := docopt.Parse(Usage, nil, true, "Anaytics Go CLI", false)
- check(err)
-
- writeKey := getOptionalString(arguments, "--writeKey")
- if writeKey == "" {
- writeKey = os.Getenv("SEGMENT_WRITE_KEY")
- if writeKey == "" {
- log.Fatal("either $SEGMENT_WRITE_KEY or --writeKey must be provided")
- }
- }
-
- client := analytics.New(writeKey)
- client.Size = 1
- client.Verbose = true
-
- if arguments["track"].(bool) {
- m := &analytics.Track{
- Event: arguments["<event>"].(string),
- }
- properties := getOptionalString(arguments, "--properties")
- if properties != "" {
- var parsedProperties map[string]interface{}
- err := json.Unmarshal([]byte(properties), &parsedProperties)
- check(err)
- m.Properties = parsedProperties
- }
-
- setCommonFields(m, arguments)
-
- check(client.Track(m))
- }
-
- if arguments["screen"].(bool) || arguments["page"].(bool) {
- m := &analytics.Page{
- Name: arguments["<name>"].(string),
- }
- /* Bug in Go library - page has traits not properties.
- properties := getOptionalString(arguments, "--properties")
- if properties != "" {
- var parsedProperties map[string]interface{}
- err := json.Unmarshal([]byte(properties), &parsedProperties)
- check(err)
- t.Properties = parsedProperties
- }
- */
-
- setCommonFields(m, arguments)
-
- check(client.Page(m))
- }
-
- if arguments["identify"].(bool) {
- m := &analytics.Identify{}
- traits := getOptionalString(arguments, "--traits")
- if traits != "" {
- var parsedTraits map[string]interface{}
- err := json.Unmarshal([]byte(traits), &parsedTraits)
- check(err)
- m.Traits = parsedTraits
- }
-
- setCommonFields(m, arguments)
-
- check(client.Identify(m))
- }
-
- if arguments["group"].(bool) {
- m := &analytics.Group{
- GroupId: arguments["--groupId"].(string),
- }
- traits := getOptionalString(arguments, "--traits")
- if traits != "" {
- var parsedTraits map[string]interface{}
- err := json.Unmarshal([]byte(traits), &parsedTraits)
- check(err)
- m.Traits = parsedTraits
- }
-
- setCommonFields(m, arguments)
-
- check(client.Group(m))
- }
-
- if arguments["alias"].(bool) {
- m := &analytics.Alias{
- PreviousId: arguments["--previousId"].(string),
- }
-
- setCommonFields(m, arguments)
-
- check(client.Alias(m))
- }
-
- client.Close()
-}
-
-func setCommonFields(message interface{}, arguments map[string]interface{}) {
- userId := getOptionalString(arguments, "--userId")
- if userId != "" {
- setFieldValue(message, "UserId", userId)
- }
- anonymousId := getOptionalString(arguments, "--anonymousId")
- if anonymousId != "" {
- setFieldValue(message, "AnonymousId", anonymousId)
- }
- integrations := getOptionalString(arguments, "--integrations")
- if integrations != "" {
- var parsedIntegrations map[string]interface{}
- err := json.Unmarshal([]byte(integrations), &parsedIntegrations)
- check(err)
- setFieldValue(message, "Integrations", parsedIntegrations)
- }
- context := getOptionalString(arguments, "--context")
- if context != "" {
- var parsedContext map[string]interface{}
- err := json.Unmarshal([]byte(context), &parsedContext)
- check(err)
- setFieldValue(message, "Context", parsedContext)
-
- }
- timestamp := getOptionalString(arguments, "--timestamp")
- if timestamp != "" {
- setFieldValue(message, "Timestamp", timestamp)
- }
-}
-
-func setFieldValue(target interface{}, field string, value interface{}) {
- reflect.ValueOf(target).Elem().FieldByName(field).Set(reflect.ValueOf(value))
-}
-
-func getOptionalString(m map[string]interface{}, k string) string {
- v := m[k]
- if v == nil {
- return ""
- }
- return v.(string)
-}
-
-func check(err error) {
- if err != nil {
- log.Fatal(err)
- }
-}
diff --git a/vendor/github.com/segmentio/backo-go/.gitmodules b/vendor/github.com/segmentio/backo-go/.gitmodules
new file mode 100644
index 000000000..36de92977
--- /dev/null
+++ b/vendor/github.com/segmentio/backo-go/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/github.com/bmizerany/assert"]
+ path = vendor/github.com/bmizerany/assert
+ url = https://github.com/bmizerany/assert