├── .github └── ISSUE_TEMPLATE ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── auth.go ├── auth_test.go ├── bson ├── LICENSE ├── README.md ├── bson.go ├── bson_corpus_spec_test.go ├── bson_corpus_spec_test_generator.go ├── bson_test.go ├── compatability_test.go ├── compatibility.go ├── decimal.go ├── decimal_test.go ├── decode.go ├── encode.go ├── json.go ├── json_test.go ├── specdata │ ├── update.ps1 │ └── update.sh ├── stream.go └── stream_test.go ├── bulk.go ├── bulk_test.go ├── changestreams.go ├── changestreams_test.go ├── cluster.go ├── cluster_test.go ├── coarse_time.go ├── coarse_time_test.go ├── dbtest ├── dbserver.go ├── dbserver_test.go └── export_test.go ├── doc.go ├── example_test.go ├── export_test.go ├── gridfs.go ├── gridfs_test.go ├── harness ├── certs │ ├── client.crt │ ├── client.key │ ├── client.pem │ ├── client.req │ ├── server.crt │ ├── server.key │ └── server.pem ├── daemons │ ├── .env │ ├── cfg1 │ │ ├── db │ │ │ ├── .empty │ │ │ ├── journal │ │ │ │ └── tempLatencyTest │ │ │ └── mongod.lock │ │ ├── log │ │ │ └── run │ │ └── run │ ├── cfg2 │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── cfg3 │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── db1 │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── db2 │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── db3 │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs1a │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs1b │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs1c │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs2a │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs2b │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs2c │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs3a │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs3b │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs3c │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── rs4a │ │ ├── db │ │ │ └── .empty │ │ ├── log │ │ │ └── run │ │ └── run │ ├── s1 │ │ ├── log │ │ │ └── run │ │ └── run │ ├── s2 │ │ ├── log │ │ │ └── run │ │ └── run │ └── s3 │ │ ├── log │ │ └── run │ │ └── run ├── mongojs │ ├── dropall.js │ ├── init.js │ └── wait.js └── setup.sh ├── internal ├── json │ ├── LICENSE │ ├── bench_test.go │ ├── decode.go │ ├── decode_test.go │ ├── encode.go │ ├── encode_test.go │ ├── example_test.go │ ├── extension.go │ ├── extension_test.go │ ├── fold.go │ ├── fold_test.go │ ├── indent.go │ ├── number_test.go │ ├── scanner.go │ ├── scanner_test.go │ ├── stream.go │ ├── stream_test.go │ ├── tagkey_test.go │ ├── tags.go │ ├── tags_test.go │ └── testdata │ │ └── code.json.gz ├── sasl │ ├── sasl.c │ ├── sasl.go │ ├── sasl_windows.c │ ├── sasl_windows.go │ ├── sasl_windows.h │ ├── sspi_windows.c │ └── sspi_windows.h └── scram │ ├── scram.go │ └── scram_test.go ├── log.go ├── queue.go ├── queue_test.go ├── raceoff.go ├── raceon.go ├── saslimpl.go ├── saslstub.go ├── server.go ├── server_test.go ├── session.go ├── session_internal_test.go ├── session_test.go ├── socket.go ├── stats.go ├── suite_test.go ├── syscall_test.go ├── syscall_windows_test.go └── txn ├── chaos.go ├── debug.go ├── dockey_test.go ├── flusher.go ├── output.txt ├── sim_test.go ├── tarjan.go ├── tarjan_test.go ├── txn.go └── txn_test.go /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | We use the issue tracker to track bugs with mgo - if you have a usage question, 2 | it's best to try Stack Overflow :) 3 | 4 | Replace this text with your description, and please answer the questions below 5 | before submitting your issue to help us out. Thanks! 6 | 7 | ------------------------------------------------------------------------------- 8 | 9 | ### What version of MongoDB are you using (`mongod --version`)? 10 | ``` 11 | 12 | ``` 13 | 14 | ### What version of Go are you using (`go version`)? 15 | ``` 16 | 17 | ``` 18 | 19 | ### What operating system and processor architecture are you using (`go env`)? 20 | ``` 21 | 22 | ``` 23 | 24 | ### What did you do? 25 | 26 | If possible, provide a recipe for reproducing the error. 27 | A runnable program is great and really helps! 28 | 29 | ### Can you reproduce the issue on the latest `development` branch? -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _harness 2 | .vscode -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go_import_path: github.com/globalsign/mgo 4 | 5 | go: 6 | - 1.9.x 7 | - 1.10.x 8 | 9 | env: 10 | global: 11 | - BUCKET=https://s3.eu-west-2.amazonaws.com/globalsign-mgo 12 | - FASTDL=https://fastdl.mongodb.org/linux 13 | matrix: 14 | - MONGODB=x86_64-ubuntu1404-3.0.15 15 | - MONGODB=x86_64-ubuntu1404-3.2.17 16 | - MONGODB=x86_64-ubuntu1404-3.4.10 17 | - MONGODB=x86_64-ubuntu1404-3.6.0 18 | 19 | install: 20 | 21 | - wget $FASTDL/mongodb-linux-$MONGODB.tgz 22 | - tar xzvf mongodb-linux-$MONGODB.tgz 23 | - export PATH=$PWD/mongodb-linux-$MONGODB/bin:$PATH 24 | 25 | - wget $BUCKET/daemontools.tar.gz 26 | - tar xzvf daemontools.tar.gz 27 | - export PATH=$PWD/daemontools:$PATH 28 | 29 | - go get gopkg.in/check.v1 30 | - go get gopkg.in/yaml.v2 31 | - go get gopkg.in/tomb.v2 32 | - go get golang.org/x/lint/golint 33 | 34 | before_script: 35 | - golint ./... | grep -v 'ID' | cat 36 | - go vet github.com/globalsign/mgo/bson github.com/globalsign/mgo/txn github.com/globalsign/mgo 37 | - export NOIPV6=1 38 | - make startdb 39 | 40 | script: 41 | - (cd bson && go test -check.v) 42 | - go test -check.v -fast 43 | - (cd txn && go test -check.v) 44 | - make stopdb 45 | 46 | git: 47 | depth: 3 48 | 49 | # vim:sw=4:ts=4:et 50 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ------------------------- 3 | 4 | We really appreciate contributions, but they must meet the following requirements: 5 | 6 | * A PR should have a brief description of the problem/feature being proposed 7 | * Pull requests should target the `development` branch 8 | * Existing tests should pass and any new code should be covered with it's own test(s) (use [travis-ci](https://travis-ci.org)) 9 | * New functions should be [documented](https://blog.golang.org/godoc-documenting-go-code) clearly 10 | * Code should pass `golint`, `go vet` and `go fmt` 11 | 12 | We merge PRs into `development`, which is then tested in a sharded, replicated environment in our datacenter for regressions. Once everyone is happy, we merge to master - this is to maintain a bit of quality control past the usual PR process. 13 | 14 | **Thanks** for helping! 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | mgo - MongoDB driver for Go 2 | 3 | Copyright (c) 2010-2013 - Gustavo Niemeyer 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | startdb: 2 | @harness/setup.sh start 3 | 4 | stopdb: 5 | @harness/setup.sh stop 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/globalsign/mgo.svg?branch=master)](https://travis-ci.org/globalsign/mgo) [![GoDoc](https://godoc.org/github.com/globalsign/mgo?status.svg)](https://godoc.org/github.com/globalsign/mgo) 2 | 3 | The MongoDB driver for Go 4 | ------------------------- 5 | 6 | This fork has had a few improvements by ourselves as well as several PR's merged from the original mgo repo that are currently awaiting review. 7 | Changes are mostly geared towards performance improvements and bug fixes, though a few new features have been added. 8 | 9 | Further PR's (with tests) are welcome, but please maintain backwards compatibility. 10 | 11 | Detailed documentation of the API is available at 12 | [GoDoc](https://godoc.org/github.com/globalsign/mgo). 13 | 14 | A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implements the [BSON](http://bsonspec.org) specification is also included, and may be used independently of the driver. 15 | 16 | ## Supported Versions 17 | 18 | `mgo` is known to work well on (and has integration tests against) MongoDB v3.0, 3.2, 3.4 and 3.6. 19 | 20 | MongoDB 4.0 is currently experimental - we would happily accept PRs to help improve support! 21 | 22 | ## Changes 23 | * Fixes attempting to authenticate before every query ([details](https://github.com/go-mgo/mgo/issues/254)) 24 | * Removes bulk update / delete batch size limitations ([details](https://github.com/go-mgo/mgo/issues/288)) 25 | * Adds native support for `time.Duration` marshalling ([details](https://github.com/go-mgo/mgo/pull/373)) 26 | * Reduce memory footprint / garbage collection pressure by reusing buffers ([details](https://github.com/go-mgo/mgo/pull/229), [more](https://github.com/globalsign/mgo/pull/56)) 27 | * Support majority read concerns ([details](https://github.com/globalsign/mgo/pull/2)) 28 | * Improved connection handling ([details](https://github.com/globalsign/mgo/pull/5)) 29 | * Hides SASL warnings ([details](https://github.com/globalsign/mgo/pull/7)) 30 | * Support for partial indexes ([details](https://github.com/domodwyer/mgo/commit/5efe8eccb028238d93c222828cae4806aeae9f51)) 31 | * Fixes timezone handling ([details](https://github.com/go-mgo/mgo/pull/464)) 32 | * Integration tests run against MongoDB 3.2 & 3.4 releases ([details](https://github.com/globalsign/mgo/pull/4), [more](https://github.com/globalsign/mgo/pull/24), [more](https://github.com/globalsign/mgo/pull/35)) 33 | * Improved multi-document transaction performance ([details](https://github.com/globalsign/mgo/pull/10), [more](https://github.com/globalsign/mgo/pull/11), [more](https://github.com/globalsign/mgo/pull/16)) 34 | * Fixes cursor timeouts ([details](https://jira.mongodb.org/browse/SERVER-24899)) 35 | * Support index hints and timeouts for count queries ([details](https://github.com/globalsign/mgo/pull/17)) 36 | * Don't panic when handling indexed `int64` fields ([details](https://github.com/go-mgo/mgo/issues/475)) 37 | * Supports dropping all indexes on a collection ([details](https://github.com/globalsign/mgo/pull/25)) 38 | * Annotates log entries/profiler output with optional appName on 3.4+ ([details](https://github.com/globalsign/mgo/pull/28)) 39 | * Support for read-only [views](https://docs.mongodb.com/manual/core/views/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/33)) 40 | * Support for [collations](https://docs.mongodb.com/manual/reference/collation/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/37), [more](https://github.com/globalsign/mgo/pull/166)) 41 | * Provide BSON constants for convenience/sanity ([details](https://github.com/globalsign/mgo/pull/41)) 42 | * Consistently unmarshal time.Time values as UTC ([details](https://github.com/globalsign/mgo/pull/42)) 43 | * Enforces best practise coding guidelines ([details](https://github.com/globalsign/mgo/pull/44)) 44 | * GetBSON correctly handles structs with both fields and pointers ([details](https://github.com/globalsign/mgo/pull/40)) 45 | * Improved bson.Raw unmarshalling performance ([details](https://github.com/globalsign/mgo/pull/49)) 46 | * Minimise socket connection timeouts due to excessive locking ([details](https://github.com/globalsign/mgo/pull/52)) 47 | * Natively support X509 client authentication ([details](https://github.com/globalsign/mgo/pull/55)) 48 | * Gracefully recover from a temporarily unreachable server ([details](https://github.com/globalsign/mgo/pull/69)) 49 | * Use JSON tags when no explicit BSON are tags set ([details](https://github.com/globalsign/mgo/pull/91)) 50 | * Support [$changeStream](https://docs.mongodb.com/manual/changeStreams/) tailing on 3.6+ ([details](https://github.com/globalsign/mgo/pull/97)) 51 | * Fix deadlock in cluster synchronisation ([details](https://github.com/globalsign/mgo/issues/120)) 52 | * Implement `maxIdleTimeout` for pooled connections ([details](https://github.com/globalsign/mgo/pull/116)) 53 | * Connection pool waiting improvements ([details](https://github.com/globalsign/mgo/pull/115)) 54 | * Fixes BSON encoding for `$in` and friends ([details](https://github.com/globalsign/mgo/pull/128)) 55 | * Add BSON stream encoders ([details](https://github.com/globalsign/mgo/pull/127)) 56 | * Add integer map key support in the BSON encoder ([details](https://github.com/globalsign/mgo/pull/140)) 57 | * Support aggregation [collations](https://docs.mongodb.com/manual/reference/collation/) ([details](https://github.com/globalsign/mgo/pull/144)) 58 | * Support encoding of inline struct references ([details](https://github.com/globalsign/mgo/pull/146)) 59 | * Improved windows test harness ([details](https://github.com/globalsign/mgo/pull/158)) 60 | * Improved type and nil handling in the BSON codec ([details](https://github.com/globalsign/mgo/pull/147/files), [more](https://github.com/globalsign/mgo/pull/181)) 61 | * Separated network read/write timeouts ([details](https://github.com/globalsign/mgo/pull/161)) 62 | * Expanded dial string configuration options ([details](https://github.com/globalsign/mgo/pull/162)) 63 | * Implement MongoTimestamp ([details](https://github.com/globalsign/mgo/pull/171)) 64 | * Support setting `writeConcern` for `findAndModify` operations ([details](https://github.com/globalsign/mgo/pull/185)) 65 | * Add `ssl` to the dial string options ([details](https://github.com/globalsign/mgo/pull/184)) 66 | 67 | 68 | --- 69 | 70 | ### Thanks to 71 | * @aksentyev 72 | * @bachue 73 | * @bozaro 74 | * @BenLubar 75 | * @carldunham 76 | * @carter2000 77 | * @cedric-cordenier 78 | * @cezarsa 79 | * @DaytonG 80 | * @ddspog 81 | * @drichelson 82 | * @dvic 83 | * @eaglerayp 84 | * @feliixx 85 | * @fmpwizard 86 | * @gazoon 87 | * @gedge 88 | * @gnawux 89 | * @idy 90 | * @jameinel 91 | * @jefferickson 92 | * @johnlawsharrison 93 | * @KJTsanaktsidis 94 | * @larrycinnabar 95 | * @mapete94 96 | * @maxnoel 97 | * @mcspring 98 | * @Mei-Zhao 99 | * @peterdeka 100 | * @Reenjii 101 | * @roobre 102 | * @smoya 103 | * @steve-gray 104 | * @tbruyelle 105 | * @wgallagher 106 | -------------------------------------------------------------------------------- /bson/LICENSE: -------------------------------------------------------------------------------- 1 | BSON library for Go 2 | 3 | Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /bson/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/globalsign/mgo/bson?status.svg)](https://godoc.org/github.com/globalsign/mgo/bson) 2 | 3 | An Implementation of BSON for Go 4 | -------------------------------- 5 | 6 | Package bson is an implementation of the [BSON specification](http://bsonspec.org) for Go. 7 | 8 | While the BSON package implements the BSON spec as faithfully as possible, there 9 | is some MongoDB specific behaviour (such as map keys `$in`, `$all`, etc) in the 10 | `bson` package. The priority is for backwards compatibility for the `mgo` 11 | driver, though fixes for obviously buggy behaviour is welcome (and features, etc 12 | behind feature flags). 13 | -------------------------------------------------------------------------------- /bson/bson_corpus_spec_test_generator.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "go/format" 9 | "html/template" 10 | "io/ioutil" 11 | "log" 12 | "path/filepath" 13 | "strings" 14 | 15 | "github.com/globalsign/mgo/internal/json" 16 | ) 17 | 18 | func main() { 19 | log.SetFlags(0) 20 | log.SetPrefix(name + ": ") 21 | 22 | var g Generator 23 | 24 | fmt.Fprintf(&g, "// Code generated by \"%s.go\"; DO NOT EDIT\n\n", name) 25 | 26 | src := g.generate() 27 | 28 | err := ioutil.WriteFile(fmt.Sprintf("%s.go", strings.TrimSuffix(name, "_generator")), src, 0644) 29 | if err != nil { 30 | log.Fatalf("writing output: %s", err) 31 | } 32 | } 33 | 34 | // Generator holds the state of the analysis. Primarily used to buffer 35 | // the output for format.Source. 36 | type Generator struct { 37 | bytes.Buffer // Accumulated output. 38 | } 39 | 40 | // format returns the gofmt-ed contents of the Generator's buffer. 41 | func (g *Generator) format() []byte { 42 | src, err := format.Source(g.Bytes()) 43 | if err != nil { 44 | // Should never happen, but can arise when developing this code. 45 | // The user can compile the output to see the error. 46 | log.Printf("warning: internal error: invalid Go generated: %s", err) 47 | log.Printf("warning: compile the package to analyze the error") 48 | return g.Bytes() 49 | } 50 | return src 51 | } 52 | 53 | // EVERYTHING ABOVE IS CONSTANT BETWEEN THE GENERATORS 54 | 55 | const name = "bson_corpus_spec_test_generator" 56 | 57 | func (g *Generator) generate() []byte { 58 | 59 | testFiles, err := filepath.Glob("./specdata/specifications/source/bson-corpus/tests/*.json") 60 | if err != nil { 61 | log.Fatalf("error reading bson-corpus files: %s", err) 62 | } 63 | 64 | tests, err := g.loadTests(testFiles) 65 | if err != nil { 66 | log.Fatalf("error loading tests: %s", err) 67 | } 68 | 69 | tmpl, err := g.getTemplate() 70 | if err != nil { 71 | log.Fatalf("error loading template: %s", err) 72 | } 73 | 74 | tmpl.Execute(&g.Buffer, tests) 75 | 76 | return g.format() 77 | } 78 | 79 | func (g *Generator) loadTests(filenames []string) ([]*testDef, error) { 80 | var tests []*testDef 81 | for _, filename := range filenames { 82 | test, err := g.loadTest(filename) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | tests = append(tests, test) 88 | } 89 | 90 | return tests, nil 91 | } 92 | 93 | func (g *Generator) loadTest(filename string) (*testDef, error) { 94 | content, err := ioutil.ReadFile(filename) 95 | if err != nil { 96 | return nil, err 97 | } 98 | 99 | var testDef testDef 100 | err = json.Unmarshal(content, &testDef) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | names := make(map[string]struct{}) 106 | 107 | for i := len(testDef.Valid) - 1; i >= 0; i-- { 108 | if testDef.BsonType == "0x05" && testDef.Valid[i].Description == "subtype 0x02" { 109 | testDef.Valid = append(testDef.Valid[:i], testDef.Valid[i+1:]...) 110 | continue 111 | } 112 | 113 | name := cleanupFuncName(testDef.Description + "_" + testDef.Valid[i].Description) 114 | nameIdx := name 115 | j := 1 116 | for { 117 | if _, ok := names[nameIdx]; !ok { 118 | break 119 | } 120 | 121 | nameIdx = fmt.Sprintf("%s_%d", name, j) 122 | } 123 | 124 | names[nameIdx] = struct{}{} 125 | 126 | testDef.Valid[i].TestDef = &testDef 127 | testDef.Valid[i].Name = nameIdx 128 | testDef.Valid[i].StructTest = testDef.TestKey != "" && 129 | (testDef.BsonType != "0x05" || strings.Contains(testDef.Valid[i].Description, "0x00")) && 130 | !testDef.Deprecated 131 | } 132 | 133 | for i := len(testDef.DecodeErrors) - 1; i >= 0; i-- { 134 | if strings.Contains(testDef.DecodeErrors[i].Description, "UTF-8") { 135 | testDef.DecodeErrors = append(testDef.DecodeErrors[:i], testDef.DecodeErrors[i+1:]...) 136 | continue 137 | } 138 | 139 | name := cleanupFuncName(testDef.Description + "_" + testDef.DecodeErrors[i].Description) 140 | nameIdx := name 141 | j := 1 142 | for { 143 | if _, ok := names[nameIdx]; !ok { 144 | break 145 | } 146 | 147 | nameIdx = fmt.Sprintf("%s_%d", name, j) 148 | } 149 | names[nameIdx] = struct{}{} 150 | 151 | testDef.DecodeErrors[i].Name = nameIdx 152 | } 153 | 154 | return &testDef, nil 155 | } 156 | 157 | func (g *Generator) getTemplate() (*template.Template, error) { 158 | content := `package bson_test 159 | 160 | import ( 161 | "encoding/hex" 162 | "time" 163 | 164 | . "gopkg.in/check.v1" 165 | "github.com/globalsign/mgo/bson" 166 | ) 167 | 168 | func testValid(c *C, in []byte, expected []byte, result interface{}) { 169 | err := bson.Unmarshal(in, result) 170 | c.Assert(err, IsNil) 171 | 172 | out, err := bson.Marshal(result) 173 | c.Assert(err, IsNil) 174 | 175 | c.Assert(string(expected), Equals, string(out), Commentf("roundtrip failed for %T, expected '%x' but got '%x'", result, expected, out)) 176 | } 177 | 178 | func testDecodeSkip(c *C, in []byte) { 179 | err := bson.Unmarshal(in, &struct{}{}) 180 | c.Assert(err, IsNil) 181 | } 182 | 183 | func testDecodeError(c *C, in []byte, result interface{}) { 184 | err := bson.Unmarshal(in, result) 185 | c.Assert(err, Not(IsNil)) 186 | } 187 | 188 | {{range .}} 189 | {{range .Valid}} 190 | func (s *S) Test{{.Name}}(c *C) { 191 | b, err := hex.DecodeString("{{.Bson}}") 192 | c.Assert(err, IsNil) 193 | 194 | {{if .CanonicalBson}} 195 | cb, err := hex.DecodeString("{{.CanonicalBson}}") 196 | c.Assert(err, IsNil) 197 | {{else}} 198 | cb := b 199 | {{end}} 200 | 201 | var resultD bson.D 202 | testValid(c, b, cb, &resultD) 203 | {{if .StructTest}}var resultS struct { 204 | Element {{.TestDef.GoType}} ` + "`bson:\"{{.TestDef.TestKey}}\"`" + ` 205 | } 206 | testValid(c, b, cb, &resultS){{end}} 207 | 208 | testDecodeSkip(c, b) 209 | } 210 | {{end}} 211 | 212 | {{range .DecodeErrors}} 213 | func (s *S) Test{{.Name}}(c *C) { 214 | b, err := hex.DecodeString("{{.Bson}}") 215 | c.Assert(err, IsNil) 216 | 217 | var resultD bson.D 218 | testDecodeError(c, b, &resultD) 219 | } 220 | {{end}} 221 | {{end}} 222 | ` 223 | tmpl, err := template.New("").Parse(content) 224 | if err != nil { 225 | return nil, err 226 | } 227 | return tmpl, nil 228 | } 229 | 230 | func cleanupFuncName(name string) string { 231 | return strings.Map(func(r rune) rune { 232 | if (r >= 48 && r <= 57) || (r >= 65 && r <= 90) || (r >= 97 && r <= 122) { 233 | return r 234 | } 235 | return '_' 236 | }, name) 237 | } 238 | 239 | type testDef struct { 240 | Description string `json:"description"` 241 | BsonType string `json:"bson_type"` 242 | TestKey string `json:"test_key"` 243 | Valid []*valid `json:"valid"` 244 | DecodeErrors []*decodeError `json:"decodeErrors"` 245 | Deprecated bool `json:"deprecated"` 246 | } 247 | 248 | func (t *testDef) GoType() string { 249 | switch t.BsonType { 250 | case "0x01": 251 | return "float64" 252 | case "0x02": 253 | return "string" 254 | case "0x03": 255 | return "bson.D" 256 | case "0x04": 257 | return "[]interface{}" 258 | case "0x05": 259 | return "[]byte" 260 | case "0x07": 261 | return "bson.ObjectId" 262 | case "0x08": 263 | return "bool" 264 | case "0x09": 265 | return "time.Time" 266 | case "0x0E": 267 | return "string" 268 | case "0x10": 269 | return "int32" 270 | case "0x12": 271 | return "int64" 272 | case "0x13": 273 | return "bson.Decimal" 274 | default: 275 | return "interface{}" 276 | } 277 | } 278 | 279 | type valid struct { 280 | Description string `json:"description"` 281 | Bson string `json:"bson"` 282 | CanonicalBson string `json:"canonical_bson"` 283 | 284 | Name string 285 | StructTest bool 286 | TestDef *testDef 287 | } 288 | 289 | type decodeError struct { 290 | Description string `json:"description"` 291 | Bson string `json:"bson"` 292 | 293 | Name string 294 | } 295 | -------------------------------------------------------------------------------- /bson/compatability_test.go: -------------------------------------------------------------------------------- 1 | package bson_test 2 | 3 | import ( 4 | "github.com/globalsign/mgo/bson" 5 | . "gopkg.in/check.v1" 6 | ) 7 | 8 | type mixedTagging struct { 9 | First string 10 | Second string `bson:"second_field"` 11 | Third string `json:"third_field"` 12 | Fourth string `bson:"fourth_field" json:"alternate"` 13 | } 14 | 15 | // TestTaggingFallback checks that tagging fallback can be used/works as expected. 16 | func (s *S) TestTaggingFallback(c *C) { 17 | initial := &mixedTagging{ 18 | First: "One", 19 | Second: "Two", 20 | Third: "Three", 21 | Fourth: "Four", 22 | } 23 | 24 | // Take only testing.T, leave only footprints. 25 | initialState := bson.JSONTagFallbackState() 26 | defer bson.SetJSONTagFallback(initialState) 27 | 28 | // Marshal with the new mode applied. 29 | bson.SetJSONTagFallback(true) 30 | bsonState, errBSON := bson.Marshal(initial) 31 | c.Assert(errBSON, IsNil) 32 | 33 | // Unmarshal into a generic map so that we can pick up the actual field names 34 | // selected. 35 | target := make(map[string]string) 36 | errUnmarshal := bson.Unmarshal(bsonState, target) 37 | c.Assert(errUnmarshal, IsNil) 38 | 39 | // No tag, so standard naming 40 | _, firstExists := target["first"] 41 | c.Assert(firstExists, Equals, true) 42 | 43 | // Just a BSON tag 44 | _, secondExists := target["second_field"] 45 | c.Assert(secondExists, Equals, true) 46 | 47 | // Just a JSON tag 48 | _, thirdExists := target["third_field"] 49 | c.Assert(thirdExists, Equals, true) 50 | 51 | // Should marshal 4th as fourth_field (since we have both tags) 52 | _, fourthExists := target["fourth_field"] 53 | c.Assert(fourthExists, Equals, true) 54 | } 55 | -------------------------------------------------------------------------------- /bson/compatibility.go: -------------------------------------------------------------------------------- 1 | package bson 2 | 3 | // Current state of the JSON tag fallback option. 4 | var useJSONTagFallback = false 5 | var useRespectNilValues = false 6 | 7 | // SetJSONTagFallback enables or disables the JSON-tag fallback for structure tagging. When this is enabled, structures 8 | // without BSON tags on a field will fall-back to using the JSON tag (if present). 9 | func SetJSONTagFallback(state bool) { 10 | useJSONTagFallback = state 11 | } 12 | 13 | // JSONTagFallbackState returns the current status of the JSON tag fallback compatability option. See SetJSONTagFallback 14 | // for more information. 15 | func JSONTagFallbackState() bool { 16 | return useJSONTagFallback 17 | } 18 | 19 | // SetRespectNilValues enables or disables serializing nil slices or maps to `null` values. 20 | // In other words it enables `encoding/json` compatible behaviour. 21 | func SetRespectNilValues(state bool) { 22 | useRespectNilValues = state 23 | } 24 | 25 | // RespectNilValuesState returns the current status of the JSON nil slices and maps fallback compatibility option. 26 | // See SetRespectNilValues for more information. 27 | func RespectNilValuesState() bool { 28 | return useRespectNilValues 29 | } 30 | -------------------------------------------------------------------------------- /bson/json_test.go: -------------------------------------------------------------------------------- 1 | package bson_test 2 | 3 | import ( 4 | "github.com/globalsign/mgo/bson" 5 | 6 | "reflect" 7 | "strings" 8 | "time" 9 | 10 | . "gopkg.in/check.v1" 11 | ) 12 | 13 | type jsonTest struct { 14 | a interface{} // value encoded into JSON (optional) 15 | b string // JSON expected as output of , and used as input to 16 | c interface{} // Value expected from decoding , defaults to 17 | e string // error string, if decoding (b) should fail 18 | } 19 | 20 | var jsonTests = []jsonTest{ 21 | // $binary 22 | { 23 | a: []byte("foo"), 24 | b: `{"$binary":"Zm9v","$type":"0x0"}`, 25 | }, { 26 | a: bson.Binary{Kind: 2, Data: []byte("foo")}, 27 | b: `{"$binary":"Zm9v","$type":"0x2"}`, 28 | }, { 29 | b: `BinData(2,"Zm9v")`, 30 | c: bson.Binary{Kind: 2, Data: []byte("foo")}, 31 | }, 32 | 33 | // $date 34 | { 35 | a: time.Date(2016, 5, 15, 1, 2, 3, 4000000, time.UTC), 36 | b: `{"$date":"2016-05-15T01:02:03.004Z"}`, 37 | }, { 38 | a: time.Date(2016, 5, 15, 1, 2, 3, 4000000, time.FixedZone("CET", 60*60)), 39 | b: `{"$date":"2016-05-15T01:02:03.004+01:00"}`, 40 | }, { 41 | b: `{"$date": {"$numberLong": "1002"}}`, 42 | c: time.Date(1970, 1, 1, 0, 0, 1, 2e6, time.UTC), 43 | }, { 44 | b: `ISODate("2016-05-15T01:02:03.004Z")`, 45 | c: time.Date(2016, 5, 15, 1, 2, 3, 4000000, time.UTC), 46 | }, { 47 | b: `ISODate("2016-05-15T01:02:03.004-07:00")`, 48 | c: time.Date(2016, 5, 15, 1, 2, 3, 4000000, time.FixedZone("PDT", -7*60*60)), 49 | }, { 50 | b: `new Date(1000)`, 51 | c: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), 52 | }, { 53 | b: `new Date("2016-05-15")`, 54 | c: time.Date(2016, 5, 15, 0, 0, 0, 0, time.UTC), 55 | }, 56 | 57 | // $timestamp 58 | { 59 | a: bson.MongoTimestamp(4294967298), 60 | b: `{"$timestamp":{"t":1,"i":2}}`, 61 | }, { 62 | b: `Timestamp(1, 2)`, 63 | c: bson.MongoTimestamp(4294967298), 64 | }, 65 | 66 | // $regex 67 | { 68 | a: bson.RegEx{Pattern: "pattern", Options: "options"}, 69 | b: `{"$regex":"pattern","$options":"options"}`, 70 | }, 71 | 72 | // $oid 73 | { 74 | a: bson.ObjectIdHex("0123456789abcdef01234567"), 75 | b: `{"$oid":"0123456789abcdef01234567"}`, 76 | }, { 77 | b: `ObjectId("0123456789abcdef01234567")`, 78 | c: bson.ObjectIdHex("0123456789abcdef01234567"), 79 | }, 80 | 81 | // $ref (no special type) 82 | { 83 | b: `DBRef("name", "id")`, 84 | c: map[string]interface{}{"$ref": "name", "$id": "id"}, 85 | }, 86 | 87 | // $numberLong 88 | { 89 | a: 123, 90 | b: `123`, 91 | }, { 92 | a: int64(9007199254740992), 93 | b: `{"$numberLong":9007199254740992}`, 94 | }, { 95 | a: int64(1<<53 + 1), 96 | b: `{"$numberLong":"9007199254740993"}`, 97 | }, { 98 | a: 1<<53 + 1, 99 | b: `{"$numberLong":"9007199254740993"}`, 100 | c: int64(9007199254740993), 101 | }, { 102 | b: `NumberLong(9007199254740992)`, 103 | c: int64(1 << 53), 104 | }, { 105 | b: `NumberLong("9007199254740993")`, 106 | c: int64(1<<53 + 1), 107 | }, 108 | 109 | // $minKey, $maxKey 110 | { 111 | a: bson.MinKey, 112 | b: `{"$minKey":1}`, 113 | }, { 114 | a: bson.MaxKey, 115 | b: `{"$maxKey":1}`, 116 | }, { 117 | b: `MinKey`, 118 | c: bson.MinKey, 119 | }, { 120 | b: `MaxKey`, 121 | c: bson.MaxKey, 122 | }, { 123 | b: `{"$minKey":0}`, 124 | e: `invalid $minKey object: {"$minKey":0}`, 125 | }, { 126 | b: `{"$maxKey":0}`, 127 | e: `invalid $maxKey object: {"$maxKey":0}`, 128 | }, 129 | 130 | { 131 | a: bson.Undefined, 132 | b: `{"$undefined":true}`, 133 | }, { 134 | b: `undefined`, 135 | c: bson.Undefined, 136 | }, { 137 | b: `{"v": undefined}`, 138 | c: struct{ V interface{} }{bson.Undefined}, 139 | }, 140 | 141 | // Unquoted keys and trailing commas 142 | { 143 | b: `{$foo: ["bar",],}`, 144 | c: map[string]interface{}{"$foo": []interface{}{"bar"}}, 145 | }, 146 | } 147 | 148 | func (s *S) TestJSON(c *C) { 149 | for i, item := range jsonTests { 150 | c.Logf("------------ (#%d)", i) 151 | c.Logf("A: %#v", item.a) 152 | c.Logf("B: %#v", item.b) 153 | 154 | if item.c == nil { 155 | item.c = item.a 156 | } else { 157 | c.Logf("C: %#v", item.c) 158 | } 159 | if item.e != "" { 160 | c.Logf("E: %s", item.e) 161 | } 162 | 163 | if item.a != nil { 164 | data, err := bson.MarshalJSON(item.a) 165 | c.Assert(err, IsNil) 166 | c.Logf("Dumped: %#v", string(data)) 167 | c.Assert(strings.TrimSuffix(string(data), "\n"), Equals, item.b) 168 | } 169 | 170 | var zero interface{} 171 | if item.c == nil { 172 | zero = &struct{}{} 173 | } else { 174 | zero = reflect.New(reflect.TypeOf(item.c)).Interface() 175 | } 176 | err := bson.UnmarshalJSON([]byte(item.b), zero) 177 | if item.e != "" { 178 | c.Assert(err, NotNil) 179 | c.Assert(err.Error(), Equals, item.e) 180 | continue 181 | } 182 | c.Assert(err, IsNil) 183 | zerov := reflect.ValueOf(zero) 184 | value := zerov.Interface() 185 | if zerov.Kind() == reflect.Ptr { 186 | value = zerov.Elem().Interface() 187 | } 188 | c.Logf("Loaded: %#v", value) 189 | if ctime, ok := item.c.(time.Time); ok { 190 | // time.Time must be compared with time.Time.Equal and not reflect.DeepEquals 191 | c.Assert(ctime.Equal(value.(time.Time)), Equals, true) 192 | continue 193 | } 194 | c.Assert(value, DeepEquals, item.c) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /bson/specdata/update.ps1: -------------------------------------------------------------------------------- 1 | 2 | $repo = "git@github.com:mongodb/specifications" 3 | 4 | git clone $repo 5 | 6 | go generate ../ -------------------------------------------------------------------------------- /bson/specdata/update.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | set -e 4 | 5 | rm -rf specifications 6 | 7 | git clone git@github.com:mongodb/specifications 8 | 9 | go generate ../ -------------------------------------------------------------------------------- /bson/stream.go: -------------------------------------------------------------------------------- 1 | package bson 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | ) 9 | 10 | const ( 11 | // MinDocumentSize is the size of the smallest possible valid BSON document: 12 | // an int32 size header + 0x00 (end of document). 13 | MinDocumentSize = 5 14 | 15 | // MaxDocumentSize is the largest possible size for a BSON document allowed by MongoDB, 16 | // that is, 16 MiB (see https://docs.mongodb.com/manual/reference/limits/). 17 | MaxDocumentSize = 16777216 18 | ) 19 | 20 | // ErrInvalidDocumentSize is an error returned when a BSON document's header 21 | // contains a size smaller than MinDocumentSize or greater than MaxDocumentSize. 22 | type ErrInvalidDocumentSize struct { 23 | DocumentSize int32 24 | } 25 | 26 | func (e ErrInvalidDocumentSize) Error() string { 27 | return fmt.Sprintf("invalid document size %d", e.DocumentSize) 28 | } 29 | 30 | // A Decoder reads and decodes BSON values from an input stream. 31 | type Decoder struct { 32 | source io.Reader 33 | } 34 | 35 | // NewDecoder returns a new Decoder that reads from source. 36 | // It does not add any extra buffering, and may not read data from source beyond the BSON values requested. 37 | func NewDecoder(source io.Reader) *Decoder { 38 | return &Decoder{source: source} 39 | } 40 | 41 | // Decode reads the next BSON-encoded value from its input and stores it in the value pointed to by v. 42 | // See the documentation for Unmarshal for details about the conversion of BSON into a Go value. 43 | func (dec *Decoder) Decode(v interface{}) (err error) { 44 | // BSON documents start with their size as a *signed* int32. 45 | var docSize int32 46 | if err = binary.Read(dec.source, binary.LittleEndian, &docSize); err != nil { 47 | return 48 | } 49 | 50 | if docSize < MinDocumentSize || docSize > MaxDocumentSize { 51 | return ErrInvalidDocumentSize{DocumentSize: docSize} 52 | } 53 | 54 | docBuffer := bytes.NewBuffer(make([]byte, 0, docSize)) 55 | if err = binary.Write(docBuffer, binary.LittleEndian, docSize); err != nil { 56 | return 57 | } 58 | 59 | // docSize is the *full* document's size (including the 4-byte size header, 60 | // which has already been read). 61 | if _, err = io.CopyN(docBuffer, dec.source, int64(docSize-4)); err != nil { 62 | return 63 | } 64 | 65 | // Let Unmarshal handle the rest. 66 | defer handleErr(&err) 67 | return Unmarshal(docBuffer.Bytes(), v) 68 | } 69 | 70 | // An Encoder encodes and writes BSON values to an output stream. 71 | type Encoder struct { 72 | target io.Writer 73 | } 74 | 75 | // NewEncoder returns a new Encoder that writes to target. 76 | func NewEncoder(target io.Writer) *Encoder { 77 | return &Encoder{target: target} 78 | } 79 | 80 | // Encode encodes v to BSON, and if successful writes it to the Encoder's output stream. 81 | // See the documentation for Marshal for details about the conversion of Go values to BSON. 82 | func (enc *Encoder) Encode(v interface{}) error { 83 | data, err := Marshal(v) 84 | if err != nil { 85 | return err 86 | } 87 | 88 | _, err = enc.target.Write(data) 89 | return err 90 | } 91 | -------------------------------------------------------------------------------- /bson/stream_test.go: -------------------------------------------------------------------------------- 1 | package bson_test 2 | 3 | import ( 4 | "bytes" 5 | 6 | "github.com/globalsign/mgo/bson" 7 | . "gopkg.in/check.v1" 8 | ) 9 | 10 | var invalidSizeDocuments = [][]byte{ 11 | // Empty document 12 | []byte{}, 13 | // Incomplete header 14 | []byte{0x04}, 15 | // Negative size 16 | []byte{0xff, 0xff, 0xff, 0xff}, 17 | // Full, valid size header but too small (less than 5 bytes) 18 | []byte{0x04, 0x00, 0x00, 0x00}, 19 | // Valid header, valid size but incomplete document 20 | []byte{0xff, 0x00, 0x00, 0x00, 0x00}, 21 | // Too big 22 | []byte{0xff, 0xff, 0xff, 0x7f}, 23 | } 24 | 25 | // Reusing sampleItems from bson_test 26 | 27 | func (s *S) TestEncodeSampleItems(c *C) { 28 | for i, item := range sampleItems { 29 | buf := bytes.NewBuffer(nil) 30 | enc := bson.NewEncoder(buf) 31 | 32 | err := enc.Encode(item.obj) 33 | c.Assert(err, IsNil) 34 | c.Assert(string(buf.Bytes()), Equals, item.data, Commentf("Failed on item %d", i)) 35 | } 36 | } 37 | 38 | func (s *S) TestDecodeSampleItems(c *C) { 39 | for i, item := range sampleItems { 40 | buf := bytes.NewBuffer([]byte(item.data)) 41 | dec := bson.NewDecoder(buf) 42 | 43 | value := bson.M{} 44 | err := dec.Decode(&value) 45 | c.Assert(err, IsNil) 46 | c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d", i)) 47 | } 48 | } 49 | 50 | func (s *S) TestStreamRoundTrip(c *C) { 51 | buf := bytes.NewBuffer(nil) 52 | enc := bson.NewEncoder(buf) 53 | 54 | for _, item := range sampleItems { 55 | err := enc.Encode(item.obj) 56 | c.Assert(err, IsNil) 57 | } 58 | 59 | // Ensure that everything that was encoded is decodable in the same order. 60 | dec := bson.NewDecoder(buf) 61 | for i, item := range sampleItems { 62 | value := bson.M{} 63 | err := dec.Decode(&value) 64 | c.Assert(err, IsNil) 65 | c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d", i)) 66 | } 67 | } 68 | 69 | func (s *S) TestDecodeDocumentTooSmall(c *C) { 70 | for i, item := range invalidSizeDocuments { 71 | buf := bytes.NewBuffer(item) 72 | dec := bson.NewDecoder(buf) 73 | value := bson.M{} 74 | err := dec.Decode(&value) 75 | c.Assert(err, NotNil, Commentf("Failed on invalid size item %d", i)) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /coarse_time.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | // coarseTimeProvider provides a periodically updated (approximate) time value to 10 | // amortise the cost of frequent calls to time.Now. 11 | // 12 | // A read throughput increase of ~6% was measured when using coarseTimeProvider with the 13 | // high-precision event timer (HPET) on FreeBSD 11.1 and Go 1.10.1 after merging 14 | // #116. 15 | // 16 | // Calling Now returns a time.Time that is updated at the configured interval, 17 | // however due to scheduling the value may be marginally older than expected. 18 | // 19 | // coarseTimeProvider is safe for concurrent use. 20 | type coarseTimeProvider struct { 21 | once sync.Once 22 | stop chan struct{} 23 | last atomic.Value 24 | } 25 | 26 | // Now returns the most recently acquired time.Time value. 27 | func (t *coarseTimeProvider) Now() time.Time { 28 | return t.last.Load().(time.Time) 29 | } 30 | 31 | // Close stops the periodic update of t. 32 | // 33 | // Any subsequent calls to Now will return the same value forever. 34 | func (t *coarseTimeProvider) Close() { 35 | t.once.Do(func() { 36 | close(t.stop) 37 | }) 38 | } 39 | 40 | // newcoarseTimeProvider returns a coarseTimeProvider configured to update at granularity. 41 | func newcoarseTimeProvider(granularity time.Duration) *coarseTimeProvider { 42 | t := &coarseTimeProvider{ 43 | stop: make(chan struct{}), 44 | } 45 | 46 | t.last.Store(time.Now()) 47 | 48 | go func() { 49 | ticker := time.NewTicker(granularity) 50 | for { 51 | select { 52 | case <-t.stop: 53 | ticker.Stop() 54 | return 55 | case <-ticker.C: 56 | t.last.Store(time.Now()) 57 | } 58 | } 59 | }() 60 | 61 | return t 62 | } 63 | -------------------------------------------------------------------------------- /coarse_time_test.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestCoarseTimeProvider(t *testing.T) { 9 | t.Parallel() 10 | 11 | const granularity = 50 * time.Millisecond 12 | 13 | ct := newcoarseTimeProvider(granularity) 14 | defer ct.Close() 15 | 16 | start := ct.Now().Unix() 17 | time.Sleep(time.Second) 18 | 19 | got := ct.Now().Unix() 20 | if got <= start { 21 | t.Fatalf("got %d, expected at least %d", got, start) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dbtest/dbserver.go: -------------------------------------------------------------------------------- 1 | package dbtest 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "net" 7 | "os" 8 | "os/exec" 9 | "runtime" 10 | "strconv" 11 | "time" 12 | 13 | mgo "github.com/globalsign/mgo" 14 | "gopkg.in/tomb.v2" 15 | ) 16 | 17 | // DBServer controls a MongoDB server process to be used within test suites. 18 | // 19 | // The test server is started when Session is called the first time and should 20 | // remain running for the duration of all tests, with the Wipe method being 21 | // called between tests (before each of them) to clear stored data. After all tests 22 | // are done, the Stop method should be called to stop the test server. 23 | // 24 | // Before the DBServer is used the SetPath method must be called to define 25 | // the location for the database files to be stored. 26 | type DBServer struct { 27 | session *mgo.Session 28 | output bytes.Buffer 29 | server *exec.Cmd 30 | dbpath string 31 | host string 32 | tomb tomb.Tomb 33 | } 34 | 35 | // SetPath defines the path to the directory where the database files will be 36 | // stored if it is started. The directory path itself is not created or removed 37 | // by the test helper. 38 | func (dbs *DBServer) SetPath(dbpath string) { 39 | dbs.dbpath = dbpath 40 | } 41 | 42 | func (dbs *DBServer) start() { 43 | if dbs.server != nil { 44 | panic("DBServer already started") 45 | } 46 | if dbs.dbpath == "" { 47 | panic("DBServer.SetPath must be called before using the server") 48 | } 49 | mgo.SetStats(true) 50 | l, err := net.Listen("tcp", "127.0.0.1:0") 51 | if err != nil { 52 | panic("unable to listen on a local address: " + err.Error()) 53 | } 54 | addr := l.Addr().(*net.TCPAddr) 55 | l.Close() 56 | dbs.host = addr.String() 57 | 58 | args := []string{ 59 | "--dbpath", dbs.dbpath, 60 | "--bind_ip", "127.0.0.1", 61 | "--port", strconv.Itoa(addr.Port), 62 | "--nssize", "1", 63 | "--noprealloc", 64 | "--smallfiles", 65 | "--nojournal", 66 | } 67 | dbs.tomb = tomb.Tomb{} 68 | dbs.server = exec.Command("mongod", args...) 69 | dbs.server.Stdout = &dbs.output 70 | dbs.server.Stderr = &dbs.output 71 | err = dbs.server.Start() 72 | if err != nil { 73 | // print error to facilitate troubleshooting as the panic will be caught in a panic handler 74 | fmt.Fprintf(os.Stderr, "mongod failed to start: %v\n", err) 75 | panic(err) 76 | } 77 | dbs.tomb.Go(dbs.monitor) 78 | dbs.Wipe() 79 | } 80 | 81 | func (dbs *DBServer) monitor() error { 82 | dbs.server.Process.Wait() 83 | if dbs.tomb.Alive() { 84 | // Present some debugging information. 85 | fmt.Fprintf(os.Stderr, "---- mongod process died unexpectedly:\n") 86 | fmt.Fprintf(os.Stderr, "%s", dbs.output.Bytes()) 87 | fmt.Fprintf(os.Stderr, "---- mongod processes running right now:\n") 88 | cmd := exec.Command("/bin/sh", "-c", "ps auxw | grep mongod") 89 | cmd.Stdout = os.Stderr 90 | cmd.Stderr = os.Stderr 91 | cmd.Run() 92 | fmt.Fprintf(os.Stderr, "----------------------------------------\n") 93 | 94 | panic("mongod process died unexpectedly") 95 | } 96 | return nil 97 | } 98 | 99 | // Stop stops the test server process, if it is running. 100 | // 101 | // It's okay to call Stop multiple times. After the test server is 102 | // stopped it cannot be restarted. 103 | // 104 | // All database sessions must be closed before or while the Stop method 105 | // is running. Otherwise Stop will panic after a timeout informing that 106 | // there is a session leak. 107 | func (dbs *DBServer) Stop() { 108 | if dbs.session != nil { 109 | dbs.checkSessions() 110 | if dbs.session != nil { 111 | dbs.session.Close() 112 | dbs.session = nil 113 | } 114 | } 115 | if dbs.server != nil { 116 | dbs.tomb.Kill(nil) 117 | // Windows doesn't support Interrupt 118 | if runtime.GOOS == "windows" { 119 | dbs.server.Process.Signal(os.Kill) 120 | } else { 121 | dbs.server.Process.Signal(os.Interrupt) 122 | } 123 | select { 124 | case <-dbs.tomb.Dead(): 125 | case <-time.After(5 * time.Second): 126 | panic("timeout waiting for mongod process to die") 127 | } 128 | dbs.server = nil 129 | } 130 | } 131 | 132 | // Session returns a new session to the server. The returned session 133 | // must be closed after the test is done with it. 134 | // 135 | // The first Session obtained from a DBServer will start it. 136 | func (dbs *DBServer) Session() *mgo.Session { 137 | if dbs.server == nil { 138 | dbs.start() 139 | } 140 | if dbs.session == nil { 141 | mgo.ResetStats() 142 | var err error 143 | dbs.session, err = mgo.Dial(dbs.host + "/test") 144 | if err != nil { 145 | panic(err) 146 | } 147 | } 148 | return dbs.session.Copy() 149 | } 150 | 151 | // checkSessions ensures all mgo sessions opened were properly closed. 152 | // For slightly faster tests, it may be disabled setting the 153 | // environment variable CHECK_SESSIONS to 0. 154 | func (dbs *DBServer) checkSessions() { 155 | if check := os.Getenv("CHECK_SESSIONS"); check == "0" || dbs.server == nil || dbs.session == nil { 156 | return 157 | } 158 | dbs.session.Close() 159 | dbs.session = nil 160 | for i := 0; i < 100; i++ { 161 | stats := mgo.GetStats() 162 | if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { 163 | return 164 | } 165 | time.Sleep(100 * time.Millisecond) 166 | } 167 | panic("There are mgo sessions still alive.") 168 | } 169 | 170 | // Wipe drops all created databases and their data. 171 | // 172 | // The MongoDB server remains running if it was prevoiusly running, 173 | // or stopped if it was previously stopped. 174 | // 175 | // All database sessions must be closed before or while the Wipe method 176 | // is running. Otherwise Wipe will panic after a timeout informing that 177 | // there is a session leak. 178 | func (dbs *DBServer) Wipe() { 179 | if dbs.server == nil || dbs.session == nil { 180 | return 181 | } 182 | dbs.checkSessions() 183 | sessionUnset := dbs.session == nil 184 | session := dbs.Session() 185 | defer session.Close() 186 | if sessionUnset { 187 | dbs.session.Close() 188 | dbs.session = nil 189 | } 190 | names, err := session.DatabaseNames() 191 | if err != nil { 192 | panic(err) 193 | } 194 | for _, name := range names { 195 | switch name { 196 | case "admin", "local", "config": 197 | default: 198 | err = session.DB(name).DropDatabase() 199 | if err != nil { 200 | panic(err) 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /dbtest/dbserver_test.go: -------------------------------------------------------------------------------- 1 | package dbtest_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | 8 | mgo "github.com/globalsign/mgo" 9 | . "gopkg.in/check.v1" 10 | 11 | "github.com/globalsign/mgo/dbtest" 12 | ) 13 | 14 | type M map[string]interface{} 15 | 16 | func TestAll(t *testing.T) { 17 | TestingT(t) 18 | } 19 | 20 | type S struct { 21 | oldCheckSessions string 22 | } 23 | 24 | var _ = Suite(&S{}) 25 | 26 | func (s *S) SetUpTest(c *C) { 27 | s.oldCheckSessions = os.Getenv("CHECK_SESSIONS") 28 | os.Setenv("CHECK_SESSIONS", "") 29 | } 30 | 31 | func (s *S) TearDownTest(c *C) { 32 | os.Setenv("CHECK_SESSIONS", s.oldCheckSessions) 33 | } 34 | 35 | func (s *S) TestWipeData(c *C) { 36 | var server dbtest.DBServer 37 | server.SetPath(c.MkDir()) 38 | defer server.Stop() 39 | 40 | session := server.Session() 41 | err := session.DB("mydb").C("mycoll").Insert(M{"a": 1}) 42 | session.Close() 43 | c.Assert(err, IsNil) 44 | 45 | server.Wipe() 46 | 47 | session = server.Session() 48 | names, err := session.DatabaseNames() 49 | session.Close() 50 | c.Assert(err, IsNil) 51 | for _, name := range names { 52 | if name != "local" && name != "admin" { 53 | c.Fatalf("Wipe should have removed this database: %s", name) 54 | } 55 | } 56 | } 57 | 58 | func (s *S) TestStop(c *C) { 59 | var server dbtest.DBServer 60 | server.SetPath(c.MkDir()) 61 | defer server.Stop() 62 | 63 | // Server should not be running. 64 | process := server.ProcessTest() 65 | c.Assert(process, IsNil) 66 | 67 | session := server.Session() 68 | addr := session.LiveServers()[0] 69 | session.Close() 70 | 71 | // Server should be running now. 72 | process = server.ProcessTest() 73 | p, err := os.FindProcess(process.Pid) 74 | c.Assert(err, IsNil) 75 | p.Release() 76 | 77 | server.Stop() 78 | 79 | // Server should not be running anymore. 80 | session, _ = mgo.DialWithTimeout(addr, 500*time.Millisecond) 81 | if session != nil { 82 | session.Close() 83 | c.Fatalf("Stop did not stop the server") 84 | } 85 | } 86 | 87 | func (s *S) TestCheckSessions(c *C) { 88 | var server dbtest.DBServer 89 | server.SetPath(c.MkDir()) 90 | defer server.Stop() 91 | 92 | session := server.Session() 93 | defer session.Close() 94 | c.Assert(server.Wipe, PanicMatches, "There are mgo sessions still alive.") 95 | } 96 | 97 | func (s *S) TestCheckSessionsDisabled(c *C) { 98 | var server dbtest.DBServer 99 | server.SetPath(c.MkDir()) 100 | defer server.Stop() 101 | 102 | os.Setenv("CHECK_SESSIONS", "0") 103 | 104 | // Should not panic, although it looks to Wipe like this session will leak. 105 | session := server.Session() 106 | defer session.Close() 107 | server.Wipe() 108 | } 109 | -------------------------------------------------------------------------------- /dbtest/export_test.go: -------------------------------------------------------------------------------- 1 | package dbtest 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func (dbs *DBServer) ProcessTest() *os.Process { 8 | if dbs.server == nil { 9 | return nil 10 | } 11 | return dbs.server.Process 12 | } 13 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package mgo (pronounced as "mango") offers a rich MongoDB driver for Go. 2 | // 3 | // Detailed documentation of the API is available at GoDoc: 4 | // 5 | // https://godoc.org/github.com/globalsign/mgo 6 | // 7 | // Usage of the driver revolves around the concept of sessions. To 8 | // get started, obtain a session using the Dial function: 9 | // 10 | // session, err := mgo.Dial(url) 11 | // 12 | // This will establish one or more connections with the cluster of 13 | // servers defined by the url parameter. From then on, the cluster 14 | // may be queried with multiple consistency rules (see SetMode) and 15 | // documents retrieved with statements such as: 16 | // 17 | // c := session.DB(database).C(collection) 18 | // err := c.Find(query).One(&result) 19 | // 20 | // New sessions are typically created by calling session.Copy on the 21 | // initial session obtained at dial time. These new sessions will share 22 | // the same cluster information and connection pool, and may be easily 23 | // handed into other methods and functions for organizing logic. 24 | // Every session created must have its Close method called at the end 25 | // of its life time, so its resources may be put back in the pool or 26 | // collected, depending on the case. 27 | // 28 | // There is a sub-package that provides support for BSON, which can be 29 | // used by itself as well: 30 | // 31 | // https://godoc.org/github.com/globalsign/mgo/bson 32 | // 33 | // For more details, see the documentation for the types and methods. 34 | // 35 | package mgo 36 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "io/ioutil" 7 | "net" 8 | "sync" 9 | ) 10 | 11 | func ExampleCredential_x509Authentication() { 12 | // MongoDB follows RFC2253 for the ordering of the DN - if the order is 13 | // incorrect when creating the user in Mongo, the client will not be able to 14 | // connect. 15 | // 16 | // The best way to generate the DN with the correct ordering is with 17 | // openssl: 18 | // 19 | // openssl x509 -in client.crt -inform PEM -noout -subject -nameopt RFC2253 20 | // subject= CN=Example App,OU=MongoDB Client Authentication,O=GlobalSign,C=GB 21 | // 22 | // 23 | // And then create the user in MongoDB with the above DN: 24 | // 25 | // db.getSiblingDB("$external").runCommand({ 26 | // createUser: "CN=Example App,OU=MongoDB Client Authentication,O=GlobalSign,C=GB", 27 | // roles: [ 28 | // { role: 'readWrite', db: 'bananas' }, 29 | // { role: 'userAdminAnyDatabase', db: 'admin' } 30 | // ], 31 | // writeConcern: { w: "majority" , wtimeout: 5000 } 32 | // }) 33 | // 34 | // 35 | // References: 36 | // - https://docs.mongodb.com/manual/tutorial/configure-x509-client-authentication/ 37 | // - https://docs.mongodb.com/manual/core/security-x.509/ 38 | // 39 | 40 | // Read in the PEM encoded X509 certificate. 41 | // 42 | // See the client.pem file at the path below. 43 | clientCertPEM, err := ioutil.ReadFile("harness/certs/client.pem") 44 | 45 | // Read in the PEM encoded private key. 46 | clientKeyPEM, err := ioutil.ReadFile("harness/certs/client.key") 47 | 48 | // Parse the private key, and the public key contained within the 49 | // certificate. 50 | clientCert, err := tls.X509KeyPair(clientCertPEM, clientKeyPEM) 51 | 52 | // Parse the actual certificate data 53 | clientCert.Leaf, err = x509.ParseCertificate(clientCert.Certificate[0]) 54 | 55 | // Use the cert to set up a TLS connection to Mongo 56 | tlsConfig := &tls.Config{ 57 | Certificates: []tls.Certificate{clientCert}, 58 | 59 | // This is set to true so the example works within the test 60 | // environment. 61 | // 62 | // DO NOT set InsecureSkipVerify to true in a production 63 | // environment - if you use an untrusted CA/have your own, load 64 | // its certificate into the RootCAs value instead. 65 | // 66 | // RootCAs: myCAChain, 67 | InsecureSkipVerify: true, 68 | } 69 | 70 | // Connect to Mongo using TLS 71 | host := "localhost:40003" 72 | session, err := DialWithInfo(&DialInfo{ 73 | Addrs: []string{host}, 74 | DialServer: func(addr *ServerAddr) (net.Conn, error) { 75 | return tls.Dial("tcp", host, tlsConfig) 76 | }, 77 | }) 78 | 79 | // Authenticate using the certificate 80 | cred := &Credential{Certificate: tlsConfig.Certificates[0].Leaf} 81 | if err := session.Login(cred); err != nil { 82 | panic(err) 83 | } 84 | 85 | // Done! Use mgo as normal from here. 86 | // 87 | // You should actually check the error code at each step. 88 | _ = err 89 | } 90 | 91 | func ExampleSession_concurrency() { 92 | // This example shows the best practise for concurrent use of a mgo session. 93 | // 94 | // Internally mgo maintains a connection pool, dialling new connections as 95 | // required. 96 | // 97 | // Some general suggestions: 98 | // - Define a struct holding the original session, database name and 99 | // collection name instead of passing them explicitly. 100 | // - Define an interface abstracting your data access instead of exposing 101 | // mgo to your application code directly. 102 | // - Limit concurrency at the application level, not with SetPoolLimit(). 103 | 104 | // This will be our concurrent worker 105 | var doStuff = func(wg *sync.WaitGroup, session *Session) { 106 | defer wg.Done() 107 | 108 | // Copy the session - if needed this will dial a new connection which 109 | // can later be reused. 110 | // 111 | // Calling close returns the connection to the pool. 112 | conn := session.Copy() 113 | defer conn.Close() 114 | 115 | // Do something(s) with the connection 116 | _, _ = conn.DB("").C("my_data").Count() 117 | } 118 | 119 | /////////////////////////////////////////////// 120 | 121 | // Dial a connection to Mongo - this creates the connection pool 122 | session, err := Dial("localhost:40003/my_database") 123 | if err != nil { 124 | panic(err) 125 | } 126 | 127 | // Concurrently do things, passing the session to the worker 128 | wg := &sync.WaitGroup{} 129 | for i := 0; i < 10; i++ { 130 | wg.Add(1) 131 | go doStuff(wg, session) 132 | } 133 | wg.Wait() 134 | 135 | session.Close() 136 | } 137 | 138 | func ExampleDial_usingSSL() { 139 | // To connect via TLS/SSL (enforced for MongoDB Atlas for example) requires 140 | // to set the ssl query param to true. 141 | url := "mongodb://localhost:40003?ssl=true" 142 | 143 | session, err := Dial(url) 144 | if err != nil { 145 | panic(err) 146 | } 147 | 148 | // Use session as normal 149 | session.Close() 150 | } 151 | 152 | func ExampleDial_tlsConfig() { 153 | // You can define a custom tlsConfig, this one enables TLS, like if you have 154 | // ssl=true in the connection string. 155 | url := "mongodb://localhost:40003" 156 | 157 | tlsConfig := &tls.Config{ 158 | // This can be configured to use a private root CA - see the Credential 159 | // x509 Authentication example. 160 | // 161 | // Please don't set InsecureSkipVerify to true - it makes using TLS 162 | // pointless and is never the right answer! 163 | } 164 | 165 | dialInfo, err := ParseURL(url) 166 | dialInfo.DialServer = func(addr *ServerAddr) (net.Conn, error) { 167 | return tls.Dial("tcp", addr.String(), tlsConfig) 168 | } 169 | 170 | session, err := DialWithInfo(dialInfo) 171 | if err != nil { 172 | panic(err) 173 | } 174 | 175 | // Use session as normal 176 | session.Close() 177 | } 178 | -------------------------------------------------------------------------------- /export_test.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | func HackPingDelay(newDelay time.Duration) (restore func()) { 9 | globalMutex.Lock() 10 | defer globalMutex.Unlock() 11 | 12 | oldDelay := pingDelay 13 | restore = func() { 14 | globalMutex.Lock() 15 | pingDelay = oldDelay 16 | globalMutex.Unlock() 17 | } 18 | pingDelay = newDelay 19 | return 20 | } 21 | 22 | func (s *Session) Cluster() *mongoCluster { 23 | return s.cluster() 24 | } 25 | 26 | func (cluster *mongoCluster) Server(addr string) *mongoServer { 27 | tcpaddr, err := net.ResolveTCPAddr("tcp", addr) 28 | if err != nil { 29 | panic(err) 30 | } 31 | return cluster.server(addr, tcpaddr) 32 | } 33 | -------------------------------------------------------------------------------- /harness/certs/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLjCCAhYCAQcwDQYJKoZIhvcNAQELBQAwXDELMAkGA1UEBhMCR08xDDAKBgNV 3 | BAgMA01HTzEMMAoGA1UEBwwDTUdPMQwwCgYDVQQKDANNR08xDzANBgNVBAsMBlNl 4 | cnZlcjESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE1MDkyOTA4NDAzMFoYDzIxMTUw 5 | OTA1MDg0MDMwWjBcMQswCQYDVQQGEwJHTzEMMAoGA1UECAwDTUdPMQwwCgYDVQQH 6 | DANNR08xDDAKBgNVBAoMA01HTzEPMA0GA1UECwwGQ2xpZW50MRIwEAYDVQQDDAls 7 | b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0UiQhmT+H 8 | 4IIqrn8SMESDzvcl3rwImwUoRIHlmXkovCIZCbvBCJ1nAu6X5zIN89EPPOjfNrgZ 9 | 616wPgVV/YEQXp+D7+jTAsE5s8JepRXFdecResmvh/+0i2DSuI4QFsuyVAPM1O0I 10 | AQ5EKgr0weZZmsX6lhPD4uYehV4DxDE0i/8aTAlDoNgRCAJrYFMharRTDdY7bQzd 11 | 7ZYab/pK/3DSmOKxl/AFJ8Enmcj9w1bsvy0fgAgoGEBnBru80PRFpFiqk72TJkXO 12 | Hx7zcYFpegtKPbAreTCModaCnjP//fskCp4XJrkfH5+01NeeX/r1OfEbjgE/wzzx 13 | l8NaWnPCmxNfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFwYpje3dCLDOIHYjd+5 14 | CpFOEb+bJsS4ryqm/NblTjIhCLo58hNpMsBqdJHRbHAFRCOE8fvY8yiWtdHeFZcW 15 | DgVRAXfHONLtN7faZaZQnhy/YzOhLfC/8dUMB0gQA8KXhBCPZqQmexE28AfkEO47 16 | PwICAxIWINfjm5VnFMkA3b7bDNLHon/pev2m7HqVQ3pRUJQNK3XgFOdDgRrnuXpR 17 | OKAfHORHVGTh1gf1DVwc0oM+0gnkSiJ1VG0n5pE3zhZ24fmZxu6JQ6X515W7APQI 18 | /nKVH+f1Fo+ustyTNLt8Bwxi1XmwT7IXwnkVSE9Ff6VejppXRF01V0aaWsa3kU3r 19 | z3A= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /harness/certs/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAtFIkIZk/h+CCKq5/EjBEg873Jd68CJsFKESB5Zl5KLwiGQm7 3 | wQidZwLul+cyDfPRDzzo3za4GetesD4FVf2BEF6fg+/o0wLBObPCXqUVxXXnEXrJ 4 | r4f/tItg0riOEBbLslQDzNTtCAEORCoK9MHmWZrF+pYTw+LmHoVeA8QxNIv/GkwJ 5 | Q6DYEQgCa2BTIWq0Uw3WO20M3e2WGm/6Sv9w0pjisZfwBSfBJ5nI/cNW7L8tH4AI 6 | KBhAZwa7vND0RaRYqpO9kyZFzh8e83GBaXoLSj2wK3kwjKHWgp4z//37JAqeFya5 7 | Hx+ftNTXnl/69TnxG44BP8M88ZfDWlpzwpsTXwIDAQABAoIBADzCjOAxZkHfuZyu 8 | La0wTHXpkEfXdJ6ltagq5WY7P6MlOYwcRoK152vlhgXzZl9jL6ely4YjRwec0swq 9 | KdwezpV4fOGVPmuTuw45bx47HEnr/49ZQ4p9FgF9EYQPofbz53FQc/NaMACJcogv 10 | bn+osniw+VMFrOVNmGLiZ5p3Smk8zfXE7GRHO8CL5hpWLWO/aK236yytbfWOjM2f 11 | Pr76ICb26TPRNzYaYUEThU6DtgdLU8pLnJ6QKKaDsjn+zqQzRa+Nvc0c0K8gvWwA 12 | Afq7t0325+uMSwfpLgCOFldcaZQ5uvteJ0CAVRq1MvStnSHBmMzPlgS+NzsDm6lp 13 | QH5+rIkCgYEA5j3jrWsv7TueTNbk8Hr/Zwywc+fA2Ex0pBURBHlHyc6ahSXWSCqo 14 | DtvRGX0GDoK1lCfaIf1qb/DLlGaoHpkEeqcNhXQ+hHs+bZAxfbfBY9+ikit5ZTtl 15 | QN1tIlhaiyLDnwhkpi/hMw1tiouxJUf84Io61z0sCL4hyZSPCpjn0H0CgYEAyH6F 16 | Mwl+bCD3VDL/Dr5WSoOr2B/M3bF5SfvdStwy2IPcDJ716je1Ud/2qFCnKGgqvWhJ 17 | +HU15c7CjAWo7/pXq2/pEMD8fDKTYww4Hr4p6duEA7DpbOGkwcUX8u3eknxUWT9F 18 | jOSbTCvAxuDOC1K3AElyMxVVTNUrFFe8M84R9gsCgYBXmb6RkdG3WlKde7m5gaLB 19 | K4PLZabq5RQQBe/mmtpkfxYtiLrh1FEC7kG9h+MRDExX5V3KRugDVUOv3+shUSjy 20 | HbM4ToUm1NloyE78PTj4bfMl2CKlEJcyucy3H5S7kWuKi5/31wnA6d/+sa2huKUP 21 | Lai7kgu5+9VRJBPUfV7d5QKBgCnhk/13TDtWH5QtGu5/gBMMskbxTaA5xHZZ8H4E 22 | xXJJCRxx0Dje7jduK145itF8AQGT2W/XPC0HJciOHh4TE2EyfWMMjTF8dyFHmimB 23 | 28uIGWmT+Q7Pi9UWUMxkOAwtgIksGGE4F+CvexOQPjpLSwL6VKqrGCh2lwsm0J+Z 24 | ulLFAoGAKlC93c6XEj1A31c1+usdEhUe9BrmTqtSYLYpDNpeMLdZ3VctrAZuOQPZ 25 | 4A4gkkQkqqwZGBYYSEqwqiLU6MsBdHPPZ9u3JXLLOQuh1xGeaKylvHj7qx6iT0Xo 26 | I+FkJ6/3JeMgOina/+wlzD4oyQpqR4Mnh+TuLkDfQTgY+Lg0WPk= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /harness/certs/client.pem: -------------------------------------------------------------------------------- 1 | To regenerate the key: 2 | 3 | openssl req -newkey rsa:2048 -new -x509 -days 36500 -nodes -out server.crt -keyout server.key 4 | cat server.key server.crt > server.pem 5 | openssl genrsa -out client.key 2048 6 | openssl req -key client.key -new -out client.req 7 | openssl x509 -req -in client.req -CA server.crt -CAkey server.key -days 36500 -CAserial file.srl -out client.crt 8 | cat client.key client.crt > client.pem 9 | 10 | -----BEGIN RSA PRIVATE KEY----- 11 | MIIEogIBAAKCAQEAtFIkIZk/h+CCKq5/EjBEg873Jd68CJsFKESB5Zl5KLwiGQm7 12 | wQidZwLul+cyDfPRDzzo3za4GetesD4FVf2BEF6fg+/o0wLBObPCXqUVxXXnEXrJ 13 | r4f/tItg0riOEBbLslQDzNTtCAEORCoK9MHmWZrF+pYTw+LmHoVeA8QxNIv/GkwJ 14 | Q6DYEQgCa2BTIWq0Uw3WO20M3e2WGm/6Sv9w0pjisZfwBSfBJ5nI/cNW7L8tH4AI 15 | KBhAZwa7vND0RaRYqpO9kyZFzh8e83GBaXoLSj2wK3kwjKHWgp4z//37JAqeFya5 16 | Hx+ftNTXnl/69TnxG44BP8M88ZfDWlpzwpsTXwIDAQABAoIBADzCjOAxZkHfuZyu 17 | La0wTHXpkEfXdJ6ltagq5WY7P6MlOYwcRoK152vlhgXzZl9jL6ely4YjRwec0swq 18 | KdwezpV4fOGVPmuTuw45bx47HEnr/49ZQ4p9FgF9EYQPofbz53FQc/NaMACJcogv 19 | bn+osniw+VMFrOVNmGLiZ5p3Smk8zfXE7GRHO8CL5hpWLWO/aK236yytbfWOjM2f 20 | Pr76ICb26TPRNzYaYUEThU6DtgdLU8pLnJ6QKKaDsjn+zqQzRa+Nvc0c0K8gvWwA 21 | Afq7t0325+uMSwfpLgCOFldcaZQ5uvteJ0CAVRq1MvStnSHBmMzPlgS+NzsDm6lp 22 | QH5+rIkCgYEA5j3jrWsv7TueTNbk8Hr/Zwywc+fA2Ex0pBURBHlHyc6ahSXWSCqo 23 | DtvRGX0GDoK1lCfaIf1qb/DLlGaoHpkEeqcNhXQ+hHs+bZAxfbfBY9+ikit5ZTtl 24 | QN1tIlhaiyLDnwhkpi/hMw1tiouxJUf84Io61z0sCL4hyZSPCpjn0H0CgYEAyH6F 25 | Mwl+bCD3VDL/Dr5WSoOr2B/M3bF5SfvdStwy2IPcDJ716je1Ud/2qFCnKGgqvWhJ 26 | +HU15c7CjAWo7/pXq2/pEMD8fDKTYww4Hr4p6duEA7DpbOGkwcUX8u3eknxUWT9F 27 | jOSbTCvAxuDOC1K3AElyMxVVTNUrFFe8M84R9gsCgYBXmb6RkdG3WlKde7m5gaLB 28 | K4PLZabq5RQQBe/mmtpkfxYtiLrh1FEC7kG9h+MRDExX5V3KRugDVUOv3+shUSjy 29 | HbM4ToUm1NloyE78PTj4bfMl2CKlEJcyucy3H5S7kWuKi5/31wnA6d/+sa2huKUP 30 | Lai7kgu5+9VRJBPUfV7d5QKBgCnhk/13TDtWH5QtGu5/gBMMskbxTaA5xHZZ8H4E 31 | xXJJCRxx0Dje7jduK145itF8AQGT2W/XPC0HJciOHh4TE2EyfWMMjTF8dyFHmimB 32 | 28uIGWmT+Q7Pi9UWUMxkOAwtgIksGGE4F+CvexOQPjpLSwL6VKqrGCh2lwsm0J+Z 33 | ulLFAoGAKlC93c6XEj1A31c1+usdEhUe9BrmTqtSYLYpDNpeMLdZ3VctrAZuOQPZ 34 | 4A4gkkQkqqwZGBYYSEqwqiLU6MsBdHPPZ9u3JXLLOQuh1xGeaKylvHj7qx6iT0Xo 35 | I+FkJ6/3JeMgOina/+wlzD4oyQpqR4Mnh+TuLkDfQTgY+Lg0WPk= 36 | -----END RSA PRIVATE KEY----- 37 | -----BEGIN CERTIFICATE----- 38 | MIIDLjCCAhYCAQcwDQYJKoZIhvcNAQELBQAwXDELMAkGA1UEBhMCR08xDDAKBgNV 39 | BAgMA01HTzEMMAoGA1UEBwwDTUdPMQwwCgYDVQQKDANNR08xDzANBgNVBAsMBlNl 40 | cnZlcjESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE1MDkyOTA4NDAzMFoYDzIxMTUw 41 | OTA1MDg0MDMwWjBcMQswCQYDVQQGEwJHTzEMMAoGA1UECAwDTUdPMQwwCgYDVQQH 42 | DANNR08xDDAKBgNVBAoMA01HTzEPMA0GA1UECwwGQ2xpZW50MRIwEAYDVQQDDAls 43 | b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0UiQhmT+H 44 | 4IIqrn8SMESDzvcl3rwImwUoRIHlmXkovCIZCbvBCJ1nAu6X5zIN89EPPOjfNrgZ 45 | 616wPgVV/YEQXp+D7+jTAsE5s8JepRXFdecResmvh/+0i2DSuI4QFsuyVAPM1O0I 46 | AQ5EKgr0weZZmsX6lhPD4uYehV4DxDE0i/8aTAlDoNgRCAJrYFMharRTDdY7bQzd 47 | 7ZYab/pK/3DSmOKxl/AFJ8Enmcj9w1bsvy0fgAgoGEBnBru80PRFpFiqk72TJkXO 48 | Hx7zcYFpegtKPbAreTCModaCnjP//fskCp4XJrkfH5+01NeeX/r1OfEbjgE/wzzx 49 | l8NaWnPCmxNfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFwYpje3dCLDOIHYjd+5 50 | CpFOEb+bJsS4ryqm/NblTjIhCLo58hNpMsBqdJHRbHAFRCOE8fvY8yiWtdHeFZcW 51 | DgVRAXfHONLtN7faZaZQnhy/YzOhLfC/8dUMB0gQA8KXhBCPZqQmexE28AfkEO47 52 | PwICAxIWINfjm5VnFMkA3b7bDNLHon/pev2m7HqVQ3pRUJQNK3XgFOdDgRrnuXpR 53 | OKAfHORHVGTh1gf1DVwc0oM+0gnkSiJ1VG0n5pE3zhZ24fmZxu6JQ6X515W7APQI 54 | /nKVH+f1Fo+ustyTNLt8Bwxi1XmwT7IXwnkVSE9Ff6VejppXRF01V0aaWsa3kU3r 55 | z3A= 56 | -----END CERTIFICATE----- 57 | 58 | -------------------------------------------------------------------------------- /harness/certs/client.req: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICoTCCAYkCAQAwXDELMAkGA1UEBhMCR08xDDAKBgNVBAgMA01HTzEMMAoGA1UE 3 | BwwDTUdPMQwwCgYDVQQKDANNR08xDzANBgNVBAsMBkNsaWVudDESMBAGA1UEAwwJ 4 | bG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFIkIZk/ 5 | h+CCKq5/EjBEg873Jd68CJsFKESB5Zl5KLwiGQm7wQidZwLul+cyDfPRDzzo3za4 6 | GetesD4FVf2BEF6fg+/o0wLBObPCXqUVxXXnEXrJr4f/tItg0riOEBbLslQDzNTt 7 | CAEORCoK9MHmWZrF+pYTw+LmHoVeA8QxNIv/GkwJQ6DYEQgCa2BTIWq0Uw3WO20M 8 | 3e2WGm/6Sv9w0pjisZfwBSfBJ5nI/cNW7L8tH4AIKBhAZwa7vND0RaRYqpO9kyZF 9 | zh8e83GBaXoLSj2wK3kwjKHWgp4z//37JAqeFya5Hx+ftNTXnl/69TnxG44BP8M8 10 | 8ZfDWlpzwpsTXwIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAKbOFblIscxlXalV 11 | sEGNm2oz380RN2QoLhN6nKtAiv0jWm6iKhdAhOIQIeaRPhUP3cyi8bcBvLdMeQ3d 12 | ZYIByB55/R0VSP1vs4qkXJCQegHcpMpyuIzsMV8p3Q4lxzGKyKtPA6Bb5c49p8Sk 13 | ncD+LL4ymrMEia4cBPsHL9hhFOm4gqDacbU8+ETLTpuoSvUZiw7OwngqhE2r+kMv 14 | KDweq5TOPeb+ftKzQKrrfB+XVdBoTKYw6CwARpogbc0/7mvottVcJ/0yAgC1fBbM 15 | vupkohkXwKfjxKl6nKNL3R2GkzHQOh91hglAx5zyybKQn2YMM328Vk4X6csBg+pg 16 | tb1s0MA= 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /harness/certs/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDjTCCAnWgAwIBAgIJAMW+wDfcdzC+MA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV 3 | BAYTAkdPMQwwCgYDVQQIDANNR08xDDAKBgNVBAcMA01HTzEMMAoGA1UECgwDTUdP 4 | MQ8wDQYDVQQLDAZTZXJ2ZXIxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xNTA5Mjkw 5 | ODM0MTBaGA8yMTE1MDkwNTA4MzQxMFowXDELMAkGA1UEBhMCR08xDDAKBgNVBAgM 6 | A01HTzEMMAoGA1UEBwwDTUdPMQwwCgYDVQQKDANNR08xDzANBgNVBAsMBlNlcnZl 7 | cjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 8 | CgKCAQEA/T5W1vTsAF+2gTXP1JKygjM7T/2BXHiJc6DRKVjlshTtPYuC3rpTddDm 9 | 6d86d17LWEo+T2bCT4MzZJhSGAun9peFvehdElRMr57xs7j5V1QYjwadMTBkLQuK 10 | IAg6cISN1KPUzpUTUKsWIsbx97sA0t0wiEPifROb7nfSMIVQsdz/c9LlY2UNYI+5 11 | GiU88iDGg2wrdsa3U+l2G2KSx/9uE3c5iFki6bdequLiWmBZ6rxfoaLe4gk1INji 12 | fKssNsn2i3uJ4i4Tmr3PUc4kxx0mMKuWK3HdlQsMqtpq++HQmHSvsPrbgcjl9HyP 13 | JiHDsoJ+4O5bbtcE51oQbLh1bZAhYwIDAQABo1AwTjAdBgNVHQ4EFgQUhku/u9Kd 14 | OAc1L0OR649vCCuQT+0wHwYDVR0jBBgwFoAUhku/u9KdOAc1L0OR649vCCuQT+0w 15 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAw7Bgw3hlWXWSZjLhnSOu 16 | 2mW/UJ2Sj31unHngmgtXwW/04cyzoULb+qmzPe/Z06QMgGIsku1jFBcu0JabQtUG 17 | TyalpfW77tfnvz238CYdImYwE9ZcIGuZGfhs6ySFN9XpW43B8YM7R8wTNPvOcSPw 18 | nfjqU6kueN4TTspQg9cKhDss5DcMTIdgJgLbITXhIsrCu6GlKOgtX3HrdMGpQX7s 19 | UoMXtZVG8pK32vxKWGTZ6DPqESeKjjq74NbYnB3H5U/kDU2dt7LF90C/Umdr9y+C 20 | W2OJb1WBrf6RTcbt8D6d7P9kOfLPOtyn/cbaA/pfXBMQMHqr7XNXzjnaNU+jB7hL 21 | yQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /harness/certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD9PlbW9OwAX7aB 3 | Nc/UkrKCMztP/YFceIlzoNEpWOWyFO09i4LeulN10Obp3zp3XstYSj5PZsJPgzNk 4 | mFIYC6f2l4W96F0SVEyvnvGzuPlXVBiPBp0xMGQtC4ogCDpwhI3Uo9TOlRNQqxYi 5 | xvH3uwDS3TCIQ+J9E5vud9IwhVCx3P9z0uVjZQ1gj7kaJTzyIMaDbCt2xrdT6XYb 6 | YpLH/24TdzmIWSLpt16q4uJaYFnqvF+hot7iCTUg2OJ8qyw2yfaLe4niLhOavc9R 7 | ziTHHSYwq5Yrcd2VCwyq2mr74dCYdK+w+tuByOX0fI8mIcOygn7g7ltu1wTnWhBs 8 | uHVtkCFjAgMBAAECggEASRAfRc1L+Z+jrAu2doIMdnwJdL6S//bW0UFolyFKw+I9 9 | wC/sBg6D3c3zkS4SVDZJPKPO7mGbVg1oWnGH3eAfCYoV0ACmOY+QwGp/GXcYmRVu 10 | MHWcDIEFpelaZHt7QNM9iEfsMd3YwMFblZUIYozVZADk66uKQMPTjS2Muur7qRSi 11 | wuVfSmsVZ5afH3B1Tr96BbmPsHrXLjvNpjO44k2wrnnSPQjUL7+YiZPvtnNW8Fby 12 | yuo2uoAyjg3+68PYZftOvvNneMsv1uyGlUs6Bk+DVWaqofIztWFdFZyXbHnK2PTk 13 | eGQt5EsL+RwIck5eoqd5vSE+KyzhhydL0zcpngVQoQKBgQD/Yelvholbz5NQtSy3 14 | ZoiW1y7hL1BKzvVNHuAMKJ5WOnj5szhjhKxt/wZ+hk0qcAmlV9WAPbf4izbEwPRC 15 | tnMBQzf1uBxqqbLL6WZ4YAyGrcX3UrT7GXsGfVT4zJjz7oYSw8aPircecw5V4exB 16 | xa4NF+ki8IycXSkHwvW2R56fRwKBgQD92xpxXtte/rUnmENbQmr0aKg7JEfMoih6 17 | MdX+f6mfgjMmqj+L4jPTI8/ql8HEy13SQS1534aDSHO+nBqBK5aHUCRMIgSLnTP9 18 | Xyx9Ngg03SZIkPfykqxQmnZgWkTPMhYS+K1Ao9FGVs8W5jVi7veyAdhHptAcxhP3 19 | IuxvrxVTBQKBgQCluMPiu0snaOwP04HRAZhhSgIB3tIbuXE1OnPpb/JPwmH+p25Q 20 | Jig+uN9d+4jXoRyhTv4c2fAoOS6xPwVCxWKbzyLhMTg/fx+ncy4rryhxvRJaDDGl 21 | QEO1Ul9xlFMs9/vI8YJIY5uxBrimwpStmbn4hSukoLSeQ1X802bfglpMwQKBgD8z 22 | GTY4Y20XBIrDAaHquy32EEwJEEcF6AXj+l7N8bDgfVOW9xMgUb6zH8RL29Xeu5Do 23 | 4SWCXL66fvZpbr/R1jwB28eIgJExpgvicfUKSqi+lhVi4hfmJDg8/FOopZDf61b1 24 | ykxZfHSCkDQnRAtJaylKBEpyYUWImtfgPfTgJfLxAoGAc8A/Tl2h/DsdTA+cA5d7 25 | 1e0l64m13ObruSWRczyru4hy8Yq6E/K2rOFw8cYCcFpy24NqNlk+2iXPLRpWm2zt 26 | 9R497zAPvhK/bfPXjvm0j/VjB44lvRTC9hby/RRMHy9UJk4o/UQaD+1IodxZovvk 27 | SruEA1+5bfBRMW0P+h7Qfe4= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /harness/certs/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD9PlbW9OwAX7aB 3 | Nc/UkrKCMztP/YFceIlzoNEpWOWyFO09i4LeulN10Obp3zp3XstYSj5PZsJPgzNk 4 | mFIYC6f2l4W96F0SVEyvnvGzuPlXVBiPBp0xMGQtC4ogCDpwhI3Uo9TOlRNQqxYi 5 | xvH3uwDS3TCIQ+J9E5vud9IwhVCx3P9z0uVjZQ1gj7kaJTzyIMaDbCt2xrdT6XYb 6 | YpLH/24TdzmIWSLpt16q4uJaYFnqvF+hot7iCTUg2OJ8qyw2yfaLe4niLhOavc9R 7 | ziTHHSYwq5Yrcd2VCwyq2mr74dCYdK+w+tuByOX0fI8mIcOygn7g7ltu1wTnWhBs 8 | uHVtkCFjAgMBAAECggEASRAfRc1L+Z+jrAu2doIMdnwJdL6S//bW0UFolyFKw+I9 9 | wC/sBg6D3c3zkS4SVDZJPKPO7mGbVg1oWnGH3eAfCYoV0ACmOY+QwGp/GXcYmRVu 10 | MHWcDIEFpelaZHt7QNM9iEfsMd3YwMFblZUIYozVZADk66uKQMPTjS2Muur7qRSi 11 | wuVfSmsVZ5afH3B1Tr96BbmPsHrXLjvNpjO44k2wrnnSPQjUL7+YiZPvtnNW8Fby 12 | yuo2uoAyjg3+68PYZftOvvNneMsv1uyGlUs6Bk+DVWaqofIztWFdFZyXbHnK2PTk 13 | eGQt5EsL+RwIck5eoqd5vSE+KyzhhydL0zcpngVQoQKBgQD/Yelvholbz5NQtSy3 14 | ZoiW1y7hL1BKzvVNHuAMKJ5WOnj5szhjhKxt/wZ+hk0qcAmlV9WAPbf4izbEwPRC 15 | tnMBQzf1uBxqqbLL6WZ4YAyGrcX3UrT7GXsGfVT4zJjz7oYSw8aPircecw5V4exB 16 | xa4NF+ki8IycXSkHwvW2R56fRwKBgQD92xpxXtte/rUnmENbQmr0aKg7JEfMoih6 17 | MdX+f6mfgjMmqj+L4jPTI8/ql8HEy13SQS1534aDSHO+nBqBK5aHUCRMIgSLnTP9 18 | Xyx9Ngg03SZIkPfykqxQmnZgWkTPMhYS+K1Ao9FGVs8W5jVi7veyAdhHptAcxhP3 19 | IuxvrxVTBQKBgQCluMPiu0snaOwP04HRAZhhSgIB3tIbuXE1OnPpb/JPwmH+p25Q 20 | Jig+uN9d+4jXoRyhTv4c2fAoOS6xPwVCxWKbzyLhMTg/fx+ncy4rryhxvRJaDDGl 21 | QEO1Ul9xlFMs9/vI8YJIY5uxBrimwpStmbn4hSukoLSeQ1X802bfglpMwQKBgD8z 22 | GTY4Y20XBIrDAaHquy32EEwJEEcF6AXj+l7N8bDgfVOW9xMgUb6zH8RL29Xeu5Do 23 | 4SWCXL66fvZpbr/R1jwB28eIgJExpgvicfUKSqi+lhVi4hfmJDg8/FOopZDf61b1 24 | ykxZfHSCkDQnRAtJaylKBEpyYUWImtfgPfTgJfLxAoGAc8A/Tl2h/DsdTA+cA5d7 25 | 1e0l64m13ObruSWRczyru4hy8Yq6E/K2rOFw8cYCcFpy24NqNlk+2iXPLRpWm2zt 26 | 9R497zAPvhK/bfPXjvm0j/VjB44lvRTC9hby/RRMHy9UJk4o/UQaD+1IodxZovvk 27 | SruEA1+5bfBRMW0P+h7Qfe4= 28 | -----END PRIVATE KEY----- 29 | -----BEGIN CERTIFICATE----- 30 | MIIDjTCCAnWgAwIBAgIJAMW+wDfcdzC+MA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV 31 | BAYTAkdPMQwwCgYDVQQIDANNR08xDDAKBgNVBAcMA01HTzEMMAoGA1UECgwDTUdP 32 | MQ8wDQYDVQQLDAZTZXJ2ZXIxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xNTA5Mjkw 33 | ODM0MTBaGA8yMTE1MDkwNTA4MzQxMFowXDELMAkGA1UEBhMCR08xDDAKBgNVBAgM 34 | A01HTzEMMAoGA1UEBwwDTUdPMQwwCgYDVQQKDANNR08xDzANBgNVBAsMBlNlcnZl 35 | cjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 36 | CgKCAQEA/T5W1vTsAF+2gTXP1JKygjM7T/2BXHiJc6DRKVjlshTtPYuC3rpTddDm 37 | 6d86d17LWEo+T2bCT4MzZJhSGAun9peFvehdElRMr57xs7j5V1QYjwadMTBkLQuK 38 | IAg6cISN1KPUzpUTUKsWIsbx97sA0t0wiEPifROb7nfSMIVQsdz/c9LlY2UNYI+5 39 | GiU88iDGg2wrdsa3U+l2G2KSx/9uE3c5iFki6bdequLiWmBZ6rxfoaLe4gk1INji 40 | fKssNsn2i3uJ4i4Tmr3PUc4kxx0mMKuWK3HdlQsMqtpq++HQmHSvsPrbgcjl9HyP 41 | JiHDsoJ+4O5bbtcE51oQbLh1bZAhYwIDAQABo1AwTjAdBgNVHQ4EFgQUhku/u9Kd 42 | OAc1L0OR649vCCuQT+0wHwYDVR0jBBgwFoAUhku/u9KdOAc1L0OR649vCCuQT+0w 43 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAw7Bgw3hlWXWSZjLhnSOu 44 | 2mW/UJ2Sj31unHngmgtXwW/04cyzoULb+qmzPe/Z06QMgGIsku1jFBcu0JabQtUG 45 | TyalpfW77tfnvz238CYdImYwE9ZcIGuZGfhs6ySFN9XpW43B8YM7R8wTNPvOcSPw 46 | nfjqU6kueN4TTspQg9cKhDss5DcMTIdgJgLbITXhIsrCu6GlKOgtX3HrdMGpQX7s 47 | UoMXtZVG8pK32vxKWGTZ6DPqESeKjjq74NbYnB3H5U/kDU2dt7LF90C/Umdr9y+C 48 | W2OJb1WBrf6RTcbt8D6d7P9kOfLPOtyn/cbaA/pfXBMQMHqr7XNXzjnaNU+jB7hL 49 | yQ== 50 | -----END CERTIFICATE----- 51 | -------------------------------------------------------------------------------- /harness/daemons/.env: -------------------------------------------------------------------------------- 1 | 2 | set -e 3 | 4 | MONGOVERSION=$(mongod --version | sed -n 's/.*v\([0-9]\+\.[0-9]\+\)\..*/\1/p') 5 | MONGOMAJOR=$(echo $MONGOVERSION | sed 's/\([0-9]\+\)\..*/\1/') 6 | MONGOMINOR=$(echo $MONGOVERSION | sed 's/[0-9]\+\.\([0-9]\+\)/\1/') 7 | 8 | versionAtLeast() { 9 | TESTMAJOR="$1" 10 | TESTMINOR="$2" 11 | if [ "$MONGOMAJOR" -gt "$TESTMAJOR" ]; then 12 | return 0 13 | fi 14 | if [ "$MONGOMAJOR" -lt "$TESTMAJOR" ]; then 15 | return 100 16 | fi 17 | if [ "$MONGOMINOR" -ge "$TESTMINOR" ]; then 18 | return 0 19 | fi 20 | return 100 21 | } 22 | 23 | COMMONDOPTSNOIP=" 24 | --nohttpinterface 25 | --nojournal 26 | --smallfiles 27 | --oplogSize=1 28 | --dbpath ./db 29 | " 30 | COMMONDOPTS=" 31 | $COMMONDOPTSNOIP 32 | --bind_ip=127.0.0.1 33 | " 34 | COMMONCOPTS=" 35 | $COMMONDOPTS 36 | " 37 | COMMONSOPTS=" 38 | --bind_ip=127.0.0.1 39 | " 40 | 41 | CFG1OPTS="" 42 | CFG2OPTS="" 43 | CFG3OPTS="" 44 | 45 | MONGOS1OPTS="--configdb 127.0.0.1:40101" 46 | MONGOS2OPTS="--configdb 127.0.0.1:40102" 47 | MONGOS3OPTS="--configdb 127.0.0.1:40103" 48 | 49 | 50 | 51 | if versionAtLeast 3 2; then 52 | 53 | # 3.2 doesn't like --nojournal on config servers. 54 | COMMONCOPTS="$(echo "$COMMONCOPTS" | sed '/--nojournal/d')" 55 | 56 | if versionAtLeast 3 4; then 57 | # http interface is disabled by default, this option does not exist anymore 58 | COMMONDOPTSNOIP="$(echo "$COMMONDOPTSNOIP" | sed '/--nohttpinterface/d')" 59 | COMMONDOPTS="$(echo "$COMMONDOPTS" | sed '/--nohttpinterface/d')" 60 | COMMONCOPTS="$(echo "$COMMONCOPTS" | sed '/--nohttpinterface/d')" 61 | 62 | 63 | if versionAtLeast 3 6; then 64 | #In version 3.6 --nojournal is deprecated for replica set members using WiredTiger 65 | COMMONDOPTSNOIP="$(echo "$COMMONDOPTSNOIP" | sed '/--nojournal/d')" 66 | COMMONDOPTS="$(echo "$COMMONDOPTS" | sed '/--nojournal/d')" 67 | COMMONCOPTS="$(echo "$COMMONCOPTS" | sed '/--nojournal/d')" 68 | fi 69 | 70 | # config server need to be started as replica set 71 | 72 | CFG1OPTS="--replSet conf1" 73 | CFG2OPTS="--replSet conf2" 74 | CFG3OPTS="--replSet conf3" 75 | 76 | MONGOS1OPTS="--configdb conf1/127.0.0.1:40101" 77 | MONGOS2OPTS="--configdb conf2/127.0.0.1:40102" 78 | MONGOS3OPTS="--configdb conf3/127.0.0.1:40103" 79 | fi 80 | fi 81 | 82 | 83 | 84 | if [ "$TRAVIS" = true ]; then 85 | set -x 86 | fi 87 | -------------------------------------------------------------------------------- /harness/daemons/cfg1/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/cfg1/db/.empty -------------------------------------------------------------------------------- /harness/daemons/cfg1/db/mongod.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/cfg1/db/mongod.lock -------------------------------------------------------------------------------- /harness/daemons/cfg1/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/cfg1/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONCOPTS \ 6 | --port 40101 \ 7 | --configsvr \ 8 | $CFG1OPTS 9 | 10 | -------------------------------------------------------------------------------- /harness/daemons/cfg2/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/cfg2/db/.empty -------------------------------------------------------------------------------- /harness/daemons/cfg2/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/cfg2/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONCOPTS \ 6 | --port 40102 \ 7 | --configsvr \ 8 | $CFG2OPTS 9 | 10 | -------------------------------------------------------------------------------- /harness/daemons/cfg3/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/cfg3/db/.empty -------------------------------------------------------------------------------- /harness/daemons/cfg3/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/cfg3/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONCOPTS \ 6 | --port 40103 \ 7 | --configsvr \ 8 | $CFG3OPTS \ 9 | --auth \ 10 | --keyFile=../../certs/keyfile 11 | -------------------------------------------------------------------------------- /harness/daemons/db1/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/db1/db/.empty -------------------------------------------------------------------------------- /harness/daemons/db1/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/db1/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | if [ x$NOIPV6 = x1 ]; then 6 | BINDIP="127.0.0.1" 7 | else 8 | BINDIP="127.0.0.1,::1" 9 | fi 10 | 11 | exec mongod $COMMONDOPTSNOIP \ 12 | --shardsvr \ 13 | --bind_ip=$BINDIP \ 14 | --port 40001 \ 15 | --ipv6 16 | -------------------------------------------------------------------------------- /harness/daemons/db2/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/db2/db/.empty -------------------------------------------------------------------------------- /harness/daemons/db2/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/db2/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --port 40002 \ 7 | --auth 8 | -------------------------------------------------------------------------------- /harness/daemons/db3/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/db3/db/.empty -------------------------------------------------------------------------------- /harness/daemons/db3/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/db3/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --port 40003 \ 7 | --auth \ 8 | --sslMode preferSSL \ 9 | --sslCAFile ../../certs/server.pem \ 10 | --sslPEMKeyFile ../../certs/server.pem 11 | 12 | -------------------------------------------------------------------------------- /harness/daemons/rs1a/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs1a/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs1a/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs1a/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs1 \ 8 | --port 40011 9 | -------------------------------------------------------------------------------- /harness/daemons/rs1b/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs1b/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs1b/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs1b/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs1 \ 8 | --port 40012 9 | -------------------------------------------------------------------------------- /harness/daemons/rs1c/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs1c/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs1c/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs1c/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs1 \ 8 | --port 40013 9 | -------------------------------------------------------------------------------- /harness/daemons/rs2a/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs2a/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs2a/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs2a/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs2 \ 8 | --port 40021 9 | -------------------------------------------------------------------------------- /harness/daemons/rs2b/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs2b/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs2b/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs2b/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs2 \ 8 | --port 40022 9 | -------------------------------------------------------------------------------- /harness/daemons/rs2c/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs2c/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs2c/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs2c/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs2 \ 8 | --port 40023 9 | -------------------------------------------------------------------------------- /harness/daemons/rs3a/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs3a/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs3a/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs3a/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs3 \ 8 | --port 40031 \ 9 | --keyFile=../../certs/keyfile 10 | -------------------------------------------------------------------------------- /harness/daemons/rs3b/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs3b/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs3b/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs3b/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs3 \ 8 | --port 40032 \ 9 | --keyFile=../../certs/keyfile 10 | -------------------------------------------------------------------------------- /harness/daemons/rs3c/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs3c/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs3c/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs3c/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs3 \ 8 | --port 40033 \ 9 | --keyFile=../../certs/keyfile 10 | -------------------------------------------------------------------------------- /harness/daemons/rs4a/db/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/harness/daemons/rs4a/db/.empty -------------------------------------------------------------------------------- /harness/daemons/rs4a/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/rs4a/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongod $COMMONDOPTS \ 6 | --shardsvr \ 7 | --replSet rs4 \ 8 | --port 40041 9 | -------------------------------------------------------------------------------- /harness/daemons/s1/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/s1/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongos $COMMONSOPTS \ 6 | --port 40201 \ 7 | $MONGOS1OPTS 8 | -------------------------------------------------------------------------------- /harness/daemons/s2/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/s2/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongos $COMMONSOPTS \ 6 | --port 40202 \ 7 | $MONGOS2OPTS 8 | -------------------------------------------------------------------------------- /harness/daemons/s3/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec cat - > log.txt 4 | -------------------------------------------------------------------------------- /harness/daemons/s3/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../.env 4 | 5 | exec mongos $COMMONSOPTS \ 6 | --port 40203 \ 7 | $MONGOS3OPTS \ 8 | --keyFile=../../certs/keyfile 9 | -------------------------------------------------------------------------------- /harness/mongojs/dropall.js: -------------------------------------------------------------------------------- 1 | 2 | var ports = [40001, 40002, 40011, 40012, 40013, 40021, 40022, 40023, 40041, 40101, 40102, 40103, 40201, 40202, 40203] 3 | var auth = [40002, 40103, 40203, 40031] 4 | var db1 = new Mongo("localhost:40001") 5 | 6 | if (db1.getDB("admin").serverBuildInfo().OpenSSLVersion) { 7 | ports.push(40003) 8 | auth.push(40003) 9 | } 10 | 11 | for (var i in ports) { 12 | var port = ports[i] 13 | var server = "localhost:" + port 14 | var mongo = new Mongo("localhost:" + port) 15 | var admin = mongo.getDB("admin") 16 | 17 | for (var j in auth) { 18 | if (auth[j] == port) { 19 | print("removing user for port " + auth[j]) 20 | for (var k = 0; k < 10; k++) { 21 | var ok = admin.auth("root", "rapadura") 22 | if (ok) { 23 | admin.system.users.find().forEach(function (u) { 24 | if (u.user == "root" || u.user == "reader") { 25 | return; 26 | } 27 | if (typeof admin.dropUser == "function") { 28 | mongo.getDB(u.db).dropUser(u.user); 29 | } else { 30 | admin.removeUser(u.user); 31 | } 32 | }) 33 | break 34 | } 35 | print("failed to auth for port " + port + " retrying in 1s ") 36 | sleep(1000) 37 | } 38 | } 39 | } 40 | var result = admin.runCommand({ "listDatabases": 1 }) 41 | for (var j = 0; j != 100; j++) { 42 | if (typeof result.databases != "undefined" || notMaster(result)) { 43 | break 44 | } 45 | result = admin.runCommand({ "listDatabases": 1 }) 46 | } 47 | if (notMaster(result)) { 48 | continue 49 | } 50 | if (typeof result.databases == "undefined") { 51 | print("Could not list databases. Command result:") 52 | print(JSON.stringify(result)) 53 | quit(12) 54 | } 55 | var dbs = result.databases 56 | for (var j = 0; j != dbs.length; j++) { 57 | var db = dbs[j] 58 | switch (db.name) { 59 | case "admin": 60 | case "local": 61 | case "config": 62 | break 63 | default: 64 | mongo.getDB(db.name).dropDatabase() 65 | } 66 | } 67 | } 68 | 69 | function notMaster(result) { 70 | return typeof result.errmsg != "undefined" && (result.errmsg.indexOf("not master") >= 0 || result.errmsg.indexOf("no master found")) 71 | } 72 | 73 | // vim:ts=4:sw=4:et 74 | -------------------------------------------------------------------------------- /harness/mongojs/init.js: -------------------------------------------------------------------------------- 1 | //var settings = {heartbeatSleep: 0.05, heartbeatTimeout: 0.5} 2 | var settings = {}; 3 | 4 | // We know the master of the first set (pri=1), but not of the second. 5 | var rs1cfg = { 6 | _id: "rs1", 7 | members: [{ _id: 1, host: "127.0.0.1:40011", priority: 1, tags: { rs1: "a" } }, 8 | { _id: 2, host: "127.0.0.1:40012", priority: 0, tags: { rs1: "b" } }, 9 | { _id: 3, host: "127.0.0.1:40013", priority: 0, tags: { rs1: "c" } }], 10 | settings: settings 11 | } 12 | var rs2cfg = { 13 | _id: "rs2", 14 | members: [{ _id: 1, host: "127.0.0.1:40021", priority: 1, tags: { rs2: "a" } }, 15 | { _id: 2, host: "127.0.0.1:40022", priority: 1, tags: { rs2: "b" } }, 16 | { _id: 3, host: "127.0.0.1:40023", priority: 1, tags: { rs2: "c" } }], 17 | settings: settings 18 | } 19 | var rs3cfg = { 20 | _id: "rs3", 21 | members: [{ _id: 1, host: "127.0.0.1:40031", priority: 1, tags: { rs3: "a" } }, 22 | { _id: 2, host: "127.0.0.1:40032", priority: 0, tags: { rs3: "b" } }, 23 | { _id: 3, host: "127.0.0.1:40033", priority: 0, tags: { rs3: "c" } }], 24 | settings: settings 25 | } 26 | 27 | for (var i = 0; i != 60; i++) { 28 | try { 29 | db1 = new Mongo("127.0.0.1:40001").getDB("admin") 30 | rs1a = new Mongo("127.0.0.1:40011").getDB("admin") 31 | rs2a = new Mongo("127.0.0.1:40021").getDB("admin") 32 | rs3a = new Mongo("127.0.0.1:40031").getDB("admin") 33 | cfg1 = new Mongo("127.0.0.1:40101").getDB("admin") 34 | cfg2 = new Mongo("127.0.0.1:40102").getDB("admin") 35 | cfg3 = new Mongo("127.0.0.1:40103").getDB("admin") 36 | break 37 | } catch (err) { 38 | print("Can't connect yet...") 39 | } 40 | sleep(1000) 41 | } 42 | 43 | function hasSSL() { 44 | return Boolean(db1.serverBuildInfo().OpenSSLVersion) 45 | } 46 | 47 | function versionAtLeast() { 48 | var version = db1.version().split(".") 49 | for (var i = 0; i < arguments.length; i++) { 50 | if (i == arguments.length) { 51 | return false 52 | } 53 | if (arguments[i] != version[i]) { 54 | return version[i] >= arguments[i] 55 | } 56 | } 57 | return true 58 | } 59 | 60 | 61 | if (versionAtLeast(3, 4)) { 62 | print("configuring config server for mongodb 3.4+") 63 | cfg1.runCommand({ replSetInitiate: { _id: "conf1", configsvr: true, members: [{ "_id": 1, "host": "localhost:40101" }] } }) 64 | cfg2.runCommand({ replSetInitiate: { _id: "conf2", configsvr: true, members: [{ "_id": 1, "host": "localhost:40102" }] } }) 65 | cfg3.runCommand({ replSetInitiate: { _id: "conf3", configsvr: true, members: [{ "_id": 1, "host": "localhost:40103" }] } }) 66 | } 67 | 68 | sleep(3000) 69 | 70 | rs1a.runCommand({ replSetInitiate: rs1cfg }) 71 | rs2a.runCommand({ replSetInitiate: rs2cfg }) 72 | rs3a.runCommand({ replSetInitiate: rs3cfg }) 73 | 74 | function configAuth() { 75 | var addrs = ["127.0.0.1:40002", "127.0.0.1:40203", "127.0.0.1:40031"] 76 | if (hasSSL()) { 77 | addrs.push("127.0.0.1:40003") 78 | } 79 | for (var i in addrs) { 80 | print("Configuring auth for", addrs[i]) 81 | var db = new Mongo(addrs[i]).getDB("admin") 82 | var timedOut = false 83 | createUser: 84 | for (var i = 0; i < 60; i++) { 85 | try { 86 | db.createUser({ user: "root", pwd: "rapadura", roles: ["root"] }) 87 | } catch (err) { 88 | // 3.2 consistently fails replication of creds on 40031 (config server) 89 | print("createUser command returned an error: " + err) 90 | if (String(err).indexOf("timed out") >= 0) { 91 | timedOut = true; 92 | } 93 | // on 3.6 cluster with keyFile, we sometimes get this error 94 | if (String(err).indexOf("Cache Reader No keys found for HMAC that is valid for time")) { 95 | sleep(500) 96 | continue createUser; 97 | } 98 | } 99 | break; 100 | } 101 | 102 | for (var i = 0; i < 60; i++) { 103 | var ok = db.auth("root", "rapadura") 104 | if (ok || !timedOut) { 105 | break 106 | } 107 | sleep(1000); 108 | } 109 | sleep(500) 110 | db.createUser({ user: "reader", pwd: "rapadura", roles: ["readAnyDatabase"] }) 111 | sleep(3000) 112 | } 113 | } 114 | 115 | function addShard(adminDb, shardList) { 116 | for (var index = 0; index < shardList.length; index++) { 117 | for (var i = 0; i < 10; i++) { 118 | var result = adminDb.runCommand({ addshard: shardList[index] }) 119 | if (result.ok == 1) { 120 | print("shard " + shardList[index] + " sucessfully added") 121 | break 122 | } else { 123 | print("fail to add shard: " + shardList[index] + " error: " + JSON.stringify(result) + ", retrying in 1s") 124 | sleep(1000) 125 | } 126 | } 127 | } 128 | } 129 | 130 | function configShards() { 131 | s1 = new Mongo("127.0.0.1:40201").getDB("admin") 132 | addShard(s1, ["127.0.0.1:40001", "rs1/127.0.0.1:40011"]) 133 | 134 | s2 = new Mongo("127.0.0.1:40202").getDB("admin") 135 | addShard(s2, ["rs2/127.0.0.1:40021"]) 136 | 137 | s3 = new Mongo("127.0.0.1:40203").getDB("admin") 138 | for (var i = 0; i < 10; i++) { 139 | var ok = s3.auth("root", "rapadura") 140 | if (ok) { 141 | break 142 | } 143 | sleep(1000) 144 | } 145 | addShard(s3, ["rs3/127.0.0.1:40031"]) 146 | } 147 | 148 | function countHealthy(rs) { 149 | var status = rs.runCommand({ replSetGetStatus: 1 }) 150 | var count = 0 151 | var primary = 0 152 | if (typeof status.members != "undefined") { 153 | for (var i = 0; i != status.members.length; i++) { 154 | var m = status.members[i] 155 | if (m.health == 1 && (m.state == 1 || m.state == 2)) { 156 | count += 1 157 | if (m.state == 1) { 158 | primary = 1 159 | } 160 | } 161 | } 162 | } 163 | if (primary == 0) { 164 | count = 0 165 | } 166 | return count 167 | } 168 | 169 | var totalRSMembers = rs1cfg.members.length + rs2cfg.members.length + rs3cfg.members.length 170 | 171 | for (var i = 0; i != 60; i++) { 172 | var count = countHealthy(rs1a) + countHealthy(rs2a) + countHealthy(rs3a) 173 | print("Replica sets have", count, "healthy nodes.") 174 | if (count == totalRSMembers) { 175 | configAuth() 176 | sleep(2000) 177 | configShards() 178 | quit(0) 179 | } 180 | sleep(1000) 181 | } 182 | 183 | print("Replica sets didn't sync up properly.") 184 | quit(12) 185 | 186 | // vim:ts=4:sw=4:et 187 | -------------------------------------------------------------------------------- /harness/mongojs/wait.js: -------------------------------------------------------------------------------- 1 | // We know the master of the first set (pri=1), but not of the second. 2 | var settings = {} 3 | var rs1cfg = {_id: "rs1", 4 | members: [{_id: 1, host: "127.0.0.1:40011", priority: 1}, 5 | {_id: 2, host: "127.0.0.1:40012", priority: 0}, 6 | {_id: 3, host: "127.0.0.1:40013", priority: 0}]} 7 | var rs2cfg = {_id: "rs2", 8 | members: [{_id: 1, host: "127.0.0.1:40021", priority: 1}, 9 | {_id: 2, host: "127.0.0.1:40022", priority: 1}, 10 | {_id: 3, host: "127.0.0.1:40023", priority: 0}]} 11 | var rs3cfg = {_id: "rs3", 12 | members: [{_id: 1, host: "127.0.0.1:40031", priority: 1}, 13 | {_id: 2, host: "127.0.0.1:40032", priority: 1}, 14 | {_id: 3, host: "127.0.0.1:40033", priority: 1}], 15 | settings: settings} 16 | 17 | for (var i = 0; i != 60; i++) { 18 | try { 19 | rs1a = new Mongo("127.0.0.1:40011").getDB("admin") 20 | rs2a = new Mongo("127.0.0.1:40021").getDB("admin") 21 | rs3a = new Mongo("127.0.0.1:40031").getDB("admin") 22 | rs3a.auth("root", "rapadura") 23 | db1 = new Mongo("127.0.0.1:40001").getDB("admin") 24 | db2 = new Mongo("127.0.0.1:40002").getDB("admin") 25 | break 26 | } catch(err) { 27 | print("Can't connect yet...") 28 | } 29 | sleep(1000) 30 | } 31 | 32 | function countHealthy(rs) { 33 | var status = rs.runCommand({replSetGetStatus: 1}) 34 | var count = 0 35 | var primary = 0 36 | if (typeof status.members != "undefined") { 37 | for (var i = 0; i != status.members.length; i++) { 38 | var m = status.members[i] 39 | if (m.health == 1 && (m.state == 1 || m.state == 2)) { 40 | count += 1 41 | if (m.state == 1) { 42 | primary = 1 43 | } 44 | } 45 | } 46 | } 47 | if (primary == 0) { 48 | count = 0 49 | } 50 | return count 51 | } 52 | 53 | var totalRSMembers = rs1cfg.members.length + rs2cfg.members.length + rs3cfg.members.length 54 | 55 | for (var i = 0; i != 90; i++) { 56 | var count = countHealthy(rs1a) + countHealthy(rs2a) + countHealthy(rs3a) 57 | print("Replica sets have", count, "healthy nodes.") 58 | if (count == totalRSMembers) { 59 | quit(0) 60 | } 61 | sleep(1000) 62 | } 63 | 64 | print("Replica sets didn't sync up properly.") 65 | quit(12) 66 | 67 | // vim:ts=4:sw=4:et 68 | -------------------------------------------------------------------------------- /harness/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | LINE="---------------" 4 | 5 | start() { 6 | if [ -d _harness ]; then 7 | echo "Daemon setup already in place, stop it first." 8 | exit 1 9 | fi 10 | mkdir -p _harness 11 | cd _harness 12 | cp -a ../harness/daemons . 13 | cp -a ../harness/certs . 14 | echo keyfile > certs/keyfile 15 | chmod 600 certs/keyfile 16 | if ! mongod --help | grep -q -- --ssl; then 17 | rm -rf daemons/db3 18 | fi 19 | COUNT=$(ls daemons | wc -l) 20 | echo "Running daemons..." 21 | svscan daemons & 22 | SVSCANPID=$! 23 | echo $SVSCANPID > svscan.pid 24 | if ! kill -0 $SVSCANPID; then 25 | echo "Cannot execute svscan." 26 | exit 1 27 | fi 28 | echo "Starting $COUNT processes..." 29 | for i in $(seq 30); do 30 | UP=$(svstat daemons/* | grep ' up ' | grep -v ' [0-3] seconds' | wc -l) 31 | echo "$UP processes up..." 32 | if [ x$COUNT = x$UP ]; then 33 | echo "Running init.js with mongo..." 34 | mongo --nodb ../harness/mongojs/init.js 35 | exit 0 36 | fi 37 | sleep 1 38 | done 39 | echo "Failed to start processes. svstat _harness/daemons/* output:" 40 | echo $LINE 41 | svstat daemons/* 42 | echo $LINE 43 | for DAEMON in daemons/*; do 44 | if $(svstat $DAEMON | grep ' up ' | grep ' [0-3] seconds' > /dev/null); then 45 | echo "Logs for _harness/$DAEMON:" 46 | echo $LINE 47 | cat $DAEMON/log/log.txt 48 | echo $LINE 49 | fi 50 | done 51 | exit 1 52 | } 53 | 54 | stop() { 55 | if [ -d _harness ]; then 56 | cd _harness 57 | if [ -f svscan.pid ]; then 58 | kill -9 $(cat svscan.pid) 2> /dev/null || true 59 | svc -dx daemons/* daemons/*/log > /dev/null 2>&1 || true 60 | COUNT=$(ls daemons | wc -l) 61 | echo "Shutting down $COUNT processes..." 62 | while true; do 63 | DOWN=$(svstat daemons/* | grep 'supervise not running' | wc -l) 64 | echo "$DOWN processes down..." 65 | if [ x$DOWN = x$COUNT ]; then 66 | break 67 | fi 68 | sleep 1 69 | done 70 | rm svscan.pid 71 | echo "Done." 72 | fi 73 | cd .. 74 | rm -rf _harness 75 | fi 76 | } 77 | 78 | 79 | if [ ! -f suite_test.go ]; then 80 | echo "This script must be run from within the source directory." 81 | exit 1 82 | fi 83 | 84 | case "$1" in 85 | 86 | start) 87 | start $2 88 | ;; 89 | 90 | stop) 91 | stop $2 92 | ;; 93 | 94 | esac 95 | 96 | # vim:ts=4:sw=4:et 97 | -------------------------------------------------------------------------------- /internal/json/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /internal/json/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Large data benchmark. 6 | // The JSON data is a summary of agl's changes in the 7 | // go, webkit, and chromium open source projects. 8 | // We benchmark converting between the JSON form 9 | // and in-memory data structures. 10 | 11 | package json 12 | 13 | import ( 14 | "bytes" 15 | "compress/gzip" 16 | "io/ioutil" 17 | "os" 18 | "strings" 19 | "testing" 20 | ) 21 | 22 | type codeResponse struct { 23 | Tree *codeNode `json:"tree"` 24 | Username string `json:"username"` 25 | } 26 | 27 | type codeNode struct { 28 | Name string `json:"name"` 29 | Kids []*codeNode `json:"kids"` 30 | CLWeight float64 `json:"cl_weight"` 31 | Touches int `json:"touches"` 32 | MinT int64 `json:"min_t"` 33 | MaxT int64 `json:"max_t"` 34 | MeanT int64 `json:"mean_t"` 35 | } 36 | 37 | var codeJSON []byte 38 | var codeStruct codeResponse 39 | 40 | func codeInit() { 41 | f, err := os.Open("testdata/code.json.gz") 42 | if err != nil { 43 | panic(err) 44 | } 45 | defer f.Close() 46 | gz, err := gzip.NewReader(f) 47 | if err != nil { 48 | panic(err) 49 | } 50 | data, err := ioutil.ReadAll(gz) 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | codeJSON = data 56 | 57 | if err := Unmarshal(codeJSON, &codeStruct); err != nil { 58 | panic("unmarshal code.json: " + err.Error()) 59 | } 60 | 61 | if data, err = Marshal(&codeStruct); err != nil { 62 | panic("marshal code.json: " + err.Error()) 63 | } 64 | 65 | if !bytes.Equal(data, codeJSON) { 66 | println("different lengths", len(data), len(codeJSON)) 67 | for i := 0; i < len(data) && i < len(codeJSON); i++ { 68 | if data[i] != codeJSON[i] { 69 | println("re-marshal: changed at byte", i) 70 | println("orig: ", string(codeJSON[i-10:i+10])) 71 | println("new: ", string(data[i-10:i+10])) 72 | break 73 | } 74 | } 75 | panic("re-marshal code.json: different result") 76 | } 77 | } 78 | 79 | func BenchmarkCodeEncoder(b *testing.B) { 80 | if codeJSON == nil { 81 | b.StopTimer() 82 | codeInit() 83 | b.StartTimer() 84 | } 85 | enc := NewEncoder(ioutil.Discard) 86 | for i := 0; i < b.N; i++ { 87 | if err := enc.Encode(&codeStruct); err != nil { 88 | b.Fatal("Encode:", err) 89 | } 90 | } 91 | b.SetBytes(int64(len(codeJSON))) 92 | } 93 | 94 | func BenchmarkCodeMarshal(b *testing.B) { 95 | if codeJSON == nil { 96 | b.StopTimer() 97 | codeInit() 98 | b.StartTimer() 99 | } 100 | for i := 0; i < b.N; i++ { 101 | if _, err := Marshal(&codeStruct); err != nil { 102 | b.Fatal("Marshal:", err) 103 | } 104 | } 105 | b.SetBytes(int64(len(codeJSON))) 106 | } 107 | 108 | func BenchmarkCodeDecoder(b *testing.B) { 109 | if codeJSON == nil { 110 | b.StopTimer() 111 | codeInit() 112 | b.StartTimer() 113 | } 114 | var buf bytes.Buffer 115 | dec := NewDecoder(&buf) 116 | var r codeResponse 117 | for i := 0; i < b.N; i++ { 118 | buf.Write(codeJSON) 119 | // hide EOF 120 | buf.WriteByte('\n') 121 | buf.WriteByte('\n') 122 | buf.WriteByte('\n') 123 | if err := dec.Decode(&r); err != nil { 124 | b.Fatal("Decode:", err) 125 | } 126 | } 127 | b.SetBytes(int64(len(codeJSON))) 128 | } 129 | 130 | func BenchmarkDecoderStream(b *testing.B) { 131 | b.StopTimer() 132 | var buf bytes.Buffer 133 | dec := NewDecoder(&buf) 134 | buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n") 135 | var x interface{} 136 | if err := dec.Decode(&x); err != nil { 137 | b.Fatal("Decode:", err) 138 | } 139 | ones := strings.Repeat(" 1\n", 300000) + "\n\n\n" 140 | b.StartTimer() 141 | for i := 0; i < b.N; i++ { 142 | if i%300000 == 0 { 143 | buf.WriteString(ones) 144 | } 145 | x = nil 146 | if err := dec.Decode(&x); err != nil || x != 1.0 { 147 | b.Fatalf("Decode: %v after %d", err, i) 148 | } 149 | } 150 | } 151 | 152 | func BenchmarkCodeUnmarshal(b *testing.B) { 153 | if codeJSON == nil { 154 | b.StopTimer() 155 | codeInit() 156 | b.StartTimer() 157 | } 158 | for i := 0; i < b.N; i++ { 159 | var r codeResponse 160 | if err := Unmarshal(codeJSON, &r); err != nil { 161 | b.Fatal("Unmarshal:", err) 162 | } 163 | } 164 | b.SetBytes(int64(len(codeJSON))) 165 | } 166 | 167 | func BenchmarkCodeUnmarshalReuse(b *testing.B) { 168 | if codeJSON == nil { 169 | b.StopTimer() 170 | codeInit() 171 | b.StartTimer() 172 | } 173 | var r codeResponse 174 | for i := 0; i < b.N; i++ { 175 | if err := Unmarshal(codeJSON, &r); err != nil { 176 | b.Fatal("Unmarshal:", err) 177 | } 178 | } 179 | } 180 | 181 | func BenchmarkUnmarshalString(b *testing.B) { 182 | data := []byte(`"hello, world"`) 183 | var s string 184 | 185 | for i := 0; i < b.N; i++ { 186 | if err := Unmarshal(data, &s); err != nil { 187 | b.Fatal("Unmarshal:", err) 188 | } 189 | } 190 | } 191 | 192 | func BenchmarkUnmarshalFloat64(b *testing.B) { 193 | var f float64 194 | data := []byte(`3.14`) 195 | 196 | for i := 0; i < b.N; i++ { 197 | if err := Unmarshal(data, &f); err != nil { 198 | b.Fatal("Unmarshal:", err) 199 | } 200 | } 201 | } 202 | 203 | func BenchmarkUnmarshalInt64(b *testing.B) { 204 | var x int64 205 | data := []byte(`3`) 206 | 207 | for i := 0; i < b.N; i++ { 208 | if err := Unmarshal(data, &x); err != nil { 209 | b.Fatal("Unmarshal:", err) 210 | } 211 | } 212 | } 213 | 214 | func BenchmarkIssue10335(b *testing.B) { 215 | b.ReportAllocs() 216 | var s struct{} 217 | j := []byte(`{"a":{ }}`) 218 | for n := 0; n < b.N; n++ { 219 | if err := Unmarshal(j, &s); err != nil { 220 | b.Fatal(err) 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /internal/json/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "log" 13 | "os" 14 | "strings" 15 | ) 16 | 17 | func ExampleMarshal() { 18 | type ColorGroup struct { 19 | ID int 20 | Name string 21 | Colors []string 22 | } 23 | group := ColorGroup{ 24 | ID: 1, 25 | Name: "Reds", 26 | Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, 27 | } 28 | b, err := json.Marshal(group) 29 | if err != nil { 30 | fmt.Println("error:", err) 31 | } 32 | os.Stdout.Write(b) 33 | // Output: 34 | // {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} 35 | } 36 | 37 | func ExampleUnmarshal() { 38 | var jsonBlob = []byte(`[ 39 | {"Name": "Platypus", "Order": "Monotremata"}, 40 | {"Name": "Quoll", "Order": "Dasyuromorphia"} 41 | ]`) 42 | type Animal struct { 43 | Name string 44 | Order string 45 | } 46 | var animals []Animal 47 | err := json.Unmarshal(jsonBlob, &animals) 48 | if err != nil { 49 | fmt.Println("error:", err) 50 | } 51 | fmt.Printf("%+v", animals) 52 | // Output: 53 | // [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] 54 | } 55 | 56 | // This example uses a Decoder to decode a stream of distinct JSON values. 57 | func ExampleDecoder() { 58 | const jsonStream = ` 59 | {"Name": "Ed", "Text": "Knock knock."} 60 | {"Name": "Sam", "Text": "Who's there?"} 61 | {"Name": "Ed", "Text": "Go fmt."} 62 | {"Name": "Sam", "Text": "Go fmt who?"} 63 | {"Name": "Ed", "Text": "Go fmt yourself!"} 64 | ` 65 | type Message struct { 66 | Name, Text string 67 | } 68 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 69 | for { 70 | var m Message 71 | if err := dec.Decode(&m); err == io.EOF { 72 | break 73 | } else if err != nil { 74 | log.Fatal(err) 75 | } 76 | fmt.Printf("%s: %s\n", m.Name, m.Text) 77 | } 78 | // Output: 79 | // Ed: Knock knock. 80 | // Sam: Who's there? 81 | // Ed: Go fmt. 82 | // Sam: Go fmt who? 83 | // Ed: Go fmt yourself! 84 | } 85 | 86 | // This example uses a Decoder to decode a stream of distinct JSON values. 87 | func ExampleDecoder_Token() { 88 | const jsonStream = ` 89 | {"Message": "Hello", "Array": [1, 2, 3], "Null": null, "Number": 1.234} 90 | ` 91 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 92 | for { 93 | t, err := dec.Token() 94 | if err == io.EOF { 95 | break 96 | } 97 | if err != nil { 98 | log.Fatal(err) 99 | } 100 | fmt.Printf("%T: %v", t, t) 101 | if dec.More() { 102 | fmt.Printf(" (more)") 103 | } 104 | fmt.Printf("\n") 105 | } 106 | // Output: 107 | // json.Delim: { (more) 108 | // string: Message (more) 109 | // string: Hello (more) 110 | // string: Array (more) 111 | // json.Delim: [ (more) 112 | // float64: 1 (more) 113 | // float64: 2 (more) 114 | // float64: 3 115 | // json.Delim: ] (more) 116 | // string: Null (more) 117 | // : (more) 118 | // string: Number (more) 119 | // float64: 1.234 120 | // json.Delim: } 121 | } 122 | 123 | // This example uses a Decoder to decode a streaming array of JSON objects. 124 | func ExampleDecoder_Decode_stream() { 125 | const jsonStream = ` 126 | [ 127 | {"Name": "Ed", "Text": "Knock knock."}, 128 | {"Name": "Sam", "Text": "Who's there?"}, 129 | {"Name": "Ed", "Text": "Go fmt."}, 130 | {"Name": "Sam", "Text": "Go fmt who?"}, 131 | {"Name": "Ed", "Text": "Go fmt yourself!"} 132 | ] 133 | ` 134 | type Message struct { 135 | Name, Text string 136 | } 137 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 138 | 139 | // read open bracket 140 | t, err := dec.Token() 141 | if err != nil { 142 | log.Fatal(err) 143 | } 144 | fmt.Printf("%T: %v\n", t, t) 145 | 146 | var m Message 147 | // while the array contains values 148 | for dec.More() { 149 | 150 | // decode an array value (Message) 151 | err := dec.Decode(&m) 152 | if err != nil { 153 | log.Fatal(err) 154 | } 155 | 156 | fmt.Printf("%v: %v\n", m.Name, m.Text) 157 | } 158 | 159 | // read closing bracket 160 | t, err = dec.Token() 161 | if err != nil { 162 | log.Fatal(err) 163 | } 164 | fmt.Printf("%T: %v\n", t, t) 165 | 166 | // Output: 167 | // json.Delim: [ 168 | // Ed: Knock knock. 169 | // Sam: Who's there? 170 | // Ed: Go fmt. 171 | // Sam: Go fmt who? 172 | // Ed: Go fmt yourself! 173 | // json.Delim: ] 174 | 175 | } 176 | 177 | // This example uses RawMessage to delay parsing part of a JSON message. 178 | func ExampleRawMessage() { 179 | type Color struct { 180 | Space string 181 | Point json.RawMessage // delay parsing until we know the color space 182 | } 183 | type RGB struct { 184 | R uint8 185 | G uint8 186 | B uint8 187 | } 188 | type YCbCr struct { 189 | Y uint8 190 | Cb int8 191 | Cr int8 192 | } 193 | 194 | var j = []byte(`[ 195 | {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}}, 196 | {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}} 197 | ]`) 198 | var colors []Color 199 | err := json.Unmarshal(j, &colors) 200 | if err != nil { 201 | log.Fatalln("error:", err) 202 | } 203 | 204 | for _, c := range colors { 205 | var dst interface{} 206 | switch c.Space { 207 | case "RGB": 208 | dst = new(RGB) 209 | case "YCbCr": 210 | dst = new(YCbCr) 211 | } 212 | err := json.Unmarshal(c.Point, dst) 213 | if err != nil { 214 | log.Fatalln("error:", err) 215 | } 216 | fmt.Println(c.Space, dst) 217 | } 218 | // Output: 219 | // YCbCr &{255 0 -10} 220 | // RGB &{98 218 255} 221 | } 222 | 223 | func ExampleIndent() { 224 | type Road struct { 225 | Name string 226 | Number int 227 | } 228 | roads := []Road{ 229 | {"Diamond Fork", 29}, 230 | {"Sheep Creek", 51}, 231 | } 232 | 233 | b, err := json.Marshal(roads) 234 | if err != nil { 235 | log.Fatal(err) 236 | } 237 | 238 | var out bytes.Buffer 239 | json.Indent(&out, b, "=", "\t") 240 | out.WriteTo(os.Stdout) 241 | // Output: 242 | // [ 243 | // = { 244 | // = "Name": "Diamond Fork", 245 | // = "Number": 29 246 | // = }, 247 | // = { 248 | // = "Name": "Sheep Creek", 249 | // = "Number": 51 250 | // = } 251 | // =] 252 | } 253 | -------------------------------------------------------------------------------- /internal/json/extension.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // Extension holds a set of additional rules to be used when unmarshaling 8 | // strict JSON or JSON-like content. 9 | type Extension struct { 10 | funcs map[string]funcExt 11 | consts map[string]interface{} 12 | keyed map[string]func([]byte) (interface{}, error) 13 | encode map[reflect.Type]func(v interface{}) ([]byte, error) 14 | 15 | unquotedKeys bool 16 | trailingCommas bool 17 | } 18 | 19 | type funcExt struct { 20 | key string 21 | args []string 22 | } 23 | 24 | // Extend changes the decoder behavior to consider the provided extension. 25 | func (dec *Decoder) Extend(ext *Extension) { dec.d.ext = *ext } 26 | 27 | // Extend changes the encoder behavior to consider the provided extension. 28 | func (enc *Encoder) Extend(ext *Extension) { enc.ext = *ext } 29 | 30 | // Extend includes in e the extensions defined in ext. 31 | func (e *Extension) Extend(ext *Extension) { 32 | for name, fext := range ext.funcs { 33 | e.DecodeFunc(name, fext.key, fext.args...) 34 | } 35 | for name, value := range ext.consts { 36 | e.DecodeConst(name, value) 37 | } 38 | for key, decode := range ext.keyed { 39 | e.DecodeKeyed(key, decode) 40 | } 41 | for typ, encode := range ext.encode { 42 | if e.encode == nil { 43 | e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error)) 44 | } 45 | e.encode[typ] = encode 46 | } 47 | } 48 | 49 | // DecodeFunc defines a function call that may be observed inside JSON content. 50 | // A function with the provided name will be unmarshaled as the document 51 | // {key: {args[0]: ..., args[N]: ...}}. 52 | func (e *Extension) DecodeFunc(name string, key string, args ...string) { 53 | if e.funcs == nil { 54 | e.funcs = make(map[string]funcExt) 55 | } 56 | e.funcs[name] = funcExt{key, args} 57 | } 58 | 59 | // DecodeConst defines a constant name that may be observed inside JSON content 60 | // and will be decoded with the provided value. 61 | func (e *Extension) DecodeConst(name string, value interface{}) { 62 | if e.consts == nil { 63 | e.consts = make(map[string]interface{}) 64 | } 65 | e.consts[name] = value 66 | } 67 | 68 | // DecodeKeyed defines a key that when observed as the first element inside a 69 | // JSON document triggers the decoding of that document via the provided 70 | // decode function. 71 | func (e *Extension) DecodeKeyed(key string, decode func(data []byte) (interface{}, error)) { 72 | if e.keyed == nil { 73 | e.keyed = make(map[string]func([]byte) (interface{}, error)) 74 | } 75 | e.keyed[key] = decode 76 | } 77 | 78 | // DecodeUnquotedKeys defines whether to accept map keys that are unquoted strings. 79 | func (e *Extension) DecodeUnquotedKeys(accept bool) { 80 | e.unquotedKeys = accept 81 | } 82 | 83 | // DecodeTrailingCommas defines whether to accept trailing commas in maps and arrays. 84 | func (e *Extension) DecodeTrailingCommas(accept bool) { 85 | e.trailingCommas = accept 86 | } 87 | 88 | // EncodeType registers a function to encode values with the same type of the 89 | // provided sample. 90 | func (e *Extension) EncodeType(sample interface{}, encode func(v interface{}) ([]byte, error)) { 91 | if e.encode == nil { 92 | e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error)) 93 | } 94 | e.encode[reflect.TypeOf(sample)] = encode 95 | } 96 | -------------------------------------------------------------------------------- /internal/json/extension_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | "testing" 9 | ) 10 | 11 | type funcN struct { 12 | Arg1 int `json:"arg1"` 13 | Arg2 int `json:"arg2"` 14 | } 15 | 16 | type funcs struct { 17 | Func2 *funcN `json:"$func2"` 18 | Func1 *funcN `json:"$func1"` 19 | } 20 | 21 | type funcsText struct { 22 | Func1 jsonText `json:"$func1"` 23 | Func2 jsonText `json:"$func2"` 24 | } 25 | 26 | type jsonText struct { 27 | json string 28 | } 29 | 30 | func (jt *jsonText) UnmarshalJSON(data []byte) error { 31 | jt.json = string(data) 32 | return nil 33 | } 34 | 35 | type nestedText struct { 36 | F jsonText 37 | B bool 38 | } 39 | 40 | type unquotedKey struct { 41 | S string `json:"$k_1"` 42 | } 43 | 44 | var ext Extension 45 | 46 | type keyed string 47 | 48 | func decodeKeyed(data []byte) (interface{}, error) { 49 | return keyed(data), nil 50 | } 51 | 52 | type keyedType struct { 53 | K keyed 54 | I int 55 | } 56 | 57 | type docint int 58 | 59 | type const1Type struct{} 60 | 61 | var const1 = new(const1Type) 62 | 63 | func init() { 64 | ext.DecodeFunc("Func1", "$func1") 65 | ext.DecodeFunc("Func2", "$func2", "arg1", "arg2") 66 | ext.DecodeFunc("Func3", "$func3", "arg1") 67 | ext.DecodeFunc("new Func4", "$func4", "arg1") 68 | 69 | ext.DecodeConst("Const1", const1) 70 | 71 | ext.DecodeKeyed("$key1", decodeKeyed) 72 | ext.DecodeKeyed("$func3", decodeKeyed) 73 | 74 | ext.EncodeType(docint(0), func(v interface{}) ([]byte, error) { 75 | s := `{"$docint": ` + strconv.Itoa(int(v.(docint))) + `}` 76 | return []byte(s), nil 77 | }) 78 | 79 | ext.DecodeUnquotedKeys(true) 80 | ext.DecodeTrailingCommas(true) 81 | } 82 | 83 | type extDecodeTest struct { 84 | in string 85 | ptr interface{} 86 | out interface{} 87 | err error 88 | 89 | noext bool 90 | } 91 | 92 | var extDecodeTests = []extDecodeTest{ 93 | // Functions 94 | {in: `Func1()`, ptr: new(interface{}), out: map[string]interface{}{ 95 | "$func1": map[string]interface{}{}, 96 | }}, 97 | {in: `{"v": Func1()}`, ptr: new(interface{}), out: map[string]interface{}{ 98 | "v": map[string]interface{}{"$func1": map[string]interface{}{}}, 99 | }}, 100 | {in: `Func2(1)`, ptr: new(interface{}), out: map[string]interface{}{ 101 | "$func2": map[string]interface{}{"arg1": float64(1)}, 102 | }}, 103 | {in: `Func2(1, 2)`, ptr: new(interface{}), out: map[string]interface{}{ 104 | "$func2": map[string]interface{}{"arg1": float64(1), "arg2": float64(2)}, 105 | }}, 106 | {in: `Func2(Func1())`, ptr: new(interface{}), out: map[string]interface{}{ 107 | "$func2": map[string]interface{}{"arg1": map[string]interface{}{"$func1": map[string]interface{}{}}}, 108 | }}, 109 | {in: `Func2(1, 2, 3)`, ptr: new(interface{}), err: fmt.Errorf("json: too many arguments for function Func2")}, 110 | {in: `BadFunc()`, ptr: new(interface{}), err: fmt.Errorf(`json: unknown function "BadFunc"`)}, 111 | 112 | {in: `Func1()`, ptr: new(funcs), out: funcs{Func1: &funcN{}}}, 113 | {in: `Func2(1)`, ptr: new(funcs), out: funcs{Func2: &funcN{Arg1: 1}}}, 114 | {in: `Func2(1, 2)`, ptr: new(funcs), out: funcs{Func2: &funcN{Arg1: 1, Arg2: 2}}}, 115 | 116 | {in: `Func2(1, 2, 3)`, ptr: new(funcs), err: fmt.Errorf("json: too many arguments for function Func2")}, 117 | {in: `BadFunc()`, ptr: new(funcs), err: fmt.Errorf(`json: unknown function "BadFunc"`)}, 118 | 119 | {in: `Func2(1)`, ptr: new(jsonText), out: jsonText{"Func2(1)"}}, 120 | {in: `Func2(1, 2)`, ptr: new(funcsText), out: funcsText{Func2: jsonText{"Func2(1, 2)"}}}, 121 | {in: `{"f": Func2(1, 2), "b": true}`, ptr: new(nestedText), out: nestedText{jsonText{"Func2(1, 2)"}, true}}, 122 | 123 | {in: `Func1()`, ptr: new(struct{}), out: struct{}{}}, 124 | 125 | // Functions with "new" prefix 126 | {in: `new Func4(1)`, ptr: new(interface{}), out: map[string]interface{}{ 127 | "$func4": map[string]interface{}{"arg1": float64(1)}, 128 | }}, 129 | 130 | // Constants 131 | {in: `Const1`, ptr: new(interface{}), out: const1}, 132 | {in: `{"c": Const1}`, ptr: new(struct{ C *const1Type }), out: struct{ C *const1Type }{const1}}, 133 | 134 | // Space after quoted key 135 | {in: `{"c" : Const1}`, ptr: new(struct{ C *const1Type }), out: struct{ C *const1Type }{const1}}, 136 | 137 | // Keyed documents 138 | {in: `{"v": {"$key1": 1}}`, ptr: new(interface{}), out: map[string]interface{}{"v": keyed(`{"$key1": 1}`)}}, 139 | {in: `{"k": {"$key1": 1}}`, ptr: new(keyedType), out: keyedType{K: keyed(`{"$key1": 1}`)}}, 140 | {in: `{"i": {"$key1": 1}}`, ptr: new(keyedType), err: &UnmarshalTypeError{"object", reflect.TypeOf(0), 18}}, 141 | 142 | // Keyed function documents 143 | {in: `{"v": Func3()}`, ptr: new(interface{}), out: map[string]interface{}{"v": keyed(`Func3()`)}}, 144 | {in: `{"k": Func3()}`, ptr: new(keyedType), out: keyedType{K: keyed(`Func3()`)}}, 145 | {in: `{"i": Func3()}`, ptr: new(keyedType), err: &UnmarshalTypeError{"object", reflect.TypeOf(0), 13}}, 146 | 147 | // Unquoted keys 148 | {in: `{$k_1: "bar"}`, ptr: new(interface{}), out: map[string]interface{}{"$k_1": "bar"}}, 149 | {in: `{$k_1: "bar"}`, ptr: new(unquotedKey), out: unquotedKey{"bar"}}, 150 | 151 | {in: `{$k_1: "bar"}`, noext: true, ptr: new(interface{}), 152 | err: &SyntaxError{"invalid character '$' looking for beginning of object key string", 2}}, 153 | {in: `{$k_1: "bar"}`, noext: true, ptr: new(unquotedKey), 154 | err: &SyntaxError{"invalid character '$' looking for beginning of object key string", 2}}, 155 | 156 | // Trailing commas 157 | {in: `{"k": "v",}`, ptr: new(interface{}), out: map[string]interface{}{"k": "v"}}, 158 | {in: `{"k": "v",}`, ptr: new(struct{}), out: struct{}{}}, 159 | {in: `["v",]`, ptr: new(interface{}), out: []interface{}{"v"}}, 160 | 161 | {in: `{"k": "v",}`, noext: true, ptr: new(interface{}), 162 | err: &SyntaxError{"invalid character '}' looking for beginning of object key string", 11}}, 163 | {in: `{"k": "v",}`, noext: true, ptr: new(struct{}), 164 | err: &SyntaxError{"invalid character '}' looking for beginning of object key string", 11}}, 165 | {in: `["a",]`, noext: true, ptr: new(interface{}), 166 | err: &SyntaxError{"invalid character ']' looking for beginning of value", 6}}, 167 | } 168 | 169 | type extEncodeTest struct { 170 | in interface{} 171 | out string 172 | err error 173 | } 174 | 175 | var extEncodeTests = []extEncodeTest{ 176 | {in: docint(13), out: "{\"$docint\":13}\n"}, 177 | } 178 | 179 | func TestExtensionDecode(t *testing.T) { 180 | for i, tt := range extDecodeTests { 181 | in := []byte(tt.in) 182 | 183 | // v = new(right-type) 184 | v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) 185 | dec := NewDecoder(bytes.NewReader(in)) 186 | if !tt.noext { 187 | dec.Extend(&ext) 188 | } 189 | if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) { 190 | t.Errorf("#%d: %v, want %v", i, err, tt.err) 191 | continue 192 | } else if err != nil { 193 | continue 194 | } 195 | if !reflect.DeepEqual(v.Elem().Interface(), tt.out) { 196 | t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out) 197 | data, _ := Marshal(v.Elem().Interface()) 198 | t.Logf("%s", string(data)) 199 | data, _ = Marshal(tt.out) 200 | t.Logf("%s", string(data)) 201 | continue 202 | } 203 | } 204 | } 205 | 206 | func TestExtensionEncode(t *testing.T) { 207 | var buf bytes.Buffer 208 | for i, tt := range extEncodeTests { 209 | buf.Truncate(0) 210 | enc := NewEncoder(&buf) 211 | enc.Extend(&ext) 212 | err := enc.Encode(tt.in) 213 | if !reflect.DeepEqual(err, tt.err) { 214 | t.Errorf("#%d: %v, want %v", i, err, tt.err) 215 | continue 216 | } 217 | if buf.String() != tt.out { 218 | t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, buf.String(), tt.out) 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /internal/json/fold.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "unicode/utf8" 10 | ) 11 | 12 | const ( 13 | caseMask = ^byte(0x20) // Mask to ignore case in ASCII. 14 | kelvin = '\u212a' 15 | smallLongEss = '\u017f' 16 | ) 17 | 18 | // foldFunc returns one of four different case folding equivalence 19 | // functions, from most general (and slow) to fastest: 20 | // 21 | // 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 22 | // 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') 23 | // 3) asciiEqualFold, no special, but includes non-letters (including _) 24 | // 4) simpleLetterEqualFold, no specials, no non-letters. 25 | // 26 | // The letters S and K are special because they map to 3 runes, not just 2: 27 | // * S maps to s and to U+017F 'ſ' Latin small letter long s 28 | // * k maps to K and to U+212A 'K' Kelvin sign 29 | // See https://play.golang.org/p/tTxjOc0OGo 30 | // 31 | // The returned function is specialized for matching against s and 32 | // should only be given s. It's not curried for performance reasons. 33 | func foldFunc(s []byte) func(s, t []byte) bool { 34 | nonLetter := false 35 | special := false // special letter 36 | for _, b := range s { 37 | if b >= utf8.RuneSelf { 38 | return bytes.EqualFold 39 | } 40 | upper := b & caseMask 41 | if upper < 'A' || upper > 'Z' { 42 | nonLetter = true 43 | } else if upper == 'K' || upper == 'S' { 44 | // See above for why these letters are special. 45 | special = true 46 | } 47 | } 48 | if special { 49 | return equalFoldRight 50 | } 51 | if nonLetter { 52 | return asciiEqualFold 53 | } 54 | return simpleLetterEqualFold 55 | } 56 | 57 | // equalFoldRight is a specialization of bytes.EqualFold when s is 58 | // known to be all ASCII (including punctuation), but contains an 's', 59 | // 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. 60 | // See comments on foldFunc. 61 | func equalFoldRight(s, t []byte) bool { 62 | for _, sb := range s { 63 | if len(t) == 0 { 64 | return false 65 | } 66 | tb := t[0] 67 | if tb < utf8.RuneSelf { 68 | if sb != tb { 69 | sbUpper := sb & caseMask 70 | if 'A' <= sbUpper && sbUpper <= 'Z' { 71 | if sbUpper != tb&caseMask { 72 | return false 73 | } 74 | } else { 75 | return false 76 | } 77 | } 78 | t = t[1:] 79 | continue 80 | } 81 | // sb is ASCII and t is not. t must be either kelvin 82 | // sign or long s; sb must be s, S, k, or K. 83 | tr, size := utf8.DecodeRune(t) 84 | switch sb { 85 | case 's', 'S': 86 | if tr != smallLongEss { 87 | return false 88 | } 89 | case 'k', 'K': 90 | if tr != kelvin { 91 | return false 92 | } 93 | default: 94 | return false 95 | } 96 | t = t[size:] 97 | 98 | } 99 | if len(t) > 0 { 100 | return false 101 | } 102 | return true 103 | } 104 | 105 | // asciiEqualFold is a specialization of bytes.EqualFold for use when 106 | // s is all ASCII (but may contain non-letters) and contains no 107 | // special-folding letters. 108 | // See comments on foldFunc. 109 | func asciiEqualFold(s, t []byte) bool { 110 | if len(s) != len(t) { 111 | return false 112 | } 113 | for i, sb := range s { 114 | tb := t[i] 115 | if sb == tb { 116 | continue 117 | } 118 | if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { 119 | if sb&caseMask != tb&caseMask { 120 | return false 121 | } 122 | } else { 123 | return false 124 | } 125 | } 126 | return true 127 | } 128 | 129 | // simpleLetterEqualFold is a specialization of bytes.EqualFold for 130 | // use when s is all ASCII letters (no underscores, etc) and also 131 | // doesn't contain 'k', 'K', 's', or 'S'. 132 | // See comments on foldFunc. 133 | func simpleLetterEqualFold(s, t []byte) bool { 134 | if len(s) != len(t) { 135 | return false 136 | } 137 | for i, b := range s { 138 | if b&caseMask != t[i]&caseMask { 139 | return false 140 | } 141 | } 142 | return true 143 | } 144 | -------------------------------------------------------------------------------- /internal/json/fold_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "strings" 10 | "testing" 11 | "unicode/utf8" 12 | ) 13 | 14 | var foldTests = []struct { 15 | fn func(s, t []byte) bool 16 | s, t string 17 | want bool 18 | }{ 19 | {equalFoldRight, "", "", true}, 20 | {equalFoldRight, "a", "a", true}, 21 | {equalFoldRight, "", "a", false}, 22 | {equalFoldRight, "a", "", false}, 23 | {equalFoldRight, "a", "A", true}, 24 | {equalFoldRight, "AB", "ab", true}, 25 | {equalFoldRight, "AB", "ac", false}, 26 | {equalFoldRight, "sbkKc", "ſbKKc", true}, 27 | {equalFoldRight, "SbKkc", "ſbKKc", true}, 28 | {equalFoldRight, "SbKkc", "ſbKK", false}, 29 | {equalFoldRight, "e", "é", false}, 30 | {equalFoldRight, "s", "S", true}, 31 | 32 | {simpleLetterEqualFold, "", "", true}, 33 | {simpleLetterEqualFold, "abc", "abc", true}, 34 | {simpleLetterEqualFold, "abc", "ABC", true}, 35 | {simpleLetterEqualFold, "abc", "ABCD", false}, 36 | {simpleLetterEqualFold, "abc", "xxx", false}, 37 | 38 | {asciiEqualFold, "a_B", "A_b", true}, 39 | {asciiEqualFold, "aa@", "aa`", false}, // verify 0x40 and 0x60 aren't case-equivalent 40 | } 41 | 42 | func TestFold(t *testing.T) { 43 | for i, tt := range foldTests { 44 | if got := tt.fn([]byte(tt.s), []byte(tt.t)); got != tt.want { 45 | t.Errorf("%d. %q, %q = %v; want %v", i, tt.s, tt.t, got, tt.want) 46 | } 47 | truth := strings.EqualFold(tt.s, tt.t) 48 | if truth != tt.want { 49 | t.Errorf("strings.EqualFold doesn't agree with case %d", i) 50 | } 51 | } 52 | } 53 | 54 | func TestFoldAgainstUnicode(t *testing.T) { 55 | const bufSize = 5 56 | buf1 := make([]byte, 0, bufSize) 57 | buf2 := make([]byte, 0, bufSize) 58 | var runes []rune 59 | for i := 0x20; i <= 0x7f; i++ { 60 | runes = append(runes, rune(i)) 61 | } 62 | runes = append(runes, kelvin, smallLongEss) 63 | 64 | funcs := []struct { 65 | name string 66 | fold func(s, t []byte) bool 67 | letter bool // must be ASCII letter 68 | simple bool // must be simple ASCII letter (not 'S' or 'K') 69 | }{ 70 | { 71 | name: "equalFoldRight", 72 | fold: equalFoldRight, 73 | }, 74 | { 75 | name: "asciiEqualFold", 76 | fold: asciiEqualFold, 77 | simple: true, 78 | }, 79 | { 80 | name: "simpleLetterEqualFold", 81 | fold: simpleLetterEqualFold, 82 | simple: true, 83 | letter: true, 84 | }, 85 | } 86 | 87 | for _, ff := range funcs { 88 | for _, r := range runes { 89 | if r >= utf8.RuneSelf { 90 | continue 91 | } 92 | if ff.letter && !isASCIILetter(byte(r)) { 93 | continue 94 | } 95 | if ff.simple && (r == 's' || r == 'S' || r == 'k' || r == 'K') { 96 | continue 97 | } 98 | for _, r2 := range runes { 99 | buf1 := append(buf1[:0], 'x') 100 | buf2 := append(buf2[:0], 'x') 101 | buf1 = buf1[:1+utf8.EncodeRune(buf1[1:bufSize], r)] 102 | buf2 = buf2[:1+utf8.EncodeRune(buf2[1:bufSize], r2)] 103 | buf1 = append(buf1, 'x') 104 | buf2 = append(buf2, 'x') 105 | want := bytes.EqualFold(buf1, buf2) 106 | if got := ff.fold(buf1, buf2); got != want { 107 | t.Errorf("%s(%q, %q) = %v; want %v", ff.name, buf1, buf2, got, want) 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | func isASCIILetter(b byte) bool { 115 | return ('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z') 116 | } 117 | -------------------------------------------------------------------------------- /internal/json/indent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import "bytes" 8 | 9 | // Compact appends to dst the JSON-encoded src with 10 | // insignificant space characters elided. 11 | func Compact(dst *bytes.Buffer, src []byte) error { 12 | return compact(dst, src, false) 13 | } 14 | 15 | func compact(dst *bytes.Buffer, src []byte, escape bool) error { 16 | origLen := dst.Len() 17 | var scan scanner 18 | scan.reset() 19 | start := 0 20 | for i, c := range src { 21 | if escape && (c == '<' || c == '>' || c == '&') { 22 | if start < i { 23 | dst.Write(src[start:i]) 24 | } 25 | dst.WriteString(`\u00`) 26 | dst.WriteByte(hex[c>>4]) 27 | dst.WriteByte(hex[c&0xF]) 28 | start = i + 1 29 | } 30 | // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). 31 | if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { 32 | if start < i { 33 | dst.Write(src[start:i]) 34 | } 35 | dst.WriteString(`\u202`) 36 | dst.WriteByte(hex[src[i+2]&0xF]) 37 | start = i + 3 38 | } 39 | v := scan.step(&scan, c) 40 | if v >= scanSkipSpace { 41 | if v == scanError { 42 | break 43 | } 44 | if start < i { 45 | dst.Write(src[start:i]) 46 | } 47 | start = i + 1 48 | } 49 | } 50 | if scan.eof() == scanError { 51 | dst.Truncate(origLen) 52 | return scan.err 53 | } 54 | if start < len(src) { 55 | dst.Write(src[start:]) 56 | } 57 | return nil 58 | } 59 | 60 | func newline(dst *bytes.Buffer, prefix, indent string, depth int) { 61 | dst.WriteByte('\n') 62 | dst.WriteString(prefix) 63 | for i := 0; i < depth; i++ { 64 | dst.WriteString(indent) 65 | } 66 | } 67 | 68 | // Indent appends to dst an indented form of the JSON-encoded src. 69 | // Each element in a JSON object or array begins on a new, 70 | // indented line beginning with prefix followed by one or more 71 | // copies of indent according to the indentation nesting. 72 | // The data appended to dst does not begin with the prefix nor 73 | // any indentation, to make it easier to embed inside other formatted JSON data. 74 | // Although leading space characters (space, tab, carriage return, newline) 75 | // at the beginning of src are dropped, trailing space characters 76 | // at the end of src are preserved and copied to dst. 77 | // For example, if src has no trailing spaces, neither will dst; 78 | // if src ends in a trailing newline, so will dst. 79 | func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { 80 | origLen := dst.Len() 81 | var scan scanner 82 | scan.reset() 83 | needIndent := false 84 | depth := 0 85 | for _, c := range src { 86 | scan.bytes++ 87 | v := scan.step(&scan, c) 88 | if v == scanSkipSpace { 89 | continue 90 | } 91 | if v == scanError { 92 | break 93 | } 94 | if needIndent && v != scanEndObject && v != scanEndArray { 95 | needIndent = false 96 | depth++ 97 | newline(dst, prefix, indent, depth) 98 | } 99 | 100 | // Emit semantically uninteresting bytes 101 | // (in particular, punctuation in strings) unmodified. 102 | if v == scanContinue { 103 | dst.WriteByte(c) 104 | continue 105 | } 106 | 107 | // Add spacing around real punctuation. 108 | switch c { 109 | case '{', '[': 110 | // delay indent so that empty object and array are formatted as {} and []. 111 | needIndent = true 112 | dst.WriteByte(c) 113 | 114 | case ',': 115 | dst.WriteByte(c) 116 | newline(dst, prefix, indent, depth) 117 | 118 | case ':': 119 | dst.WriteByte(c) 120 | dst.WriteByte(' ') 121 | 122 | case '}', ']': 123 | if needIndent { 124 | // suppress indent in empty object/array 125 | needIndent = false 126 | } else { 127 | depth-- 128 | newline(dst, prefix, indent, depth) 129 | } 130 | dst.WriteByte(c) 131 | 132 | default: 133 | dst.WriteByte(c) 134 | } 135 | } 136 | if scan.eof() == scanError { 137 | dst.Truncate(origLen) 138 | return scan.err 139 | } 140 | return nil 141 | } 142 | -------------------------------------------------------------------------------- /internal/json/number_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "regexp" 9 | "testing" 10 | ) 11 | 12 | func TestNumberIsValid(t *testing.T) { 13 | // From: http://stackoverflow.com/a/13340826 14 | var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 15 | 16 | validTests := []string{ 17 | "0", 18 | "-0", 19 | "1", 20 | "-1", 21 | "0.1", 22 | "-0.1", 23 | "1234", 24 | "-1234", 25 | "12.34", 26 | "-12.34", 27 | "12E0", 28 | "12E1", 29 | "12e34", 30 | "12E-0", 31 | "12e+1", 32 | "12e-34", 33 | "-12E0", 34 | "-12E1", 35 | "-12e34", 36 | "-12E-0", 37 | "-12e+1", 38 | "-12e-34", 39 | "1.2E0", 40 | "1.2E1", 41 | "1.2e34", 42 | "1.2E-0", 43 | "1.2e+1", 44 | "1.2e-34", 45 | "-1.2E0", 46 | "-1.2E1", 47 | "-1.2e34", 48 | "-1.2E-0", 49 | "-1.2e+1", 50 | "-1.2e-34", 51 | "0E0", 52 | "0E1", 53 | "0e34", 54 | "0E-0", 55 | "0e+1", 56 | "0e-34", 57 | "-0E0", 58 | "-0E1", 59 | "-0e34", 60 | "-0E-0", 61 | "-0e+1", 62 | "-0e-34", 63 | } 64 | 65 | for _, test := range validTests { 66 | if !isValidNumber(test) { 67 | t.Errorf("%s should be valid", test) 68 | } 69 | 70 | var f float64 71 | if err := Unmarshal([]byte(test), &f); err != nil { 72 | t.Errorf("%s should be valid but Unmarshal failed: %v", test, err) 73 | } 74 | 75 | if !jsonNumberRegexp.MatchString(test) { 76 | t.Errorf("%s should be valid but regexp does not match", test) 77 | } 78 | } 79 | 80 | invalidTests := []string{ 81 | "", 82 | "invalid", 83 | "1.0.1", 84 | "1..1", 85 | "-1-2", 86 | "012a42", 87 | "01.2", 88 | "012", 89 | "12E12.12", 90 | "1e2e3", 91 | "1e+-2", 92 | "1e--23", 93 | "1e", 94 | "e1", 95 | "1e+", 96 | "1ea", 97 | "1a", 98 | "1.a", 99 | "1.", 100 | "01", 101 | "1.e1", 102 | } 103 | 104 | for _, test := range invalidTests { 105 | if isValidNumber(test) { 106 | t.Errorf("%s should be invalid", test) 107 | } 108 | 109 | var f float64 110 | if err := Unmarshal([]byte(test), &f); err == nil { 111 | t.Errorf("%s should be invalid but unmarshal wrote %v", test, f) 112 | } 113 | 114 | if jsonNumberRegexp.MatchString(test) { 115 | t.Errorf("%s should be invalid but matches regexp", test) 116 | } 117 | } 118 | } 119 | 120 | func BenchmarkNumberIsValid(b *testing.B) { 121 | s := "-61657.61667E+61673" 122 | for i := 0; i < b.N; i++ { 123 | isValidNumber(s) 124 | } 125 | } 126 | 127 | func BenchmarkNumberIsValidRegexp(b *testing.B) { 128 | var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 129 | s := "-61657.61667E+61673" 130 | for i := 0; i < b.N; i++ { 131 | jsonNumberRegexp.MatchString(s) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /internal/json/scanner_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "math" 10 | "math/rand" 11 | "reflect" 12 | "testing" 13 | ) 14 | 15 | // Tests of simple examples. 16 | 17 | type example struct { 18 | compact string 19 | indent string 20 | } 21 | 22 | var examples = []example{ 23 | {`1`, `1`}, 24 | {`{}`, `{}`}, 25 | {`[]`, `[]`}, 26 | {`{"":2}`, "{\n\t\"\": 2\n}"}, 27 | {`[3]`, "[\n\t3\n]"}, 28 | {`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"}, 29 | {`{"x":1}`, "{\n\t\"x\": 1\n}"}, 30 | {ex1, ex1i}, 31 | } 32 | 33 | var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]` 34 | 35 | var ex1i = `[ 36 | true, 37 | false, 38 | null, 39 | "x", 40 | 1, 41 | 1.5, 42 | 0, 43 | -5e+2 44 | ]` 45 | 46 | func TestCompact(t *testing.T) { 47 | var buf bytes.Buffer 48 | for _, tt := range examples { 49 | buf.Reset() 50 | if err := Compact(&buf, []byte(tt.compact)); err != nil { 51 | t.Errorf("Compact(%#q): %v", tt.compact, err) 52 | } else if s := buf.String(); s != tt.compact { 53 | t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s) 54 | } 55 | 56 | buf.Reset() 57 | if err := Compact(&buf, []byte(tt.indent)); err != nil { 58 | t.Errorf("Compact(%#q): %v", tt.indent, err) 59 | continue 60 | } else if s := buf.String(); s != tt.compact { 61 | t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact) 62 | } 63 | } 64 | } 65 | 66 | func TestCompactSeparators(t *testing.T) { 67 | // U+2028 and U+2029 should be escaped inside strings. 68 | // They should not appear outside strings. 69 | tests := []struct { 70 | in, compact string 71 | }{ 72 | {"{\"\u2028\": 1}", `{"\u2028":1}`}, 73 | {"{\"\u2029\" :2}", `{"\u2029":2}`}, 74 | } 75 | for _, tt := range tests { 76 | var buf bytes.Buffer 77 | if err := Compact(&buf, []byte(tt.in)); err != nil { 78 | t.Errorf("Compact(%q): %v", tt.in, err) 79 | } else if s := buf.String(); s != tt.compact { 80 | t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact) 81 | } 82 | } 83 | } 84 | 85 | func TestIndent(t *testing.T) { 86 | var buf bytes.Buffer 87 | for _, tt := range examples { 88 | buf.Reset() 89 | if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil { 90 | t.Errorf("Indent(%#q): %v", tt.indent, err) 91 | } else if s := buf.String(); s != tt.indent { 92 | t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s) 93 | } 94 | 95 | buf.Reset() 96 | if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil { 97 | t.Errorf("Indent(%#q): %v", tt.compact, err) 98 | continue 99 | } else if s := buf.String(); s != tt.indent { 100 | t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent) 101 | } 102 | } 103 | } 104 | 105 | // Tests of a large random structure. 106 | 107 | func TestCompactBig(t *testing.T) { 108 | initBig() 109 | var buf bytes.Buffer 110 | if err := Compact(&buf, jsonBig); err != nil { 111 | t.Fatalf("Compact: %v", err) 112 | } 113 | b := buf.Bytes() 114 | if !bytes.Equal(b, jsonBig) { 115 | t.Error("Compact(jsonBig) != jsonBig") 116 | diff(t, b, jsonBig) 117 | return 118 | } 119 | } 120 | 121 | func TestIndentBig(t *testing.T) { 122 | initBig() 123 | var buf bytes.Buffer 124 | if err := Indent(&buf, jsonBig, "", "\t"); err != nil { 125 | t.Fatalf("Indent1: %v", err) 126 | } 127 | b := buf.Bytes() 128 | if len(b) == len(jsonBig) { 129 | // jsonBig is compact (no unnecessary spaces); 130 | // indenting should make it bigger 131 | t.Fatalf("Indent(jsonBig) did not get bigger") 132 | } 133 | 134 | // should be idempotent 135 | var buf1 bytes.Buffer 136 | if err := Indent(&buf1, b, "", "\t"); err != nil { 137 | t.Fatalf("Indent2: %v", err) 138 | } 139 | b1 := buf1.Bytes() 140 | if !bytes.Equal(b1, b) { 141 | t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)") 142 | diff(t, b1, b) 143 | return 144 | } 145 | 146 | // should get back to original 147 | buf1.Reset() 148 | if err := Compact(&buf1, b); err != nil { 149 | t.Fatalf("Compact: %v", err) 150 | } 151 | b1 = buf1.Bytes() 152 | if !bytes.Equal(b1, jsonBig) { 153 | t.Error("Compact(Indent(jsonBig)) != jsonBig") 154 | diff(t, b1, jsonBig) 155 | return 156 | } 157 | } 158 | 159 | type indentErrorTest struct { 160 | in string 161 | err error 162 | } 163 | 164 | var indentErrorTests = []indentErrorTest{ 165 | {`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}}, 166 | {`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}}, 167 | } 168 | 169 | func TestIndentErrors(t *testing.T) { 170 | for i, tt := range indentErrorTests { 171 | slice := make([]uint8, 0) 172 | buf := bytes.NewBuffer(slice) 173 | if err := Indent(buf, []uint8(tt.in), "", ""); err != nil { 174 | if !reflect.DeepEqual(err, tt.err) { 175 | t.Errorf("#%d: Indent: %#v", i, err) 176 | continue 177 | } 178 | } 179 | } 180 | } 181 | 182 | func TestNextValueBig(t *testing.T) { 183 | initBig() 184 | var scan scanner 185 | item, rest, err := nextValue(jsonBig, &scan) 186 | if err != nil { 187 | t.Fatalf("nextValue: %s", err) 188 | } 189 | if len(item) != len(jsonBig) || &item[0] != &jsonBig[0] { 190 | t.Errorf("invalid item: %d %d", len(item), len(jsonBig)) 191 | } 192 | if len(rest) != 0 { 193 | t.Errorf("invalid rest: %d", len(rest)) 194 | } 195 | 196 | item, rest, err = nextValue(append(jsonBig, "HELLO WORLD"...), &scan) 197 | if err != nil { 198 | t.Fatalf("nextValue extra: %s", err) 199 | } 200 | if len(item) != len(jsonBig) { 201 | t.Errorf("invalid item: %d %d", len(item), len(jsonBig)) 202 | } 203 | if string(rest) != "HELLO WORLD" { 204 | t.Errorf("invalid rest: %d", len(rest)) 205 | } 206 | } 207 | 208 | var benchScan scanner 209 | 210 | func BenchmarkSkipValue(b *testing.B) { 211 | initBig() 212 | b.ResetTimer() 213 | for i := 0; i < b.N; i++ { 214 | nextValue(jsonBig, &benchScan) 215 | } 216 | b.SetBytes(int64(len(jsonBig))) 217 | } 218 | 219 | func diff(t *testing.T, a, b []byte) { 220 | for i := 0; ; i++ { 221 | if i >= len(a) || i >= len(b) || a[i] != b[i] { 222 | j := i - 10 223 | if j < 0 { 224 | j = 0 225 | } 226 | t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:])) 227 | return 228 | } 229 | } 230 | } 231 | 232 | func trim(b []byte) []byte { 233 | if len(b) > 20 { 234 | return b[0:20] 235 | } 236 | return b 237 | } 238 | 239 | // Generate a random JSON object. 240 | 241 | var jsonBig []byte 242 | 243 | func initBig() { 244 | n := 10000 245 | if testing.Short() { 246 | n = 100 247 | } 248 | b, err := Marshal(genValue(n)) 249 | if err != nil { 250 | panic(err) 251 | } 252 | jsonBig = b 253 | } 254 | 255 | func genValue(n int) interface{} { 256 | if n > 1 { 257 | switch rand.Intn(2) { 258 | case 0: 259 | return genArray(n) 260 | case 1: 261 | return genMap(n) 262 | } 263 | } 264 | switch rand.Intn(3) { 265 | case 0: 266 | return rand.Intn(2) == 0 267 | case 1: 268 | return rand.NormFloat64() 269 | case 2: 270 | return genString(30) 271 | } 272 | panic("unreachable") 273 | } 274 | 275 | func genString(stddev float64) string { 276 | n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2)) 277 | c := make([]rune, n) 278 | for i := range c { 279 | f := math.Abs(rand.NormFloat64()*64 + 32) 280 | if f > 0x10ffff { 281 | f = 0x10ffff 282 | } 283 | c[i] = rune(f) 284 | } 285 | return string(c) 286 | } 287 | 288 | func genArray(n int) []interface{} { 289 | f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) 290 | if f > n { 291 | f = n 292 | } 293 | if f < 1 { 294 | f = 1 295 | } 296 | x := make([]interface{}, f) 297 | for i := range x { 298 | x[i] = genValue(((i+1)*n)/f - (i*n)/f) 299 | } 300 | return x 301 | } 302 | 303 | func genMap(n int) map[string]interface{} { 304 | f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) 305 | if f > n { 306 | f = n 307 | } 308 | if n > 0 && f == 0 { 309 | f = 1 310 | } 311 | x := make(map[string]interface{}) 312 | for i := 0; i < f; i++ { 313 | x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f) 314 | } 315 | return x 316 | } 317 | -------------------------------------------------------------------------------- /internal/json/tagkey_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | type basicLatin2xTag struct { 12 | V string `json:"$%-/"` 13 | } 14 | 15 | type basicLatin3xTag struct { 16 | V string `json:"0123456789"` 17 | } 18 | 19 | type basicLatin4xTag struct { 20 | V string `json:"ABCDEFGHIJKLMO"` 21 | } 22 | 23 | type basicLatin5xTag struct { 24 | V string `json:"PQRSTUVWXYZ_"` 25 | } 26 | 27 | type basicLatin6xTag struct { 28 | V string `json:"abcdefghijklmno"` 29 | } 30 | 31 | type basicLatin7xTag struct { 32 | V string `json:"pqrstuvwxyz"` 33 | } 34 | 35 | type miscPlaneTag struct { 36 | V string `json:"色は匂へど"` 37 | } 38 | 39 | type percentSlashTag struct { 40 | V string `json:"text/html%"` // https://golang.org/issue/2718 41 | } 42 | 43 | type punctuationTag struct { 44 | V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546 45 | } 46 | 47 | type emptyTag struct { 48 | W string 49 | } 50 | 51 | type misnamedTag struct { 52 | X string `jsom:"Misnamed"` 53 | } 54 | 55 | type badFormatTag struct { 56 | Y string `:"BadFormat"` 57 | } 58 | 59 | type badCodeTag struct { 60 | Z string `json:" !\"#&'()*+,."` 61 | } 62 | 63 | type spaceTag struct { 64 | Q string `json:"With space"` 65 | } 66 | 67 | type unicodeTag struct { 68 | W string `json:"Ελλάδα"` 69 | } 70 | 71 | var structTagObjectKeyTests = []struct { 72 | raw interface{} 73 | value string 74 | key string 75 | }{ 76 | {basicLatin2xTag{"2x"}, "2x", "$%-/"}, 77 | {basicLatin3xTag{"3x"}, "3x", "0123456789"}, 78 | {basicLatin4xTag{"4x"}, "4x", "ABCDEFGHIJKLMO"}, 79 | {basicLatin5xTag{"5x"}, "5x", "PQRSTUVWXYZ_"}, 80 | {basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"}, 81 | {basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"}, 82 | {miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"}, 83 | {emptyTag{"Pour Moi"}, "Pour Moi", "W"}, 84 | {misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"}, 85 | {badFormatTag{"Orfevre"}, "Orfevre", "Y"}, 86 | {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, 87 | {percentSlashTag{"brut"}, "brut", "text/html%"}, 88 | {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, 89 | {spaceTag{"Perreddu"}, "Perreddu", "With space"}, 90 | {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, 91 | } 92 | 93 | func TestStructTagObjectKey(t *testing.T) { 94 | for _, tt := range structTagObjectKeyTests { 95 | b, err := Marshal(tt.raw) 96 | if err != nil { 97 | t.Fatalf("Marshal(%#q) failed: %v", tt.raw, err) 98 | } 99 | var f interface{} 100 | err = Unmarshal(b, &f) 101 | if err != nil { 102 | t.Fatalf("Unmarshal(%#q) failed: %v", b, err) 103 | } 104 | for i, v := range f.(map[string]interface{}) { 105 | switch i { 106 | case tt.key: 107 | if s, ok := v.(string); !ok || s != tt.value { 108 | t.Fatalf("Unexpected value: %#q, want %v", s, tt.value) 109 | } 110 | default: 111 | t.Fatalf("Unexpected key: %#q, from %#q", i, b) 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /internal/json/tags.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // tagOptions is the string following a comma in a struct field's "json" 12 | // tag, or the empty string. It does not include the leading comma. 13 | type tagOptions string 14 | 15 | // parseTag splits a struct field's json tag into its name and 16 | // comma-separated options. 17 | func parseTag(tag string) (string, tagOptions) { 18 | if idx := strings.Index(tag, ","); idx != -1 { 19 | return tag[:idx], tagOptions(tag[idx+1:]) 20 | } 21 | return tag, tagOptions("") 22 | } 23 | 24 | // Contains reports whether a comma-separated list of options 25 | // contains a particular substr flag. substr must be surrounded by a 26 | // string boundary or commas. 27 | func (o tagOptions) Contains(optionName string) bool { 28 | if len(o) == 0 { 29 | return false 30 | } 31 | s := string(o) 32 | for s != "" { 33 | var next string 34 | i := strings.Index(s, ",") 35 | if i >= 0 { 36 | s, next = s[:i], s[i+1:] 37 | } 38 | if s == optionName { 39 | return true 40 | } 41 | s = next 42 | } 43 | return false 44 | } 45 | -------------------------------------------------------------------------------- /internal/json/tags_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestTagParsing(t *testing.T) { 12 | name, opts := parseTag("field,foobar,foo") 13 | if name != "field" { 14 | t.Fatalf("name = %q, want field", name) 15 | } 16 | for _, tt := range []struct { 17 | opt string 18 | want bool 19 | }{ 20 | {"foobar", true}, 21 | {"foo", true}, 22 | {"bar", false}, 23 | } { 24 | if opts.Contains(tt.opt) != tt.want { 25 | t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /internal/json/testdata/code.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/internal/json/testdata/code.json.gz -------------------------------------------------------------------------------- /internal/sasl/sasl.c: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static int mgo_sasl_simple(void *context, int id, const char **result, unsigned int *len) 9 | { 10 | if (!result) { 11 | return SASL_BADPARAM; 12 | } 13 | switch (id) { 14 | case SASL_CB_USER: 15 | *result = (char *)context; 16 | break; 17 | case SASL_CB_AUTHNAME: 18 | *result = (char *)context; 19 | break; 20 | case SASL_CB_LANGUAGE: 21 | *result = NULL; 22 | break; 23 | default: 24 | return SASL_BADPARAM; 25 | } 26 | if (len) { 27 | *len = *result ? strlen(*result) : 0; 28 | } 29 | return SASL_OK; 30 | } 31 | 32 | typedef int (*callback)(void); 33 | 34 | static int mgo_sasl_secret(sasl_conn_t *conn, void *context, int id, sasl_secret_t **result) 35 | { 36 | if (!conn || !result || id != SASL_CB_PASS) { 37 | return SASL_BADPARAM; 38 | } 39 | *result = (sasl_secret_t *)context; 40 | return SASL_OK; 41 | } 42 | 43 | sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password) 44 | { 45 | sasl_callback_t *cb = malloc(4 * sizeof(sasl_callback_t)); 46 | int n = 0; 47 | 48 | size_t len = strlen(password); 49 | sasl_secret_t *secret = (sasl_secret_t*)malloc(sizeof(sasl_secret_t) + len); 50 | if (!secret) { 51 | free(cb); 52 | return NULL; 53 | } 54 | strcpy((char *)secret->data, password); 55 | secret->len = len; 56 | 57 | cb[n].id = SASL_CB_PASS; 58 | cb[n].proc = (callback)&mgo_sasl_secret; 59 | cb[n].context = secret; 60 | n++; 61 | 62 | cb[n].id = SASL_CB_USER; 63 | cb[n].proc = (callback)&mgo_sasl_simple; 64 | cb[n].context = (char*)username; 65 | n++; 66 | 67 | cb[n].id = SASL_CB_AUTHNAME; 68 | cb[n].proc = (callback)&mgo_sasl_simple; 69 | cb[n].context = (char*)username; 70 | n++; 71 | 72 | cb[n].id = SASL_CB_LIST_END; 73 | cb[n].proc = NULL; 74 | cb[n].context = NULL; 75 | 76 | return cb; 77 | } 78 | -------------------------------------------------------------------------------- /internal/sasl/sasl.go: -------------------------------------------------------------------------------- 1 | // Package sasl is an implementation detail of the mgo package. 2 | // 3 | // This package is not meant to be used by itself. 4 | // 5 | 6 | // +build !windows 7 | 8 | package sasl 9 | 10 | // #cgo LDFLAGS: -lsasl2 11 | // #cgo CFLAGS: -Wno-deprecated-declarations 12 | // 13 | // struct sasl_conn {}; 14 | // 15 | // #include 16 | // #include 17 | // 18 | // sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password); 19 | // 20 | import "C" 21 | 22 | import ( 23 | "fmt" 24 | "strings" 25 | "sync" 26 | "unsafe" 27 | ) 28 | 29 | // Stepper interface for saslSession 30 | type Stepper interface { 31 | Step(serverData []byte) (clientData []byte, done bool, err error) 32 | Close() 33 | } 34 | 35 | type saslSession struct { 36 | conn *C.sasl_conn_t 37 | step int 38 | mech string 39 | 40 | cstrings []*C.char 41 | callbacks *C.sasl_callback_t 42 | } 43 | 44 | var initError error 45 | var initOnce sync.Once 46 | 47 | func initSASL() { 48 | rc := C.sasl_client_init(nil) 49 | if rc != C.SASL_OK { 50 | initError = saslError(rc, nil, "cannot initialize SASL library") 51 | } 52 | } 53 | 54 | // New creates a new saslSession 55 | func New(username, password, mechanism, service, host string) (Stepper, error) { 56 | initOnce.Do(initSASL) 57 | if initError != nil { 58 | return nil, initError 59 | } 60 | 61 | ss := &saslSession{mech: mechanism} 62 | if service == "" { 63 | service = "mongodb" 64 | } 65 | if i := strings.Index(host, ":"); i >= 0 { 66 | host = host[:i] 67 | } 68 | ss.callbacks = C.mgo_sasl_callbacks(ss.cstr(username), ss.cstr(password)) 69 | rc := C.sasl_client_new(ss.cstr(service), ss.cstr(host), nil, nil, ss.callbacks, 0, &ss.conn) 70 | if rc != C.SASL_OK { 71 | ss.Close() 72 | return nil, saslError(rc, nil, "cannot create new SASL client") 73 | } 74 | return ss, nil 75 | } 76 | 77 | func (ss *saslSession) cstr(s string) *C.char { 78 | cstr := C.CString(s) 79 | ss.cstrings = append(ss.cstrings, cstr) 80 | return cstr 81 | } 82 | 83 | func (ss *saslSession) Close() { 84 | for _, cstr := range ss.cstrings { 85 | C.free(unsafe.Pointer(cstr)) 86 | } 87 | ss.cstrings = nil 88 | 89 | if ss.callbacks != nil { 90 | C.free(unsafe.Pointer(ss.callbacks)) 91 | } 92 | 93 | // The documentation of SASL dispose makes it clear that this should only 94 | // be done when the connection is done, not when the authentication phase 95 | // is done, because an encryption layer may have been negotiated. 96 | // Even then, we'll do this for now, because it's simpler and prevents 97 | // keeping track of this state for every socket. If it breaks, we'll fix it. 98 | C.sasl_dispose(&ss.conn) 99 | } 100 | 101 | func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) { 102 | ss.step++ 103 | if ss.step > 10 { 104 | return nil, false, fmt.Errorf("too many SASL steps without authentication") 105 | } 106 | var cclientData *C.char 107 | var cclientDataLen C.uint 108 | var rc C.int 109 | if ss.step == 1 { 110 | var mechanism *C.char // ignored - must match cred 111 | rc = C.sasl_client_start(ss.conn, ss.cstr(ss.mech), nil, &cclientData, &cclientDataLen, &mechanism) 112 | } else { 113 | var cserverData *C.char 114 | var cserverDataLen C.uint 115 | if len(serverData) > 0 { 116 | cserverData = (*C.char)(unsafe.Pointer(&serverData[0])) 117 | cserverDataLen = C.uint(len(serverData)) 118 | } 119 | rc = C.sasl_client_step(ss.conn, cserverData, cserverDataLen, nil, &cclientData, &cclientDataLen) 120 | } 121 | if cclientData != nil && cclientDataLen > 0 { 122 | clientData = C.GoBytes(unsafe.Pointer(cclientData), C.int(cclientDataLen)) 123 | } 124 | if rc == C.SASL_OK { 125 | return clientData, true, nil 126 | } 127 | if rc == C.SASL_CONTINUE { 128 | return clientData, false, nil 129 | } 130 | 131 | return nil, false, saslError(rc, ss.conn, "cannot establish SASL session") 132 | } 133 | 134 | func saslError(rc C.int, conn *C.sasl_conn_t, msg string) error { 135 | var detail string 136 | if conn == nil { 137 | detail = C.GoString(C.sasl_errstring(rc, nil, nil)) 138 | } else { 139 | detail = C.GoString(C.sasl_errdetail(conn)) 140 | } 141 | return fmt.Errorf(msg + ": " + detail) 142 | } 143 | -------------------------------------------------------------------------------- /internal/sasl/sasl_windows.c: -------------------------------------------------------------------------------- 1 | #include "sasl_windows.h" 2 | 3 | static const LPSTR SSPI_PACKAGE_NAME = "kerberos"; 4 | 5 | SECURITY_STATUS SEC_ENTRY sspi_acquire_credentials_handle(CredHandle *cred_handle, char *username, char *password, char *domain) 6 | { 7 | SEC_WINNT_AUTH_IDENTITY auth_identity; 8 | SECURITY_INTEGER ignored; 9 | 10 | auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; 11 | auth_identity.User = (LPSTR) username; 12 | auth_identity.UserLength = strlen(username); 13 | auth_identity.Password = NULL; 14 | auth_identity.PasswordLength = 0; 15 | if(password){ 16 | auth_identity.Password = (LPSTR) password; 17 | auth_identity.PasswordLength = strlen(password); 18 | } 19 | auth_identity.Domain = (LPSTR) domain; 20 | auth_identity.DomainLength = strlen(domain); 21 | return call_sspi_acquire_credentials_handle(NULL, SSPI_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, NULL, &auth_identity, NULL, NULL, cred_handle, &ignored); 22 | } 23 | 24 | int sspi_step(CredHandle *cred_handle, int has_context, CtxtHandle *context, PVOID buffer, ULONG buffer_length, PVOID *out_buffer, ULONG *out_buffer_length, char *target) 25 | { 26 | SecBufferDesc inbuf; 27 | SecBuffer in_bufs[1]; 28 | SecBufferDesc outbuf; 29 | SecBuffer out_bufs[1]; 30 | 31 | if (has_context > 0) { 32 | // If we already have a context, we now have data to send. 33 | // Put this data in an inbuf. 34 | inbuf.ulVersion = SECBUFFER_VERSION; 35 | inbuf.cBuffers = 1; 36 | inbuf.pBuffers = in_bufs; 37 | in_bufs[0].pvBuffer = buffer; 38 | in_bufs[0].cbBuffer = buffer_length; 39 | in_bufs[0].BufferType = SECBUFFER_TOKEN; 40 | } 41 | 42 | outbuf.ulVersion = SECBUFFER_VERSION; 43 | outbuf.cBuffers = 1; 44 | outbuf.pBuffers = out_bufs; 45 | out_bufs[0].pvBuffer = NULL; 46 | out_bufs[0].cbBuffer = 0; 47 | out_bufs[0].BufferType = SECBUFFER_TOKEN; 48 | 49 | ULONG context_attr = 0; 50 | 51 | int ret = call_sspi_initialize_security_context(cred_handle, 52 | has_context > 0 ? context : NULL, 53 | (LPSTR) target, 54 | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MUTUAL_AUTH, 55 | 0, 56 | SECURITY_NETWORK_DREP, 57 | has_context > 0 ? &inbuf : NULL, 58 | 0, 59 | context, 60 | &outbuf, 61 | &context_attr, 62 | NULL); 63 | 64 | *out_buffer = malloc(out_bufs[0].cbBuffer); 65 | *out_buffer_length = out_bufs[0].cbBuffer; 66 | memcpy(*out_buffer, out_bufs[0].pvBuffer, *out_buffer_length); 67 | 68 | return ret; 69 | } 70 | 71 | int sspi_send_client_authz_id(CtxtHandle *context, PVOID *buffer, ULONG *buffer_length, char *user_plus_realm) 72 | { 73 | SecPkgContext_Sizes sizes; 74 | SECURITY_STATUS status = call_sspi_query_context_attributes(context, SECPKG_ATTR_SIZES, &sizes); 75 | 76 | if (status != SEC_E_OK) { 77 | return status; 78 | } 79 | 80 | size_t user_plus_realm_length = strlen(user_plus_realm); 81 | int msgSize = 4 + user_plus_realm_length; 82 | char *msg = malloc((sizes.cbSecurityTrailer + msgSize + sizes.cbBlockSize) * sizeof(char)); 83 | msg[sizes.cbSecurityTrailer + 0] = 1; 84 | msg[sizes.cbSecurityTrailer + 1] = 0; 85 | msg[sizes.cbSecurityTrailer + 2] = 0; 86 | msg[sizes.cbSecurityTrailer + 3] = 0; 87 | memcpy(&msg[sizes.cbSecurityTrailer + 4], user_plus_realm, user_plus_realm_length); 88 | 89 | SecBuffer wrapBufs[3]; 90 | SecBufferDesc wrapBufDesc; 91 | wrapBufDesc.cBuffers = 3; 92 | wrapBufDesc.pBuffers = wrapBufs; 93 | wrapBufDesc.ulVersion = SECBUFFER_VERSION; 94 | 95 | wrapBufs[0].cbBuffer = sizes.cbSecurityTrailer; 96 | wrapBufs[0].BufferType = SECBUFFER_TOKEN; 97 | wrapBufs[0].pvBuffer = msg; 98 | 99 | wrapBufs[1].cbBuffer = msgSize; 100 | wrapBufs[1].BufferType = SECBUFFER_DATA; 101 | wrapBufs[1].pvBuffer = msg + sizes.cbSecurityTrailer; 102 | 103 | wrapBufs[2].cbBuffer = sizes.cbBlockSize; 104 | wrapBufs[2].BufferType = SECBUFFER_PADDING; 105 | wrapBufs[2].pvBuffer = msg + sizes.cbSecurityTrailer + msgSize; 106 | 107 | status = call_sspi_encrypt_message(context, SECQOP_WRAP_NO_ENCRYPT, &wrapBufDesc, 0); 108 | if (status != SEC_E_OK) { 109 | free(msg); 110 | return status; 111 | } 112 | 113 | *buffer_length = wrapBufs[0].cbBuffer + wrapBufs[1].cbBuffer + wrapBufs[2].cbBuffer; 114 | *buffer = malloc(*buffer_length); 115 | 116 | memcpy(*buffer, wrapBufs[0].pvBuffer, wrapBufs[0].cbBuffer); 117 | memcpy(*buffer + wrapBufs[0].cbBuffer, wrapBufs[1].pvBuffer, wrapBufs[1].cbBuffer); 118 | memcpy(*buffer + wrapBufs[0].cbBuffer + wrapBufs[1].cbBuffer, wrapBufs[2].pvBuffer, wrapBufs[2].cbBuffer); 119 | 120 | free(msg); 121 | return SEC_E_OK; 122 | } 123 | -------------------------------------------------------------------------------- /internal/sasl/sasl_windows.go: -------------------------------------------------------------------------------- 1 | package sasl 2 | 3 | // #include "sasl_windows.h" 4 | import "C" 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | "sync" 10 | "unsafe" 11 | ) 12 | 13 | type saslStepper interface { 14 | Step(serverData []byte) (clientData []byte, done bool, err error) 15 | Close() 16 | } 17 | 18 | type saslSession struct { 19 | // Credentials 20 | mech string 21 | service string 22 | host string 23 | userPlusRealm string 24 | target string 25 | domain string 26 | 27 | // Internal state 28 | authComplete bool 29 | errored bool 30 | step int 31 | 32 | // C internal state 33 | credHandle C.CredHandle 34 | context C.CtxtHandle 35 | hasContext C.int 36 | 37 | // Keep track of pointers we need to explicitly free 38 | stringsToFree []*C.char 39 | } 40 | 41 | var initError error 42 | var initOnce sync.Once 43 | 44 | func initSSPI() { 45 | rc := C.load_secur32_dll() 46 | if rc != 0 { 47 | initError = fmt.Errorf("Error loading libraries: %v", rc) 48 | } 49 | } 50 | 51 | func New(username, password, mechanism, service, host string) (saslStepper, error) { 52 | initOnce.Do(initSSPI) 53 | ss := &saslSession{mech: mechanism, hasContext: 0, userPlusRealm: username} 54 | if service == "" { 55 | service = "mongodb" 56 | } 57 | if i := strings.Index(host, ":"); i >= 0 { 58 | host = host[:i] 59 | } 60 | ss.service = service 61 | ss.host = host 62 | 63 | usernameComponents := strings.Split(username, "@") 64 | if len(usernameComponents) < 2 { 65 | return nil, fmt.Errorf("Username '%v' doesn't contain a realm!", username) 66 | } 67 | user := usernameComponents[0] 68 | ss.domain = usernameComponents[1] 69 | ss.target = fmt.Sprintf("%s/%s", ss.service, ss.host) 70 | 71 | var status C.SECURITY_STATUS 72 | // Step 0: call AcquireCredentialsHandle to get a nice SSPI CredHandle 73 | if len(password) > 0 { 74 | status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), ss.cstr(password), ss.cstr(ss.domain)) 75 | } else { 76 | status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), nil, ss.cstr(ss.domain)) 77 | } 78 | if status != C.SEC_E_OK { 79 | ss.errored = true 80 | return nil, fmt.Errorf("Couldn't create new SSPI client, error code %v", status) 81 | } 82 | return ss, nil 83 | } 84 | 85 | func (ss *saslSession) cstr(s string) *C.char { 86 | cstr := C.CString(s) 87 | ss.stringsToFree = append(ss.stringsToFree, cstr) 88 | return cstr 89 | } 90 | 91 | func (ss *saslSession) Close() { 92 | for _, cstr := range ss.stringsToFree { 93 | C.free(unsafe.Pointer(cstr)) 94 | } 95 | } 96 | 97 | func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) { 98 | ss.step++ 99 | if ss.step > 10 { 100 | return nil, false, fmt.Errorf("too many SSPI steps without authentication") 101 | } 102 | var buffer C.PVOID 103 | var bufferLength C.ULONG 104 | var outBuffer C.PVOID 105 | var outBufferLength C.ULONG 106 | if len(serverData) > 0 { 107 | buffer = (C.PVOID)(unsafe.Pointer(&serverData[0])) 108 | bufferLength = C.ULONG(len(serverData)) 109 | } 110 | var status C.int 111 | if ss.authComplete { 112 | // Step 3: last bit of magic to use the correct server credentials 113 | status = C.sspi_send_client_authz_id(&ss.context, &outBuffer, &outBufferLength, ss.cstr(ss.userPlusRealm)) 114 | } else { 115 | // Step 1 + Step 2: set up security context with the server and TGT 116 | status = C.sspi_step(&ss.credHandle, ss.hasContext, &ss.context, buffer, bufferLength, &outBuffer, &outBufferLength, ss.cstr(ss.target)) 117 | } 118 | if outBuffer != C.PVOID(nil) { 119 | defer C.free(unsafe.Pointer(outBuffer)) 120 | } 121 | if status != C.SEC_E_OK && status != C.SEC_I_CONTINUE_NEEDED { 122 | ss.errored = true 123 | return nil, false, ss.handleSSPIErrorCode(status) 124 | } 125 | 126 | clientData = C.GoBytes(unsafe.Pointer(outBuffer), C.int(outBufferLength)) 127 | if status == C.SEC_E_OK { 128 | ss.authComplete = true 129 | return clientData, true, nil 130 | } else { 131 | ss.hasContext = 1 132 | return clientData, false, nil 133 | } 134 | } 135 | 136 | func (ss *saslSession) handleSSPIErrorCode(code C.int) error { 137 | switch { 138 | case code == C.SEC_E_TARGET_UNKNOWN: 139 | return fmt.Errorf("Target %v@%v not found", ss.target, ss.domain) 140 | } 141 | return fmt.Errorf("Unknown error doing step %v, error code %v", ss.step, code) 142 | } 143 | -------------------------------------------------------------------------------- /internal/sasl/sasl_windows.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "sspi_windows.h" 4 | 5 | SECURITY_STATUS SEC_ENTRY sspi_acquire_credentials_handle(CredHandle* cred_handle, char* username, char* password, char* domain); 6 | int sspi_step(CredHandle* cred_handle, int has_context, CtxtHandle* context, PVOID buffer, ULONG buffer_length, PVOID* out_buffer, ULONG* out_buffer_length, char* target); 7 | int sspi_send_client_authz_id(CtxtHandle* context, PVOID* buffer, ULONG* buffer_length, char* user_plus_realm); 8 | -------------------------------------------------------------------------------- /internal/sasl/sspi_windows.c: -------------------------------------------------------------------------------- 1 | // Code adapted from the NodeJS kerberos library: 2 | // 3 | // https://github.com/christkv/kerberos/tree/master/lib/win32/kerberos_sspi.c 4 | // 5 | // Under the terms of the Apache License, Version 2.0: 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | #include 10 | 11 | #include "sspi_windows.h" 12 | 13 | static HINSTANCE sspi_secur32_dll = NULL; 14 | 15 | int load_secur32_dll() 16 | { 17 | sspi_secur32_dll = LoadLibrary("secur32.dll"); 18 | if (sspi_secur32_dll == NULL) { 19 | return GetLastError(); 20 | } 21 | return 0; 22 | } 23 | 24 | SECURITY_STATUS SEC_ENTRY call_sspi_encrypt_message(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo) 25 | { 26 | if (sspi_secur32_dll == NULL) { 27 | return -1; 28 | } 29 | encryptMessage_fn pfn_encryptMessage = (encryptMessage_fn) GetProcAddress(sspi_secur32_dll, "EncryptMessage"); 30 | if (!pfn_encryptMessage) { 31 | return -2; 32 | } 33 | return (*pfn_encryptMessage)(phContext, fQOP, pMessage, MessageSeqNo); 34 | } 35 | 36 | SECURITY_STATUS SEC_ENTRY call_sspi_acquire_credentials_handle( 37 | LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse, 38 | void *pvLogonId, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument, 39 | PCredHandle phCredential, PTimeStamp ptsExpiry) 40 | { 41 | if (sspi_secur32_dll == NULL) { 42 | return -1; 43 | } 44 | acquireCredentialsHandle_fn pfn_acquireCredentialsHandle; 45 | #ifdef _UNICODE 46 | pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn) GetProcAddress(sspi_secur32_dll, "AcquireCredentialsHandleW"); 47 | #else 48 | pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn) GetProcAddress(sspi_secur32_dll, "AcquireCredentialsHandleA"); 49 | #endif 50 | if (!pfn_acquireCredentialsHandle) { 51 | return -2; 52 | } 53 | return (*pfn_acquireCredentialsHandle)( 54 | pszPrincipal, pszPackage, fCredentialUse, pvLogonId, pAuthData, 55 | pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry); 56 | } 57 | 58 | SECURITY_STATUS SEC_ENTRY call_sspi_initialize_security_context( 59 | PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, 60 | unsigned long fContextReq, unsigned long Reserved1, unsigned long TargetDataRep, 61 | PSecBufferDesc pInput, unsigned long Reserved2, PCtxtHandle phNewContext, 62 | PSecBufferDesc pOutput, unsigned long *pfContextAttr, PTimeStamp ptsExpiry) 63 | { 64 | if (sspi_secur32_dll == NULL) { 65 | return -1; 66 | } 67 | initializeSecurityContext_fn pfn_initializeSecurityContext; 68 | #ifdef _UNICODE 69 | pfn_initializeSecurityContext = (initializeSecurityContext_fn) GetProcAddress(sspi_secur32_dll, "InitializeSecurityContextW"); 70 | #else 71 | pfn_initializeSecurityContext = (initializeSecurityContext_fn) GetProcAddress(sspi_secur32_dll, "InitializeSecurityContextA"); 72 | #endif 73 | if (!pfn_initializeSecurityContext) { 74 | return -2; 75 | } 76 | return (*pfn_initializeSecurityContext)( 77 | phCredential, phContext, pszTargetName, fContextReq, Reserved1, TargetDataRep, 78 | pInput, Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry); 79 | } 80 | 81 | SECURITY_STATUS SEC_ENTRY call_sspi_query_context_attributes(PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer) 82 | { 83 | if (sspi_secur32_dll == NULL) { 84 | return -1; 85 | } 86 | queryContextAttributes_fn pfn_queryContextAttributes; 87 | #ifdef _UNICODE 88 | pfn_queryContextAttributes = (queryContextAttributes_fn) GetProcAddress(sspi_secur32_dll, "QueryContextAttributesW"); 89 | #else 90 | pfn_queryContextAttributes = (queryContextAttributes_fn) GetProcAddress(sspi_secur32_dll, "QueryContextAttributesA"); 91 | #endif 92 | if (!pfn_queryContextAttributes) { 93 | return -2; 94 | } 95 | return (*pfn_queryContextAttributes)(phContext, ulAttribute, pBuffer); 96 | } 97 | -------------------------------------------------------------------------------- /internal/sasl/sspi_windows.h: -------------------------------------------------------------------------------- 1 | // Code adapted from the NodeJS kerberos library: 2 | // 3 | // https://github.com/christkv/kerberos/tree/master/lib/win32/kerberos_sspi.h 4 | // 5 | // Under the terms of the Apache License, Version 2.0: 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | #ifndef SSPI_WINDOWS_H 10 | #define SSPI_WINDOWS_H 11 | 12 | #define SECURITY_WIN32 1 13 | 14 | #include 15 | #include 16 | 17 | int load_secur32_dll(); 18 | 19 | SECURITY_STATUS SEC_ENTRY call_sspi_encrypt_message(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo); 20 | 21 | typedef DWORD (WINAPI *encryptMessage_fn)(PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo); 22 | 23 | SECURITY_STATUS SEC_ENTRY call_sspi_acquire_credentials_handle( 24 | LPSTR pszPrincipal, // Name of principal 25 | LPSTR pszPackage, // Name of package 26 | unsigned long fCredentialUse, // Flags indicating use 27 | void *pvLogonId, // Pointer to logon ID 28 | void *pAuthData, // Package specific data 29 | SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func 30 | void *pvGetKeyArgument, // Value to pass to GetKey() 31 | PCredHandle phCredential, // (out) Cred Handle 32 | PTimeStamp ptsExpiry // (out) Lifetime (optional) 33 | ); 34 | 35 | typedef DWORD (WINAPI *acquireCredentialsHandle_fn)( 36 | LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse, 37 | void *pvLogonId, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument, 38 | PCredHandle phCredential, PTimeStamp ptsExpiry 39 | ); 40 | 41 | SECURITY_STATUS SEC_ENTRY call_sspi_initialize_security_context( 42 | PCredHandle phCredential, // Cred to base context 43 | PCtxtHandle phContext, // Existing context (OPT) 44 | LPSTR pszTargetName, // Name of target 45 | unsigned long fContextReq, // Context Requirements 46 | unsigned long Reserved1, // Reserved, MBZ 47 | unsigned long TargetDataRep, // Data rep of target 48 | PSecBufferDesc pInput, // Input Buffers 49 | unsigned long Reserved2, // Reserved, MBZ 50 | PCtxtHandle phNewContext, // (out) New Context handle 51 | PSecBufferDesc pOutput, // (inout) Output Buffers 52 | unsigned long *pfContextAttr, // (out) Context attrs 53 | PTimeStamp ptsExpiry // (out) Life span (OPT) 54 | ); 55 | 56 | typedef DWORD (WINAPI *initializeSecurityContext_fn)( 57 | PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, unsigned long fContextReq, 58 | unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput, unsigned long Reserved2, 59 | PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long *pfContextAttr, PTimeStamp ptsExpiry); 60 | 61 | SECURITY_STATUS SEC_ENTRY call_sspi_query_context_attributes( 62 | PCtxtHandle phContext, // Context to query 63 | unsigned long ulAttribute, // Attribute to query 64 | void *pBuffer // Buffer for attributes 65 | ); 66 | 67 | typedef DWORD (WINAPI *queryContextAttributes_fn)( 68 | PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer); 69 | 70 | #endif // SSPI_WINDOWS_H 71 | -------------------------------------------------------------------------------- /internal/scram/scram_test.go: -------------------------------------------------------------------------------- 1 | package scram_test 2 | 3 | import ( 4 | "crypto/sha1" 5 | "testing" 6 | 7 | "strings" 8 | 9 | "github.com/globalsign/mgo/internal/scram" 10 | . "gopkg.in/check.v1" 11 | ) 12 | 13 | var _ = Suite(&S{}) 14 | 15 | func Test(t *testing.T) { TestingT(t) } 16 | 17 | type S struct{} 18 | 19 | var tests = [][]string{{ 20 | "U: user pencil", 21 | "N: fyko+d2lbbFgONRv9qkxdawL", 22 | "C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL", 23 | "S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096", 24 | "C: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=", 25 | "S: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=", 26 | }, { 27 | "U: root fe8c89e308ec08763df36333cbf5d3a2", 28 | "N: OTcxNDk5NjM2MzE5", 29 | "C: n,,n=root,r=OTcxNDk5NjM2MzE5", 30 | "S: r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,s=XRDkVrFC9JuL7/F4tG0acQ==,i=10000", 31 | "C: c=biws,r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,p=6y1jp9R7ETyouTXS9fW9k5UHdBc=", 32 | "S: v=LBnd9dUJRxdqZiEq91NKP3z/bHA=", 33 | }} 34 | 35 | func (s *S) TestExamples(c *C) { 36 | for _, steps := range tests { 37 | if len(steps) < 2 || len(steps[0]) < 3 || !strings.HasPrefix(steps[0], "U: ") { 38 | c.Fatalf("Invalid test: %#v", steps) 39 | } 40 | auth := strings.Fields(steps[0][3:]) 41 | client := scram.NewClient(sha1.New, auth[0], auth[1]) 42 | first, done := true, false 43 | c.Logf("-----") 44 | c.Logf("%s", steps[0]) 45 | for _, step := range steps[1:] { 46 | c.Logf("%s", step) 47 | switch step[:3] { 48 | case "N: ": 49 | client.SetNonce([]byte(step[3:])) 50 | case "C: ": 51 | if first { 52 | first = false 53 | done = client.Step(nil) 54 | } 55 | c.Assert(done, Equals, false) 56 | c.Assert(client.Err(), IsNil) 57 | c.Assert(string(client.Out()), Equals, step[3:]) 58 | case "S: ": 59 | first = false 60 | done = client.Step([]byte(step[3:])) 61 | default: 62 | panic("invalid test line: " + step) 63 | } 64 | } 65 | c.Assert(done, Equals, true) 66 | c.Assert(client.Err(), IsNil) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo 28 | 29 | import ( 30 | "fmt" 31 | "sync" 32 | ) 33 | 34 | // --------------------------------------------------------------------------- 35 | // Logging integration. 36 | 37 | // LogLogger avoid importing the log type information unnecessarily. There's a small cost 38 | // associated with using an interface rather than the type. Depending on how 39 | // often the logger is plugged in, it would be worth using the type instead. 40 | type logLogger interface { 41 | Output(calldepth int, s string) error 42 | } 43 | 44 | var ( 45 | globalLogger logLogger 46 | globalDebug bool 47 | globalMutex sync.Mutex 48 | ) 49 | 50 | // RACE WARNING: There are known data races when logging, which are manually 51 | // silenced when the race detector is in use. These data races won't be 52 | // observed in typical use, because logging is supposed to be set up once when 53 | // the application starts. Having raceDetector as a constant, the compiler 54 | // should elide the locks altogether in actual use. 55 | 56 | // SetLogger specify the *log.Logger object where log messages should be sent to. 57 | func SetLogger(logger logLogger) { 58 | if raceDetector { 59 | globalMutex.Lock() 60 | defer globalMutex.Unlock() 61 | } 62 | globalLogger = logger 63 | } 64 | 65 | // SetDebug enable the delivery of debug messages to the logger. Only meaningful 66 | // if a logger is also set. 67 | func SetDebug(debug bool) { 68 | if raceDetector { 69 | globalMutex.Lock() 70 | defer globalMutex.Unlock() 71 | } 72 | globalDebug = debug 73 | } 74 | 75 | func log(v ...interface{}) { 76 | if raceDetector { 77 | globalMutex.Lock() 78 | defer globalMutex.Unlock() 79 | } 80 | if globalLogger != nil { 81 | globalLogger.Output(2, fmt.Sprint(v...)) 82 | } 83 | } 84 | 85 | func logln(v ...interface{}) { 86 | if raceDetector { 87 | globalMutex.Lock() 88 | defer globalMutex.Unlock() 89 | } 90 | if globalLogger != nil { 91 | globalLogger.Output(2, fmt.Sprintln(v...)) 92 | } 93 | } 94 | 95 | func logf(format string, v ...interface{}) { 96 | if raceDetector { 97 | globalMutex.Lock() 98 | defer globalMutex.Unlock() 99 | } 100 | if globalLogger != nil { 101 | globalLogger.Output(2, fmt.Sprintf(format, v...)) 102 | } 103 | } 104 | 105 | func debug(v ...interface{}) { 106 | if raceDetector { 107 | globalMutex.Lock() 108 | defer globalMutex.Unlock() 109 | } 110 | if globalDebug && globalLogger != nil { 111 | globalLogger.Output(2, fmt.Sprint(v...)) 112 | } 113 | } 114 | 115 | func debugln(v ...interface{}) { 116 | if raceDetector { 117 | globalMutex.Lock() 118 | defer globalMutex.Unlock() 119 | } 120 | if globalDebug && globalLogger != nil { 121 | globalLogger.Output(2, fmt.Sprintln(v...)) 122 | } 123 | } 124 | 125 | func debugf(format string, v ...interface{}) { 126 | if raceDetector { 127 | globalMutex.Lock() 128 | defer globalMutex.Unlock() 129 | } 130 | if globalDebug && globalLogger != nil { 131 | globalLogger.Output(2, fmt.Sprintf(format, v...)) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /queue.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo 28 | 29 | type queue struct { 30 | elems []interface{} 31 | nelems, popi, pushi int 32 | } 33 | 34 | func (q *queue) Len() int { 35 | return q.nelems 36 | } 37 | 38 | func (q *queue) Push(elem interface{}) { 39 | //debugf("Pushing(pushi=%d popi=%d cap=%d): %#v\n", 40 | // q.pushi, q.popi, len(q.elems), elem) 41 | if q.nelems == len(q.elems) { 42 | q.expand() 43 | } 44 | q.elems[q.pushi] = elem 45 | q.nelems++ 46 | q.pushi = (q.pushi + 1) % len(q.elems) 47 | //debugf(" Pushed(pushi=%d popi=%d cap=%d): %#v\n", 48 | // q.pushi, q.popi, len(q.elems), elem) 49 | } 50 | 51 | func (q *queue) Pop() (elem interface{}) { 52 | //debugf("Popping(pushi=%d popi=%d cap=%d)\n", 53 | // q.pushi, q.popi, len(q.elems)) 54 | if q.nelems == 0 { 55 | return nil 56 | } 57 | elem = q.elems[q.popi] 58 | q.elems[q.popi] = nil // Help GC. 59 | q.nelems-- 60 | q.popi = (q.popi + 1) % len(q.elems) 61 | //debugf(" Popped(pushi=%d popi=%d cap=%d): %#v\n", 62 | // q.pushi, q.popi, len(q.elems), elem) 63 | return elem 64 | } 65 | 66 | func (q *queue) expand() { 67 | curcap := len(q.elems) 68 | var newcap int 69 | if curcap == 0 { 70 | newcap = 8 71 | } else if curcap < 1024 { 72 | newcap = curcap * 2 73 | } else { 74 | newcap = curcap + (curcap / 4) 75 | } 76 | elems := make([]interface{}, newcap) 77 | 78 | if q.popi == 0 { 79 | copy(elems, q.elems) 80 | q.pushi = curcap 81 | } else { 82 | newpopi := newcap - (curcap - q.popi) 83 | copy(elems, q.elems[:q.popi]) 84 | copy(elems[newpopi:], q.elems[q.popi:]) 85 | q.popi = newpopi 86 | } 87 | for i := range q.elems { 88 | q.elems[i] = nil // Help GC. 89 | } 90 | q.elems = elems 91 | } 92 | -------------------------------------------------------------------------------- /queue_test.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo 28 | 29 | import ( 30 | . "gopkg.in/check.v1" 31 | ) 32 | 33 | type QS struct{} 34 | 35 | var _ = Suite(&QS{}) 36 | 37 | func (s *QS) TestSequentialGrowth(c *C) { 38 | q := queue{} 39 | n := 2048 40 | for i := 0; i != n; i++ { 41 | q.Push(i) 42 | } 43 | for i := 0; i != n; i++ { 44 | c.Assert(q.Pop(), Equals, i) 45 | } 46 | } 47 | 48 | var queueTestLists = [][]int{ 49 | // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 50 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 51 | 52 | // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} 53 | {0, 1, 2, 3, 4, 5, 6, 7, -1, -1, 8, 9, 10, 11}, 54 | 55 | // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} 56 | {0, 1, 2, 3, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11}, 57 | 58 | // {0, 1, 2, 3, 4, 5, 6, 7, 8} 59 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 60 | -1, -1, -1, -1, -1, -1, -1, -1, -1, 61 | 0, 1, 2, 3, 4, 5, 6, 7, 8}, 62 | } 63 | 64 | func (s *QS) TestQueueTestLists(c *C) { 65 | test := []int{} 66 | testi := 0 67 | reset := func() { 68 | test = test[0:0] 69 | testi = 0 70 | } 71 | push := func(i int) { 72 | test = append(test, i) 73 | } 74 | pop := func() (i int) { 75 | if testi == len(test) { 76 | return -1 77 | } 78 | i = test[testi] 79 | testi++ 80 | return 81 | } 82 | 83 | for _, list := range queueTestLists { 84 | reset() 85 | q := queue{} 86 | for _, n := range list { 87 | if n == -1 { 88 | c.Assert(q.Pop(), Equals, pop(), Commentf("With list %#v", list)) 89 | } else { 90 | q.Push(n) 91 | push(n) 92 | } 93 | } 94 | 95 | for n := pop(); n != -1; n = pop() { 96 | c.Assert(q.Pop(), Equals, n, Commentf("With list %#v", list)) 97 | } 98 | 99 | c.Assert(q.Pop(), Equals, nil, Commentf("With list %#v", list)) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /raceoff.go: -------------------------------------------------------------------------------- 1 | // +build !race 2 | 3 | package mgo 4 | 5 | const raceDetector = false 6 | -------------------------------------------------------------------------------- /raceon.go: -------------------------------------------------------------------------------- 1 | // +build race 2 | 3 | package mgo 4 | 5 | const raceDetector = true 6 | -------------------------------------------------------------------------------- /saslimpl.go: -------------------------------------------------------------------------------- 1 | //+build sasl 2 | 3 | package mgo 4 | 5 | import ( 6 | "github.com/globalsign/mgo/internal/sasl" 7 | ) 8 | 9 | func saslNew(cred Credential, host string) (saslStepper, error) { 10 | return sasl.New(cred.Username, cred.Password, cred.Mechanism, cred.Service, host) 11 | } 12 | -------------------------------------------------------------------------------- /saslstub.go: -------------------------------------------------------------------------------- 1 | //+build !sasl 2 | 3 | package mgo 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func saslNew(cred Credential, host string) (saslStepper, error) { 10 | return nil, fmt.Errorf("SASL support not enabled during build (-tags sasl)") 11 | } 12 | -------------------------------------------------------------------------------- /server_test.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2018 Canonical Ltd 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo_test 28 | 29 | import ( 30 | "time" 31 | 32 | "github.com/globalsign/mgo" 33 | . "gopkg.in/check.v1" 34 | ) 35 | 36 | func (s *S) TestServerRecoversFromAbend(c *C) { 37 | session, err := mgo.Dial("localhost:40001") 38 | c.Assert(err, IsNil) 39 | defer session.Close() 40 | // Peek behind the scenes 41 | cluster := session.Cluster() 42 | server := cluster.Server("127.0.0.1:40001") 43 | 44 | info := &mgo.DialInfo{ 45 | Timeout: time.Second, 46 | PoolLimit: 100, 47 | } 48 | 49 | sock, abended, err := server.AcquireSocket(info) 50 | c.Assert(err, IsNil) 51 | c.Assert(sock, NotNil) 52 | sock.Release() 53 | c.Check(abended, Equals, false) 54 | // Forcefully abend this socket 55 | sock.Close() 56 | server.AbendSocket(sock) 57 | // Next acquire notices the connection was abnormally ended 58 | sock, abended, err = server.AcquireSocket(info) 59 | c.Assert(err, IsNil) 60 | sock.Release() 61 | c.Check(abended, Equals, true) 62 | // cluster.AcquireSocketWithPoolTimeout should fix the abended problems 63 | sock, err = cluster.AcquireSocketWithPoolTimeout(mgo.Primary, false, time.Minute, nil, info) 64 | c.Assert(err, IsNil) 65 | sock.Release() 66 | sock, abended, err = server.AcquireSocket(info) 67 | c.Assert(err, IsNil) 68 | c.Check(abended, Equals, false) 69 | sock.Release() 70 | } 71 | -------------------------------------------------------------------------------- /session_internal_test.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | import ( 4 | "crypto/x509/pkix" 5 | "encoding/asn1" 6 | "testing" 7 | "time" 8 | 9 | "github.com/globalsign/mgo/bson" 10 | . "gopkg.in/check.v1" 11 | ) 12 | 13 | type S struct{} 14 | 15 | var _ = Suite(&S{}) 16 | 17 | // This file is for testing functions that are not exported outside the mgo 18 | // package - avoid doing so if at all possible. 19 | 20 | // Ensures indexed int64 fields do not cause mgo to panic. 21 | // 22 | // See https://github.com/globalsign/mgo/pull/23 23 | func TestIndexedInt64FieldsBug(t *testing.T) { 24 | input := bson.D{ 25 | {Name: "testkey", Value: int(1)}, 26 | {Name: "testkey", Value: int64(1)}, 27 | {Name: "testkey", Value: "test"}, 28 | {Name: "testkey", Value: float64(1)}, 29 | } 30 | 31 | _ = simpleIndexKey(input) 32 | } 33 | 34 | func (s *S) TestGetRFC2253NameStringSingleValued(c *C) { 35 | var RDNElements = pkix.RDNSequence{ 36 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 6}, Value: "GO"}}, 37 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 8}, Value: "MGO"}}, 38 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 7}, Value: "MGO"}}, 39 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 10}, Value: "MGO"}}, 40 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 11}, Value: "Client"}}, 41 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 3}, Value: "localhost"}}, 42 | } 43 | 44 | c.Assert(getRFC2253NameString(&RDNElements), Equals, "CN=localhost,OU=Client,O=MGO,L=MGO,ST=MGO,C=GO") 45 | } 46 | 47 | func (s *S) TestGetRFC2253NameStringEscapeChars(c *C) { 48 | var RDNElements = pkix.RDNSequence{ 49 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 6}, Value: "GB"}}, 50 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 8}, Value: "MGO "}}, 51 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 10}, Value: "Sue, Grabbit and Runn < > ;"}}, 52 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 3}, Value: "L. Eagle"}}, 53 | } 54 | 55 | c.Assert(getRFC2253NameString(&RDNElements), Equals, "CN=L. Eagle,O=Sue\\, Grabbit and Runn \\< \\> \\;,ST=MGO\\ ,C=GB") 56 | } 57 | 58 | func (s *S) TestGetRFC2253NameStringMultiValued(c *C) { 59 | var RDNElements = pkix.RDNSequence{ 60 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 6}, Value: "US"}}, 61 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 10}, Value: "Widget Inc."}}, 62 | {{Type: asn1.ObjectIdentifier{2, 5, 4, 11}, Value: "Sales"}, {Type: asn1.ObjectIdentifier{2, 5, 4, 3}, Value: "J. Smith"}}, 63 | } 64 | 65 | c.Assert(getRFC2253NameString(&RDNElements), Equals, "OU=Sales+CN=J. Smith,O=Widget Inc.,C=US") 66 | } 67 | 68 | func (s *S) TestDialTimeouts(c *C) { 69 | info := &DialInfo{} 70 | 71 | c.Assert(info.readTimeout(), Equals, time.Duration(0)) 72 | c.Assert(info.writeTimeout(), Equals, time.Duration(0)) 73 | c.Assert(info.roundTripTimeout(), Equals, time.Duration(0)) 74 | 75 | info.Timeout = 60 * time.Second 76 | c.Assert(info.readTimeout(), Equals, 60*time.Second) 77 | c.Assert(info.writeTimeout(), Equals, 60*time.Second) 78 | c.Assert(info.roundTripTimeout(), Equals, 120*time.Second) 79 | 80 | info.ReadTimeout = time.Second 81 | c.Assert(info.readTimeout(), Equals, time.Second) 82 | 83 | info.WriteTimeout = time.Second 84 | c.Assert(info.writeTimeout(), Equals, time.Second) 85 | } 86 | -------------------------------------------------------------------------------- /stats.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo 28 | 29 | import ( 30 | "sync" 31 | "time" 32 | ) 33 | 34 | var stats *Stats 35 | var statsMutex sync.Mutex 36 | 37 | // SetStats enable database state monitoring 38 | func SetStats(enabled bool) { 39 | statsMutex.Lock() 40 | if enabled { 41 | if stats == nil { 42 | stats = &Stats{} 43 | } 44 | } else { 45 | stats = nil 46 | } 47 | statsMutex.Unlock() 48 | } 49 | 50 | // GetStats return the current database state 51 | func GetStats() (snapshot Stats) { 52 | statsMutex.Lock() 53 | snapshot = *stats 54 | statsMutex.Unlock() 55 | return 56 | } 57 | 58 | // ResetStats reset Stats to the previous database state 59 | func ResetStats() { 60 | statsMutex.Lock() 61 | debug("Resetting stats") 62 | old := stats 63 | stats = &Stats{} 64 | // These are absolute values: 65 | stats.Clusters = old.Clusters 66 | stats.SocketsInUse = old.SocketsInUse 67 | stats.SocketsAlive = old.SocketsAlive 68 | stats.SocketRefs = old.SocketRefs 69 | statsMutex.Unlock() 70 | return 71 | } 72 | 73 | // Stats holds info on the database state 74 | // 75 | // Relevant documentation: 76 | // 77 | // https://docs.mongodb.com/manual/reference/command/serverStatus/ 78 | // 79 | // TODO outdated fields ? 80 | type Stats struct { 81 | Clusters int 82 | MasterConns int 83 | SlaveConns int 84 | SentOps int 85 | ReceivedOps int 86 | ReceivedDocs int 87 | SocketsAlive int 88 | SocketsInUse int 89 | SocketRefs int 90 | TimesSocketAcquired int 91 | TimesWaitedForPool int 92 | TotalPoolWaitTime time.Duration 93 | PoolTimeouts int 94 | } 95 | 96 | func (stats *Stats) cluster(delta int) { 97 | if stats != nil { 98 | statsMutex.Lock() 99 | stats.Clusters += delta 100 | statsMutex.Unlock() 101 | } 102 | } 103 | 104 | func (stats *Stats) conn(delta int, master bool) { 105 | if stats != nil { 106 | statsMutex.Lock() 107 | if master { 108 | stats.MasterConns += delta 109 | } else { 110 | stats.SlaveConns += delta 111 | } 112 | statsMutex.Unlock() 113 | } 114 | } 115 | 116 | func (stats *Stats) sentOps(delta int) { 117 | if stats != nil { 118 | statsMutex.Lock() 119 | stats.SentOps += delta 120 | statsMutex.Unlock() 121 | } 122 | } 123 | 124 | func (stats *Stats) receivedOps(delta int) { 125 | if stats != nil { 126 | statsMutex.Lock() 127 | stats.ReceivedOps += delta 128 | statsMutex.Unlock() 129 | } 130 | } 131 | 132 | func (stats *Stats) receivedDocs(delta int) { 133 | if stats != nil { 134 | statsMutex.Lock() 135 | stats.ReceivedDocs += delta 136 | statsMutex.Unlock() 137 | } 138 | } 139 | 140 | func (stats *Stats) socketsInUse(delta int) { 141 | if stats != nil { 142 | statsMutex.Lock() 143 | stats.SocketsInUse += delta 144 | statsMutex.Unlock() 145 | } 146 | } 147 | 148 | func (stats *Stats) socketsAlive(delta int) { 149 | if stats != nil { 150 | statsMutex.Lock() 151 | stats.SocketsAlive += delta 152 | statsMutex.Unlock() 153 | } 154 | } 155 | 156 | func (stats *Stats) socketRefs(delta int) { 157 | if stats != nil { 158 | statsMutex.Lock() 159 | stats.SocketRefs += delta 160 | statsMutex.Unlock() 161 | } 162 | } 163 | 164 | func (stats *Stats) noticeSocketAcquisition(waitTime time.Duration) { 165 | if stats != nil { 166 | statsMutex.Lock() 167 | stats.TimesSocketAcquired++ 168 | stats.TotalPoolWaitTime += waitTime 169 | if waitTime > 0 { 170 | stats.TimesWaitedForPool++ 171 | } 172 | statsMutex.Unlock() 173 | } 174 | } 175 | 176 | func (stats *Stats) noticePoolTimeout(waitTime time.Duration) { 177 | if stats != nil { 178 | statsMutex.Lock() 179 | stats.TimesWaitedForPool++ 180 | stats.PoolTimeouts++ 181 | stats.TotalPoolWaitTime += waitTime 182 | statsMutex.Unlock() 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /suite_test.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo_test 28 | 29 | import ( 30 | "bytes" 31 | "errors" 32 | "flag" 33 | "fmt" 34 | "net" 35 | "os/exec" 36 | "runtime" 37 | "strconv" 38 | "testing" 39 | "time" 40 | 41 | mgo "github.com/globalsign/mgo" 42 | "github.com/globalsign/mgo/bson" 43 | . "gopkg.in/check.v1" 44 | ) 45 | 46 | var fast = flag.Bool("fast", false, "Skip slow tests") 47 | 48 | type M bson.M 49 | 50 | type cLogger C 51 | 52 | func (c *cLogger) Output(calldepth int, s string) error { 53 | ns := time.Now().UnixNano() 54 | t := float64(ns%100e9) / 1e9 55 | ((*C)(c)).Logf("[LOG] %.05f %s", t, s) 56 | return nil 57 | } 58 | 59 | func TestAll(t *testing.T) { 60 | TestingT(t) 61 | } 62 | 63 | type S struct { 64 | session *mgo.Session 65 | stopped bool 66 | build mgo.BuildInfo 67 | frozen []string 68 | } 69 | 70 | func (s *S) versionAtLeast(v ...int) (result bool) { 71 | for i := range v { 72 | if i == len(s.build.VersionArray) { 73 | return false 74 | } 75 | if s.build.VersionArray[i] != v[i] { 76 | return s.build.VersionArray[i] >= v[i] 77 | } 78 | } 79 | return true 80 | } 81 | 82 | var _ = Suite(&S{}) 83 | 84 | func (s *S) SetUpSuite(c *C) { 85 | mgo.SetDebug(true) 86 | mgo.SetStats(true) 87 | s.StartAll() 88 | 89 | session, err := mgo.Dial("localhost:40001") 90 | c.Assert(err, IsNil) 91 | s.build, err = session.BuildInfo() 92 | c.Check(err, IsNil) 93 | session.Close() 94 | } 95 | 96 | func (s *S) SetUpTest(c *C) { 97 | err := run("mongo --nodb harness/mongojs/dropall.js") 98 | if err != nil { 99 | panic(err.Error()) 100 | } 101 | mgo.SetLogger((*cLogger)(c)) 102 | mgo.ResetStats() 103 | } 104 | 105 | func (s *S) TearDownTest(c *C) { 106 | if s.stopped { 107 | s.Stop(":40201") 108 | s.Stop(":40202") 109 | s.Stop(":40203") 110 | s.StartAll() 111 | } 112 | for _, host := range s.frozen { 113 | if host != "" { 114 | s.Thaw(host) 115 | } 116 | } 117 | var stats mgo.Stats 118 | for i := 0; ; i++ { 119 | stats = mgo.GetStats() 120 | if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { 121 | break 122 | } 123 | if i == 20 { 124 | c.Fatal("Test left sockets in a dirty state") 125 | } 126 | c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive) 127 | time.Sleep(500 * time.Millisecond) 128 | } 129 | for i := 0; ; i++ { 130 | stats = mgo.GetStats() 131 | if stats.Clusters == 0 { 132 | break 133 | } 134 | if i == 60 { 135 | c.Fatal("Test left clusters alive") 136 | } 137 | c.Logf("Waiting for clusters to die: %d alive", stats.Clusters) 138 | time.Sleep(1 * time.Second) 139 | } 140 | } 141 | 142 | func (s *S) Stop(host string) { 143 | // Give a moment for slaves to sync and avoid getting rollback issues. 144 | panicOnWindows() 145 | time.Sleep(2 * time.Second) 146 | err := run("svc -d _harness/daemons/" + supvName(host)) 147 | if err != nil { 148 | panic(err) 149 | } 150 | s.stopped = true 151 | } 152 | 153 | func (s *S) pid(host string) int { 154 | // Note recent releases of lsof force 'f' to be present in the output (WTF?). 155 | cmd := exec.Command("lsof", "-iTCP:"+hostPort(host), "-sTCP:LISTEN", "-Fpf") 156 | output, err := cmd.CombinedOutput() 157 | if err != nil { 158 | panic(err) 159 | } 160 | pidstr := string(bytes.Fields(output[1:])[0]) 161 | pid, err := strconv.Atoi(pidstr) 162 | if err != nil { 163 | panic(fmt.Errorf("cannot convert pid to int: %q, command line: %q", pidstr, cmd.Args)) 164 | } 165 | return pid 166 | } 167 | 168 | func (s *S) Freeze(host string) { 169 | err := stop(s.pid(host)) 170 | if err != nil { 171 | panic(err) 172 | } 173 | s.frozen = append(s.frozen, host) 174 | } 175 | 176 | func (s *S) Thaw(host string) { 177 | err := cont(s.pid(host)) 178 | if err != nil { 179 | panic(err) 180 | } 181 | for i, frozen := range s.frozen { 182 | if frozen == host { 183 | s.frozen[i] = "" 184 | } 185 | } 186 | } 187 | 188 | func (s *S) StartAll() { 189 | if s.stopped { 190 | // Restart any stopped nodes. 191 | run("svc -u _harness/daemons/*") 192 | err := run("mongo --nodb harness/mongojs/wait.js") 193 | if err != nil { 194 | panic(err) 195 | } 196 | s.stopped = false 197 | } 198 | } 199 | 200 | func run(command string) error { 201 | var output []byte 202 | var err error 203 | if runtime.GOOS == "windows" { 204 | output, err = exec.Command("cmd", "/C", command).CombinedOutput() 205 | } else { 206 | output, err = exec.Command("/bin/sh", "-c", command).CombinedOutput() 207 | } 208 | 209 | if err != nil { 210 | msg := fmt.Sprintf("Failed to execute: %s: %s\n%s", command, err.Error(), string(output)) 211 | return errors.New(msg) 212 | } 213 | return nil 214 | } 215 | 216 | var supvNames = map[string]string{ 217 | "40001": "db1", 218 | "40002": "db2", 219 | "40011": "rs1a", 220 | "40012": "rs1b", 221 | "40013": "rs1c", 222 | "40021": "rs2a", 223 | "40022": "rs2b", 224 | "40023": "rs2c", 225 | "40031": "rs3a", 226 | "40032": "rs3b", 227 | "40033": "rs3c", 228 | "40041": "rs4a", 229 | "40101": "cfg1", 230 | "40102": "cfg2", 231 | "40103": "cfg3", 232 | "40201": "s1", 233 | "40202": "s2", 234 | "40203": "s3", 235 | } 236 | 237 | // supvName returns the daemon name for the given host address. 238 | func supvName(host string) string { 239 | host, port, err := net.SplitHostPort(host) 240 | if err != nil { 241 | panic(err) 242 | } 243 | name, ok := supvNames[port] 244 | if !ok { 245 | panic("Unknown host: " + host) 246 | } 247 | return name 248 | } 249 | 250 | func hostPort(host string) string { 251 | _, port, err := net.SplitHostPort(host) 252 | if err != nil { 253 | panic(err) 254 | } 255 | return port 256 | } 257 | 258 | func panicOnWindows() { 259 | if runtime.GOOS == "windows" { 260 | panic("the test suite is not yet fully supported on Windows") 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /syscall_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package mgo_test 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | func stop(pid int) (err error) { 10 | return syscall.Kill(pid, syscall.SIGSTOP) 11 | } 12 | 13 | func cont(pid int) (err error) { 14 | return syscall.Kill(pid, syscall.SIGCONT) 15 | } 16 | -------------------------------------------------------------------------------- /syscall_windows_test.go: -------------------------------------------------------------------------------- 1 | package mgo_test 2 | 3 | func stop(pid int) (err error) { 4 | panicOnWindows() // Always does. 5 | return nil 6 | } 7 | 8 | func cont(pid int) (err error) { 9 | panicOnWindows() // Always does. 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /txn/chaos.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | mrand "math/rand" 5 | "time" 6 | ) 7 | 8 | var chaosEnabled = false 9 | var chaosSetting Chaos 10 | 11 | // Chaos holds parameters for the failure injection mechanism. 12 | type Chaos struct { 13 | // KillChance is the 0.0 to 1.0 chance that a given checkpoint 14 | // within the algorithm will raise an interruption that will 15 | // stop the procedure. 16 | KillChance float64 17 | 18 | // SlowdownChance is the 0.0 to 1.0 chance that a given checkpoint 19 | // within the algorithm will be delayed by Slowdown before 20 | // continuing. 21 | SlowdownChance float64 22 | Slowdown time.Duration 23 | 24 | // If Breakpoint is set, the above settings will only affect the 25 | // named breakpoint. 26 | Breakpoint string 27 | } 28 | 29 | // SetChaos sets the failure injection parameters to c. 30 | func SetChaos(c Chaos) { 31 | chaosSetting = c 32 | chaosEnabled = c.KillChance > 0 || c.SlowdownChance > 0 33 | } 34 | 35 | func chaos(bpname string) { 36 | if !chaosEnabled { 37 | return 38 | } 39 | switch chaosSetting.Breakpoint { 40 | case "", bpname: 41 | kc := chaosSetting.KillChance 42 | if kc > 0 && mrand.Intn(1000) < int(kc*1000) { 43 | panic(chaosError{}) 44 | } 45 | if bpname == "insert" { 46 | return 47 | } 48 | sc := chaosSetting.SlowdownChance 49 | if sc > 0 && mrand.Intn(1000) < int(sc*1000) { 50 | time.Sleep(chaosSetting.Slowdown) 51 | } 52 | } 53 | } 54 | 55 | type chaosError struct{} 56 | 57 | func (f *flusher) handleChaos(err *error) { 58 | v := recover() 59 | if v == nil { 60 | return 61 | } 62 | if _, ok := v.(chaosError); ok { 63 | f.debugf("Killed by chaos!") 64 | *err = ErrChaos 65 | return 66 | } 67 | panic(v) 68 | } 69 | -------------------------------------------------------------------------------- /txn/debug.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sort" 7 | "sync/atomic" 8 | 9 | "github.com/globalsign/mgo/bson" 10 | ) 11 | 12 | var ( 13 | debugEnabled bool 14 | logger logLogger 15 | ) 16 | 17 | type logLogger interface { 18 | Output(calldepth int, s string) error 19 | } 20 | 21 | // SetLogger specify the *log.Logger where logged messages should be sent to. 22 | func SetLogger(l logLogger) { 23 | logger = l 24 | } 25 | 26 | // SetDebug enables or disables debugging. 27 | func SetDebug(debug bool) { 28 | debugEnabled = debug 29 | } 30 | 31 | // ErrChaos error returned when operation failed due to 32 | // the failure injection mechanism. 33 | var ErrChaos = fmt.Errorf("interrupted by chaos") 34 | 35 | var debugId uint32 36 | 37 | func debugPrefix() string { 38 | d := atomic.AddUint32(&debugId, 1) - 1 39 | s := make([]byte, 0, 10) 40 | for i := uint(0); i < 8; i++ { 41 | s = append(s, "abcdefghijklmnop"[(d>>(4*i))&0xf]) 42 | if d>>(4*(i+1)) == 0 { 43 | break 44 | } 45 | } 46 | s = append(s, ')', ' ') 47 | return string(s) 48 | } 49 | 50 | func logf(format string, args ...interface{}) { 51 | if logger != nil { 52 | logger.Output(2, fmt.Sprintf(format, argsForLog(args)...)) 53 | } 54 | } 55 | 56 | func debugf(format string, args ...interface{}) { 57 | if debugEnabled && logger != nil { 58 | logger.Output(2, fmt.Sprintf(format, argsForLog(args)...)) 59 | } 60 | } 61 | 62 | func argsForLog(args []interface{}) []interface{} { 63 | for i, arg := range args { 64 | switch v := arg.(type) { 65 | case bson.ObjectId: 66 | args[i] = v.Hex() 67 | case []bson.ObjectId: 68 | lst := make([]string, len(v)) 69 | for j, id := range v { 70 | lst[j] = id.Hex() 71 | } 72 | args[i] = lst 73 | case map[docKey][]bson.ObjectId: 74 | buf := &bytes.Buffer{} 75 | var dkeys docKeys 76 | for dkey := range v { 77 | dkeys = append(dkeys, dkey) 78 | } 79 | sort.Sort(dkeys) 80 | for i, dkey := range dkeys { 81 | if i > 0 { 82 | buf.WriteByte(' ') 83 | } 84 | buf.WriteString(fmt.Sprintf("%v: {", dkey)) 85 | for j, id := range v[dkey] { 86 | if j > 0 { 87 | buf.WriteByte(' ') 88 | } 89 | buf.WriteString(id.Hex()) 90 | } 91 | buf.WriteByte('}') 92 | } 93 | args[i] = buf.String() 94 | case map[docKey][]int64: 95 | buf := &bytes.Buffer{} 96 | var dkeys docKeys 97 | for dkey := range v { 98 | dkeys = append(dkeys, dkey) 99 | } 100 | sort.Sort(dkeys) 101 | for i, dkey := range dkeys { 102 | if i > 0 { 103 | buf.WriteByte(' ') 104 | } 105 | buf.WriteString(fmt.Sprintf("%v: %v", dkey, v[dkey])) 106 | } 107 | args[i] = buf.String() 108 | } 109 | } 110 | return args 111 | } 112 | -------------------------------------------------------------------------------- /txn/dockey_test.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "sort" 5 | 6 | . "gopkg.in/check.v1" 7 | ) 8 | 9 | type DocKeySuite struct{} 10 | 11 | var _ = Suite(&DocKeySuite{}) 12 | 13 | type T struct { 14 | A int 15 | B string 16 | } 17 | 18 | type T2 struct { 19 | A int 20 | B string 21 | } 22 | 23 | type T3 struct { 24 | A int 25 | B string 26 | } 27 | 28 | type T4 struct { 29 | A int 30 | B string 31 | } 32 | 33 | type T5 struct { 34 | F int 35 | Q string 36 | } 37 | 38 | type T6 struct { 39 | A int 40 | B string 41 | } 42 | 43 | type T7 struct { 44 | A bool 45 | B float64 46 | } 47 | 48 | type T8 struct { 49 | A int 50 | B string 51 | } 52 | 53 | type T9 struct { 54 | A int 55 | B string 56 | C bool 57 | } 58 | 59 | type T10 struct { 60 | C int `bson:"a"` 61 | D string `bson:"b,omitempty"` 62 | } 63 | 64 | type T11 struct { 65 | C int 66 | D string 67 | } 68 | 69 | type T12 struct { 70 | S string 71 | } 72 | 73 | type T13 struct { 74 | p, q, r bool 75 | S string 76 | } 77 | 78 | var docKeysTests = [][]docKeys{ 79 | {{ 80 | {"c", 1}, 81 | {"c", 5}, 82 | {"c", 2}, 83 | }, { 84 | {"c", 1}, 85 | {"c", 2}, 86 | {"c", 5}, 87 | }}, {{ 88 | {"c", "foo"}, 89 | {"c", "bar"}, 90 | {"c", "bob"}, 91 | }, { 92 | {"c", "bar"}, 93 | {"c", "bob"}, 94 | {"c", "foo"}, 95 | }}, {{ 96 | {"c", 0.2}, 97 | {"c", 0.07}, 98 | {"c", 0.9}, 99 | }, { 100 | {"c", 0.07}, 101 | {"c", 0.2}, 102 | {"c", 0.9}, 103 | }}, {{ 104 | {"c", true}, 105 | {"c", false}, 106 | {"c", true}, 107 | }, { 108 | {"c", false}, 109 | {"c", true}, 110 | {"c", true}, 111 | }}, {{ 112 | {"c", T{1, "b"}}, 113 | {"c", T{1, "a"}}, 114 | {"c", T{0, "b"}}, 115 | {"c", T{0, "a"}}, 116 | }, { 117 | {"c", T{0, "a"}}, 118 | {"c", T{0, "b"}}, 119 | {"c", T{1, "a"}}, 120 | {"c", T{1, "b"}}, 121 | }}, {{ 122 | {"c", T{1, "a"}}, 123 | {"c", T{0, "a"}}, 124 | }, { 125 | {"c", T{0, "a"}}, 126 | {"c", T{1, "a"}}, 127 | }}, {{ 128 | {"c", T3{0, "b"}}, 129 | {"c", T2{1, "b"}}, 130 | {"c", T3{1, "a"}}, 131 | {"c", T2{0, "a"}}, 132 | }, { 133 | {"c", T2{0, "a"}}, 134 | {"c", T3{0, "b"}}, 135 | {"c", T3{1, "a"}}, 136 | {"c", T2{1, "b"}}, 137 | }}, {{ 138 | {"c", T5{1, "b"}}, 139 | {"c", T4{1, "b"}}, 140 | {"c", T5{0, "a"}}, 141 | {"c", T4{0, "a"}}, 142 | }, { 143 | {"c", T4{0, "a"}}, 144 | {"c", T5{0, "a"}}, 145 | {"c", T4{1, "b"}}, 146 | {"c", T5{1, "b"}}, 147 | }}, {{ 148 | {"c", T6{1, "b"}}, 149 | {"c", T7{true, 0.2}}, 150 | {"c", T6{0, "a"}}, 151 | {"c", T7{false, 0.04}}, 152 | }, { 153 | {"c", T6{0, "a"}}, 154 | {"c", T6{1, "b"}}, 155 | {"c", T7{false, 0.04}}, 156 | {"c", T7{true, 0.2}}, 157 | }}, {{ 158 | {"c", T9{1, "b", true}}, 159 | {"c", T8{1, "b"}}, 160 | {"c", T9{0, "a", false}}, 161 | {"c", T8{0, "a"}}, 162 | }, { 163 | {"c", T9{0, "a", false}}, 164 | {"c", T8{0, "a"}}, 165 | {"c", T9{1, "b", true}}, 166 | {"c", T8{1, "b"}}, 167 | }}, {{ 168 | {"b", 2}, 169 | {"a", 5}, 170 | {"c", 2}, 171 | {"b", 1}, 172 | }, { 173 | {"a", 5}, 174 | {"b", 1}, 175 | {"b", 2}, 176 | {"c", 2}, 177 | }}, {{ 178 | {"c", T11{1, "a"}}, 179 | {"c", T11{1, "a"}}, 180 | {"c", T10{1, "a"}}, 181 | }, { 182 | {"c", T10{1, "a"}}, 183 | {"c", T11{1, "a"}}, 184 | {"c", T11{1, "a"}}, 185 | }}, {{ 186 | {"c", T12{"a"}}, 187 | {"c", T13{false, true, false, "a"}}, 188 | {"c", T12{"b"}}, 189 | {"c", T13{false, true, false, "b"}}, 190 | }, { 191 | {"c", T12{"a"}}, 192 | {"c", T13{false, true, false, "a"}}, 193 | {"c", T12{"b"}}, 194 | {"c", T13{false, true, false, "b"}}, 195 | }}, 196 | } 197 | 198 | func (s *DocKeySuite) TestSort(c *C) { 199 | for _, test := range docKeysTests { 200 | keys := test[0] 201 | expected := test[1] 202 | sort.Sort(keys) 203 | c.Check(keys, DeepEquals, expected) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /txn/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalsign/mgo/eeefdecb41b842af6dc652aaea4026e8403e62df/txn/output.txt -------------------------------------------------------------------------------- /txn/tarjan.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/globalsign/mgo/bson" 7 | ) 8 | 9 | func tarjanSort(successors map[bson.ObjectId][]bson.ObjectId) [][]bson.ObjectId { 10 | // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm 11 | data := &tarjanData{ 12 | successors: successors, 13 | nodes: make([]tarjanNode, 0, len(successors)), 14 | index: make(map[bson.ObjectId]int, len(successors)), 15 | } 16 | 17 | for id := range successors { 18 | id := bson.ObjectId(string(id)) 19 | if _, seen := data.index[id]; !seen { 20 | data.strongConnect(id) 21 | } 22 | } 23 | 24 | // Sort connected components to stabilize the algorithm. 25 | for _, ids := range data.output { 26 | if len(ids) > 1 { 27 | sort.Sort(idList(ids)) 28 | } 29 | } 30 | return data.output 31 | } 32 | 33 | type tarjanData struct { 34 | successors map[bson.ObjectId][]bson.ObjectId 35 | output [][]bson.ObjectId 36 | 37 | nodes []tarjanNode 38 | stack []bson.ObjectId 39 | index map[bson.ObjectId]int 40 | } 41 | 42 | type tarjanNode struct { 43 | lowlink int 44 | stacked bool 45 | } 46 | 47 | type idList []bson.ObjectId 48 | 49 | func (l idList) Len() int { return len(l) } 50 | func (l idList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 51 | func (l idList) Less(i, j int) bool { return l[i] < l[j] } 52 | 53 | func (data *tarjanData) strongConnect(id bson.ObjectId) *tarjanNode { 54 | index := len(data.nodes) 55 | data.index[id] = index 56 | data.stack = append(data.stack, id) 57 | data.nodes = append(data.nodes, tarjanNode{index, true}) 58 | node := &data.nodes[index] 59 | 60 | for _, succid := range data.successors[id] { 61 | succindex, seen := data.index[succid] 62 | if !seen { 63 | succnode := data.strongConnect(succid) 64 | if succnode.lowlink < node.lowlink { 65 | node.lowlink = succnode.lowlink 66 | } 67 | } else if data.nodes[succindex].stacked { 68 | // Part of the current strongly-connected component. 69 | if succindex < node.lowlink { 70 | node.lowlink = succindex 71 | } 72 | } 73 | } 74 | 75 | if node.lowlink == index { 76 | // Root node; pop stack and output new 77 | // strongly-connected component. 78 | var scc []bson.ObjectId 79 | i := len(data.stack) - 1 80 | for { 81 | stackid := data.stack[i] 82 | stackindex := data.index[stackid] 83 | data.nodes[stackindex].stacked = false 84 | scc = append(scc, stackid) 85 | if stackindex == index { 86 | break 87 | } 88 | i-- 89 | } 90 | data.stack = data.stack[:i] 91 | data.output = append(data.output, scc) 92 | } 93 | 94 | return node 95 | } 96 | -------------------------------------------------------------------------------- /txn/tarjan_test.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/globalsign/mgo/bson" 7 | . "gopkg.in/check.v1" 8 | ) 9 | 10 | type TarjanSuite struct{} 11 | 12 | var _ = Suite(TarjanSuite{}) 13 | 14 | func bid(n int) bson.ObjectId { 15 | return bson.ObjectId(fmt.Sprintf("%024d", n)) 16 | } 17 | 18 | func bids(ns ...int) (ids []bson.ObjectId) { 19 | for _, n := range ns { 20 | ids = append(ids, bid(n)) 21 | } 22 | return 23 | } 24 | 25 | func (TarjanSuite) TestExample(c *C) { 26 | successors := map[bson.ObjectId][]bson.ObjectId{ 27 | bid(1): bids(2, 3), 28 | bid(2): bids(1, 5), 29 | bid(3): bids(4), 30 | bid(4): bids(3, 5), 31 | bid(5): bids(6), 32 | bid(6): bids(7), 33 | bid(7): bids(8), 34 | bid(8): bids(6, 9), 35 | bid(9): bids(), 36 | } 37 | 38 | c.Assert(tarjanSort(successors), DeepEquals, [][]bson.ObjectId{ 39 | bids(9), 40 | bids(6, 7, 8), 41 | bids(5), 42 | bids(3, 4), 43 | bids(1, 2), 44 | }) 45 | } 46 | --------------------------------------------------------------------------------