diff --git a/bindata/bootkube/config/bootstrap-config-overrides.yaml b/bindata/bootkube/config/bootstrap-config-overrides.yaml index 170f06687..9d8c2b184 100644 --- a/bindata/bootkube/config/bootstrap-config-overrides.yaml +++ b/bindata/bootkube/config/bootstrap-config-overrides.yaml @@ -11,3 +11,11 @@ extendedArguments: - "/etc/kubernetes/secrets/kube-ca.key" port: - "0" + {{if .ClusterCIDR }} + cluster-cidr: {{range .ClusterCIDR}} + - {{.}}{{end}} + {{end}} + {{if .ServiceClusterIPRange }} + service-cluster-ip-range: {{range .ServiceClusterIPRange}} + - {{.}}{{end}} + {{end}} diff --git a/cmd/cluster-kube-controller-manager-operator/render/render.go b/cmd/cluster-kube-controller-manager-operator/render/render.go index 00bb7652e..6ca5554b1 100644 --- a/cmd/cluster-kube-controller-manager-operator/render/render.go +++ b/cmd/cluster-kube-controller-manager-operator/render/render.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "path/filepath" + "github.com/ghodss/yaml" "github.com/golang/glog" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -14,6 +15,8 @@ import ( "github.com/openshift/cluster-kube-controller-manager-operator/pkg/operator/v311_00_assets" genericrender "github.com/openshift/library-go/pkg/operator/render" genericrenderoptions "github.com/openshift/library-go/pkg/operator/render/options" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" ) const ( @@ -25,8 +28,9 @@ type renderOpts struct { manifest genericrenderoptions.ManifestOptions generic genericrenderoptions.GenericOptions - disablePhase2 bool - errOut io.Writer + clusterConfigFile string + disablePhase2 bool + errOut io.Writer } // NewRenderCommand creates a render command. @@ -64,6 +68,8 @@ func (r *renderOpts) AddFlags(fs *pflag.FlagSet) { r.manifest.AddFlags(fs, "controller manager") r.generic.AddFlags(fs, kubecontrolplanev1.GroupVersion.WithKind("KubeControllerManagerConfig")) + fs.StringVar(&r.clusterConfigFile, "cluster-config-file", r.clusterConfigFile, "Openshift Cluster API Config file.") + // TODO: remove when the installer has stopped using it fs.BoolVar(&r.disablePhase2, "disable-phase-2", r.disablePhase2, "Disable rendering of the phase 2 daemonset and dependencies.") fs.MarkHidden("disable-phase-2") @@ -78,6 +84,7 @@ func (r *renderOpts) Validate() error { if err := r.generic.Validate(); err != nil { return err } + return nil } @@ -92,9 +99,58 @@ func (r *renderOpts) Complete() error { return nil } +type TemplateData struct { + genericrenderoptions.TemplateData + + ClusterCIDR []string + ServiceClusterIPRange []string +} + +func discoverRestrictedCIDRs(clusterConfigFileData []byte, renderConfig *TemplateData) error { + configJson, err := yaml.YAMLToJSON(clusterConfigFileData) + if err != nil { + return err + } + + clusterConfigObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, configJson) + if err != nil { + return err + } + clusterConfig, ok := clusterConfigObj.(*unstructured.Unstructured) + if !ok { + return fmt.Errorf("unexpected object in %t", clusterConfigObj) + } + + if clusterCIDR, found, err := unstructured.NestedStringSlice( + clusterConfig.Object, "spec", "clusterNetwork", "pods", "cidrBlocks"); found && err == nil { + renderConfig.ClusterCIDR = clusterCIDR + } + if err != nil { + return err + } + if serviceClusterIPRange, found, err := unstructured.NestedStringSlice( + clusterConfig.Object, "spec", "clusterNetwork", "services", "cidrBlocks"); found && err == nil { + renderConfig.ServiceClusterIPRange = serviceClusterIPRange + } + if err != nil { + return err + } + return nil +} + // Run contains the logic of the render command. func (r *renderOpts) Run() error { - renderConfig := genericrenderoptions.TemplateData{} + renderConfig := TemplateData{} + if len(r.clusterConfigFile) > 0 { + clusterConfigFileData, err := ioutil.ReadFile(r.clusterConfigFile) + if err != nil { + return err + } + err = discoverRestrictedCIDRs(clusterConfigFileData, &renderConfig) + if err != nil { + return fmt.Errorf("unable to parse restricted CIDRs from config: %v", err) + } + } if err := r.manifest.ApplyTo(&renderConfig.ManifestConfig); err != nil { return err } diff --git a/glide.lock b/glide.lock index d7760ef31..981a166d2 100644 --- a/glide.lock +++ b/glide.lock @@ -1,6 +1,8 @@ hash: d08d4a2860d84f9c1e6c1669e356923bcd0995ca6024c815be15a931a06c4f75 -updated: 2018-11-05T16:02:10.783905+01:00 +updated: 2018-11-07T10:50:10.170603163-05:00 imports: +- name: bitbucket.org/ww/goautoneg + version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 - name: github.com/beorn7/perks version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 subpackages: @@ -9,14 +11,117 @@ imports: version: b38d23b8782a487059e8fc8773e9a5b228a77cb6 - name: github.com/certifi/gocertifi version: ee1a9a0726d2ae45f54118cac878c990d4016ded +- name: github.com/coreos/etcd + version: 95a726a27e09030f9ccbd9982a1508f5a6d25ada + subpackages: + - alarm + - auth + - auth/authpb + - client + - clientv3 + - clientv3/concurrency + - clientv3/namespace + - clientv3/naming + - compactor + - discovery + - embed + - error + - etcdserver + - etcdserver/api + - etcdserver/api/etcdhttp + - etcdserver/api/v2http + - etcdserver/api/v2http/httptypes + - etcdserver/api/v3client + - etcdserver/api/v3election + - etcdserver/api/v3election/v3electionpb + - etcdserver/api/v3election/v3electionpb/gw + - etcdserver/api/v3lock + - etcdserver/api/v3lock/v3lockpb + - etcdserver/api/v3lock/v3lockpb/gw + - etcdserver/api/v3rpc + - etcdserver/api/v3rpc/rpctypes + - etcdserver/auth + - etcdserver/etcdserverpb + - etcdserver/etcdserverpb/gw + - etcdserver/membership + - etcdserver/stats + - integration + - lease + - lease/leasehttp + - lease/leasepb + - mvcc + - mvcc/backend + - mvcc/mvccpb + - pkg/adt + - pkg/contention + - pkg/cors + - pkg/cpuutil + - pkg/crc + - pkg/debugutil + - pkg/fileutil + - pkg/httputil + - pkg/idutil + - pkg/ioutil + - pkg/logutil + - pkg/monotime + - pkg/netutil + - pkg/pathutil + - pkg/pbutil + - pkg/runtime + - pkg/schedule + - pkg/srv + - pkg/testutil + - pkg/tlsutil + - pkg/transport + - pkg/types + - pkg/wait + - proxy/grpcproxy + - proxy/grpcproxy/adapter + - proxy/grpcproxy/cache + - raft + - raft/raftpb + - rafthttp + - snap + - snap/snappb + - store + - version + - wal + - wal/walpb +- name: github.com/coreos/go-semver + version: 568e959cd89871e61434c1143528d9162da89ef2 + subpackages: + - semver +- name: github.com/coreos/go-systemd + version: 48702e0da86bd25e76cfef347e2adeb434a0d0a6 + subpackages: + - daemon + - journal - name: github.com/davecgh/go-spew version: 782f4967f2dc4564575ca782fe2d04090b5faca8 subpackages: - spew +- name: github.com/elazarl/go-bindata-assetfs + version: 3dcc96556217539f50599357fb481ac0dc7439b9 +- name: github.com/emicklei/go-restful + version: ff4f55a206334ef123e4f79bbf348980da81ca46 + subpackages: + - log +- name: github.com/emicklei/go-restful-swagger12 + version: dcef7f55730566d41eae5db10e7d6981829720f6 +- name: github.com/evanphx/json-patch + version: 94e38aa1586e8a6c8a75770bddf5ff84c48a106b - name: github.com/getsentry/raven-go version: 32a13797442ccb601b11761d74232773c1402d14 - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee +- name: github.com/go-openapi/jsonpointer + version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 +- name: github.com/go-openapi/jsonreference + version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 +- name: github.com/go-openapi/spec + version: 1de3e0542de65ad8d75452a595886fdd0befb363 +- name: github.com/go-openapi/swag + version: f3f9494671f93fcff853e3c6e9e948b3eb71e590 - name: github.com/gogo/protobuf version: c0656edd0d9eab7c66d1eb0c568f9039345796f7 subpackages: @@ -65,6 +170,12 @@ imports: version: f2b4162afba35581b6d4a50d3b8f34e33c144682 - name: github.com/jteeuwen/go-bindata version: a0ff2567cfb70903282db057e799fd826784d41d +- name: github.com/mailru/easyjson + version: 2f5df55504ebc322e4d52d34df6a1f5b503bf26d + subpackages: + - buffer + - jlexer + - jwriter - name: github.com/matttproud/golang_protobuf_extensions version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a subpackages: @@ -73,8 +184,10 @@ imports: version: bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 - name: github.com/modern-go/reflect2 version: 05fbef0ca5da472bbf96c9322b84a53edc03c9fd +- name: github.com/NYTimes/gziphandler + version: 56545f4a5d46df9a6648819d1664c3a03a13ffdb - name: github.com/openshift/api - version: dcf20c602be3ac19340b60a4f80c9147c77c543b + version: 22c4ad19c5f2de1ffe266456e0a31e8fd1069f44 subpackages: - apps - apps/v1 @@ -119,19 +232,30 @@ imports: - webconsole - webconsole/v1 - name: github.com/openshift/client-go - version: 8641daf5752d24858c43f5f28feb2abc0afbfb2c + version: 90ddfba1bba0c0c6279412e3060aad5ac199092e - name: github.com/openshift/library-go - version: cd606a7da06f1e38812269d6a383dadf726e5d9b + version: 85241b52707ef2beb85ad9254a176448c58ee021 subpackages: - pkg/assets - pkg/config/client + - pkg/config/configdefaults - pkg/config/leaderelection + - pkg/config/serving - pkg/controller/controllercmd + - pkg/controller/metrics + - pkg/crypto - pkg/operator/render - pkg/operator/render/options - pkg/operator/resource/resourceapply - pkg/operator/resource/resourcemerge - pkg/operator/resource/resourceread + - pkg/operator/staticpod + - pkg/operator/staticpod/controller/backingresource + - pkg/operator/staticpod/controller/backingresource/bindata + - pkg/operator/staticpod/controller/common + - pkg/operator/staticpod/controller/deployment + - pkg/operator/staticpod/controller/installer + - pkg/operator/staticpod/controller/node - pkg/operator/staticpod/installerpod - pkg/operator/status - pkg/operator/v1alpha1helpers @@ -163,12 +287,20 @@ imports: version: 65c1f6f8f0fc1e2185eb9863a3bc751496404259 subpackages: - xfs +- name: github.com/PuerkitoBio/purell + version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 +- name: github.com/PuerkitoBio/urlesc + version: 5bd2802263f21d8788851d5305584c82a5c75d7e - name: github.com/sirupsen/logrus version: 89742aefa4b206dcf400792f3bd35b542998eb3b - name: github.com/spf13/cobra version: c439c4fa093711d42e1b01acb1235b52004753c1 - name: github.com/spf13/pflag version: 583c0c0531f06d5278b7d917446061adc344b5cd +- name: github.com/ugorji/go + version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74 + subpackages: + - codec - name: golang.org/x/crypto version: 49796115aa4b964c318aad4f3084fdb41e9aa067 subpackages: @@ -214,8 +346,35 @@ imports: version: f51c12702a4d776e4c1fa9b0fabab841babae631 subpackages: - rate +- name: google.golang.org/genproto + version: 09f6ed296fc66555a25fe4ce95173148778dfa85 + subpackages: + - googleapis/api/annotations + - googleapis/rpc/status +- name: google.golang.org/grpc + version: 5b3c4e850e90a4cf6a20ebd46c8b32a0a3afcb9e + subpackages: + - balancer + - codes + - connectivity + - credentials + - grpclb/grpc_lb_v1/messages + - grpclog + - health/grpc_health_v1 + - internal + - keepalive + - metadata + - naming + - peer + - resolver + - stats + - status + - tap + - transport - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 +- name: gopkg.in/natefinch/lumberjack.v2 + version: 20b71e5b60d756d3d2f80def009790325acc2b23 - name: gopkg.in/yaml.v2 version: 670d4cfef0544295bc27a114dbac37980d83185a - name: k8s.io/api @@ -264,9 +423,12 @@ imports: - pkg/api/errors - pkg/api/meta - pkg/api/resource + - pkg/api/validation + - pkg/api/validation/path - pkg/apis/meta/internalversion - pkg/apis/meta/v1 - pkg/apis/meta/v1/unstructured + - pkg/apis/meta/v1/validation - pkg/apis/meta/v1beta1 - pkg/conversion - pkg/conversion/queryparams @@ -291,6 +453,7 @@ imports: - pkg/util/json - pkg/util/mergepatch - pkg/util/net + - pkg/util/rand - pkg/util/runtime - pkg/util/sets - pkg/util/strategicpatch @@ -298,6 +461,7 @@ imports: - pkg/util/validation - pkg/util/validation/field - pkg/util/wait + - pkg/util/waitgroup - pkg/util/yaml - pkg/version - pkg/watch @@ -306,8 +470,95 @@ imports: - name: k8s.io/apiserver version: 01459b68eb5fee2dcf5ca0e29df8bcac89ead47b subpackages: + - pkg/admission + - pkg/admission/configuration + - pkg/admission/initializer + - pkg/admission/metrics + - pkg/admission/plugin/initialization + - pkg/admission/plugin/namespace/lifecycle + - pkg/admission/plugin/webhook/config + - pkg/admission/plugin/webhook/config/apis/webhookadmission + - pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1 + - pkg/admission/plugin/webhook/errors + - pkg/admission/plugin/webhook/generic + - pkg/admission/plugin/webhook/mutating + - pkg/admission/plugin/webhook/namespace + - pkg/admission/plugin/webhook/request + - pkg/admission/plugin/webhook/rules + - pkg/admission/plugin/webhook/validating + - pkg/apis/apiserver + - pkg/apis/apiserver/install + - pkg/apis/apiserver/v1alpha1 + - pkg/apis/audit + - pkg/apis/audit/install + - pkg/apis/audit/v1alpha1 + - pkg/apis/audit/v1beta1 + - pkg/apis/audit/validation + - pkg/audit + - pkg/audit/policy + - pkg/authentication/authenticator + - pkg/authentication/authenticatorfactory + - pkg/authentication/group + - pkg/authentication/request/anonymous + - pkg/authentication/request/bearertoken + - pkg/authentication/request/headerrequest + - pkg/authentication/request/union + - pkg/authentication/request/websocket + - pkg/authentication/request/x509 + - pkg/authentication/serviceaccount + - pkg/authentication/token/tokenfile + - pkg/authentication/user + - pkg/authorization/authorizer + - pkg/authorization/authorizerfactory + - pkg/authorization/union + - pkg/endpoints + - pkg/endpoints/discovery + - pkg/endpoints/filters + - pkg/endpoints/handlers + - pkg/endpoints/handlers/negotiation + - pkg/endpoints/handlers/responsewriters + - pkg/endpoints/metrics + - pkg/endpoints/openapi + - pkg/endpoints/request + - pkg/features + - pkg/registry/generic + - pkg/registry/generic/registry + - pkg/registry/rest + - pkg/server + - pkg/server/filters + - pkg/server/healthz + - pkg/server/httplog + - pkg/server/mux + - pkg/server/options + - pkg/server/resourceconfig + - pkg/server/routes + - pkg/server/routes/data/swagger + - pkg/server/storage + - pkg/storage + - pkg/storage/errors + - pkg/storage/etcd + - pkg/storage/etcd/metrics + - pkg/storage/etcd/util + - pkg/storage/etcd3 + - pkg/storage/etcd3/preflight + - pkg/storage/names + - pkg/storage/storagebackend + - pkg/storage/storagebackend/factory + - pkg/storage/value + - pkg/util/feature - pkg/util/flag + - pkg/util/flushwriter - pkg/util/logs + - pkg/util/openapi + - pkg/util/trace + - pkg/util/webhook + - pkg/util/wsstream + - plugin/pkg/audit/buffered + - plugin/pkg/audit/log + - plugin/pkg/audit/truncate + - plugin/pkg/audit/webhook + - plugin/pkg/authenticator/token/webhook + - plugin/pkg/authorizer/webhook - name: k8s.io/client-go version: 7d04d0e2a0a1a4d4a1cd6baa432a2301492e4e65 subpackages: @@ -356,36 +607,66 @@ imports: - informers/storage/v1alpha1 - informers/storage/v1beta1 - kubernetes + - kubernetes/fake - kubernetes/scheme - kubernetes/typed/admissionregistration/v1alpha1 + - kubernetes/typed/admissionregistration/v1alpha1/fake - kubernetes/typed/admissionregistration/v1beta1 + - kubernetes/typed/admissionregistration/v1beta1/fake - kubernetes/typed/apps/v1 + - kubernetes/typed/apps/v1/fake - kubernetes/typed/apps/v1beta1 + - kubernetes/typed/apps/v1beta1/fake - kubernetes/typed/apps/v1beta2 + - kubernetes/typed/apps/v1beta2/fake - kubernetes/typed/authentication/v1 + - kubernetes/typed/authentication/v1/fake - kubernetes/typed/authentication/v1beta1 + - kubernetes/typed/authentication/v1beta1/fake - kubernetes/typed/authorization/v1 + - kubernetes/typed/authorization/v1/fake - kubernetes/typed/authorization/v1beta1 + - kubernetes/typed/authorization/v1beta1/fake - kubernetes/typed/autoscaling/v1 + - kubernetes/typed/autoscaling/v1/fake - kubernetes/typed/autoscaling/v2beta1 + - kubernetes/typed/autoscaling/v2beta1/fake - kubernetes/typed/batch/v1 + - kubernetes/typed/batch/v1/fake - kubernetes/typed/batch/v1beta1 + - kubernetes/typed/batch/v1beta1/fake - kubernetes/typed/batch/v2alpha1 + - kubernetes/typed/batch/v2alpha1/fake - kubernetes/typed/certificates/v1beta1 + - kubernetes/typed/certificates/v1beta1/fake - kubernetes/typed/core/v1 + - kubernetes/typed/core/v1/fake - kubernetes/typed/events/v1beta1 + - kubernetes/typed/events/v1beta1/fake - kubernetes/typed/extensions/v1beta1 + - kubernetes/typed/extensions/v1beta1/fake - kubernetes/typed/networking/v1 + - kubernetes/typed/networking/v1/fake - kubernetes/typed/policy/v1beta1 + - kubernetes/typed/policy/v1beta1/fake - kubernetes/typed/rbac/v1 + - kubernetes/typed/rbac/v1/fake - kubernetes/typed/rbac/v1alpha1 + - kubernetes/typed/rbac/v1alpha1/fake - kubernetes/typed/rbac/v1beta1 + - kubernetes/typed/rbac/v1beta1/fake - kubernetes/typed/scheduling/v1alpha1 + - kubernetes/typed/scheduling/v1alpha1/fake - kubernetes/typed/scheduling/v1beta1 + - kubernetes/typed/scheduling/v1beta1/fake - kubernetes/typed/settings/v1alpha1 + - kubernetes/typed/settings/v1alpha1/fake - kubernetes/typed/storage/v1 + - kubernetes/typed/storage/v1/fake - kubernetes/typed/storage/v1alpha1 + - kubernetes/typed/storage/v1alpha1/fake - kubernetes/typed/storage/v1beta1 + - kubernetes/typed/storage/v1beta1/fake - listers/admissionregistration/v1alpha1 - listers/admissionregistration/v1beta1 - listers/apps/v1 diff --git a/vendor/bitbucket.org/ww/goautoneg/.hg_archival.txt b/vendor/bitbucket.org/ww/goautoneg/.hg_archival.txt new file mode 100644 index 000000000..b9a2ff984 --- /dev/null +++ b/vendor/bitbucket.org/ww/goautoneg/.hg_archival.txt @@ -0,0 +1,6 @@ +repo: 848b351341922ce39becda978778724d5b58dbca +node: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 +branch: default +latesttag: null +latesttagdistance: 5 +changessincelatesttag: 5 diff --git a/vendor/bitbucket.org/ww/goautoneg/Makefile b/vendor/bitbucket.org/ww/goautoneg/Makefile new file mode 100644 index 000000000..e33ee1730 --- /dev/null +++ b/vendor/bitbucket.org/ww/goautoneg/Makefile @@ -0,0 +1,13 @@ +include $(GOROOT)/src/Make.inc + +TARG=bitbucket.org/ww/goautoneg +GOFILES=autoneg.go + +include $(GOROOT)/src/Make.pkg + +format: + gofmt -w *.go + +docs: + gomake clean + godoc ${TARG} > README.txt diff --git a/vendor/bitbucket.org/ww/goautoneg/README.txt b/vendor/bitbucket.org/ww/goautoneg/README.txt new file mode 100644 index 000000000..7723656d5 --- /dev/null +++ b/vendor/bitbucket.org/ww/goautoneg/README.txt @@ -0,0 +1,67 @@ +PACKAGE + +package goautoneg +import "bitbucket.org/ww/goautoneg" + +HTTP Content-Type Autonegotiation. + +The functions in this package implement the behaviour specified in +http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + +Copyright (c) 2011, Open Knowledge Foundation Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + Neither the name of the Open Knowledge Foundation Ltd. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +FUNCTIONS + +func Negotiate(header string, alternatives []string) (content_type string) +Negotiate the most appropriate content_type given the accept header +and a list of alternatives. + +func ParseAccept(header string) (accept []Accept) +Parse an Accept Header string returning a sorted list +of clauses + + +TYPES + +type Accept struct { + Type, SubType string + Q float32 + Params map[string]string +} +Structure to represent a clause in an HTTP Accept Header + + +SUBDIRECTORIES + + .hg diff --git a/vendor/bitbucket.org/ww/goautoneg/autoneg.go b/vendor/bitbucket.org/ww/goautoneg/autoneg.go new file mode 100644 index 000000000..648b38cb6 --- /dev/null +++ b/vendor/bitbucket.org/ww/goautoneg/autoneg.go @@ -0,0 +1,162 @@ +/* +HTTP Content-Type Autonegotiation. + +The functions in this package implement the behaviour specified in +http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + +Copyright (c) 2011, Open Knowledge Foundation Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + Neither the name of the Open Knowledge Foundation Ltd. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +*/ +package goautoneg + +import ( + "sort" + "strconv" + "strings" +) + +// Structure to represent a clause in an HTTP Accept Header +type Accept struct { + Type, SubType string + Q float64 + Params map[string]string +} + +// For internal use, so that we can use the sort interface +type accept_slice []Accept + +func (accept accept_slice) Len() int { + slice := []Accept(accept) + return len(slice) +} + +func (accept accept_slice) Less(i, j int) bool { + slice := []Accept(accept) + ai, aj := slice[i], slice[j] + if ai.Q > aj.Q { + return true + } + if ai.Type != "*" && aj.Type == "*" { + return true + } + if ai.SubType != "*" && aj.SubType == "*" { + return true + } + return false +} + +func (accept accept_slice) Swap(i, j int) { + slice := []Accept(accept) + slice[i], slice[j] = slice[j], slice[i] +} + +// Parse an Accept Header string returning a sorted list +// of clauses +func ParseAccept(header string) (accept []Accept) { + parts := strings.Split(header, ",") + accept = make([]Accept, 0, len(parts)) + for _, part := range parts { + part := strings.Trim(part, " ") + + a := Accept{} + a.Params = make(map[string]string) + a.Q = 1.0 + + mrp := strings.Split(part, ";") + + media_range := mrp[0] + sp := strings.Split(media_range, "/") + a.Type = strings.Trim(sp[0], " ") + + switch { + case len(sp) == 1 && a.Type == "*": + a.SubType = "*" + case len(sp) == 2: + a.SubType = strings.Trim(sp[1], " ") + default: + continue + } + + if len(mrp) == 1 { + accept = append(accept, a) + continue + } + + for _, param := range mrp[1:] { + sp := strings.SplitN(param, "=", 2) + if len(sp) != 2 { + continue + } + token := strings.Trim(sp[0], " ") + if token == "q" { + a.Q, _ = strconv.ParseFloat(sp[1], 32) + } else { + a.Params[token] = strings.Trim(sp[1], " ") + } + } + + accept = append(accept, a) + } + + slice := accept_slice(accept) + sort.Sort(slice) + + return +} + +// Negotiate the most appropriate content_type given the accept header +// and a list of alternatives. +func Negotiate(header string, alternatives []string) (content_type string) { + asp := make([][]string, 0, len(alternatives)) + for _, ctype := range alternatives { + asp = append(asp, strings.SplitN(ctype, "/", 2)) + } + for _, clause := range ParseAccept(header) { + for i, ctsp := range asp { + if clause.Type == ctsp[0] && clause.SubType == ctsp[1] { + content_type = alternatives[i] + return + } + if clause.Type == ctsp[0] && clause.SubType == "*" { + content_type = alternatives[i] + return + } + if clause.Type == "*" && clause.SubType == "*" { + content_type = alternatives[i] + return + } + } + } + return +} diff --git a/vendor/bitbucket.org/ww/goautoneg/autoneg_test.go b/vendor/bitbucket.org/ww/goautoneg/autoneg_test.go new file mode 100644 index 000000000..41d328f1d --- /dev/null +++ b/vendor/bitbucket.org/ww/goautoneg/autoneg_test.go @@ -0,0 +1,33 @@ +package goautoneg + +import ( + "testing" +) + +var chrome = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + +func TestParseAccept(t *testing.T) { + alternatives := []string{"text/html", "image/png"} + content_type := Negotiate(chrome, alternatives) + if content_type != "image/png" { + t.Errorf("got %s expected image/png", content_type) + } + + alternatives = []string{"text/html", "text/plain", "text/n3"} + content_type = Negotiate(chrome, alternatives) + if content_type != "text/html" { + t.Errorf("got %s expected text/html", content_type) + } + + alternatives = []string{"text/n3", "text/plain"} + content_type = Negotiate(chrome, alternatives) + if content_type != "text/plain" { + t.Errorf("got %s expected text/plain", content_type) + } + + alternatives = []string{"text/n3", "application/rdf+xml"} + content_type = Negotiate(chrome, alternatives) + if content_type != "text/n3" { + t.Errorf("got %s expected text/n3", content_type) + } +} diff --git a/vendor/github.com/NYTimes/gziphandler/.gitignore b/vendor/github.com/NYTimes/gziphandler/.gitignore new file mode 100644 index 000000000..1377554eb --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/vendor/github.com/NYTimes/gziphandler/.travis.yml b/vendor/github.com/NYTimes/gziphandler/.travis.yml new file mode 100644 index 000000000..d2b67f69c --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/.travis.yml @@ -0,0 +1,6 @@ +language: go + +go: + - 1.7 + - 1.8 + - tip diff --git a/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..cdbca194c --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +--- +layout: code-of-conduct +version: v1.0 +--- + +This code of conduct outlines our expectations for participants within the **NYTimes/gziphandler** community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community. + +Our open source community strives to: + +* **Be friendly and patient.** +* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. +* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language. +* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. +* **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. +* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. + +## Definitions + +Harassment includes, but is not limited to: + +- Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation +- Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment +- Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle +- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop +- Threats of violence, both physical and psychological +- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm +- Deliberate intimidation +- Stalking or following +- Harassing photography or recording, including logging online activity for harassment purposes +- Sustained disruption of discussion +- Unwelcome sexual attention, including gratuitous or off-topic sexual images or behaviour +- Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others +- Continued one-on-one communication after requests to cease +- Deliberate “outing” of any aspect of a person’s identity without their consent except as necessary to protect others from intentional abuse +- Publication of non-harassing private communication + +Our open source community prioritizes marginalized people’s safety over privileged people’s comfort. We will not act on complaints regarding: + +- ‘Reverse’ -isms, including ‘reverse racism,’ ‘reverse sexism,’ and ‘cisphobia’ +- Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “I’m not discussing this with you” +- Refusal to explain or debate social justice concepts +- Communicating in a ‘tone’ you don’t find congenial +- Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions + + +### Diversity Statement + +We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong. + +Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected +characteristics above, including participants with disabilities. + +### Reporting Issues + +If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via **code@nytimes.com**. All reports will be handled with discretion. In your report please include: + +- Your contact information. +- Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please +include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link. +- Any additional information that may be helpful. + +After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse. + +### Attribution & Acknowledgements + +We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration: + +* [Django](https://www.djangoproject.com/conduct/reporting/) +* [Python](https://www.python.org/community/diversity/) +* [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct) +* [Contributor Covenant](http://contributor-covenant.org/) +* [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/) +* [Citizen Code of Conduct](http://citizencodeofconduct.org/) + +This Code of Conduct was based on https://github.com/todogroup/opencodeofconduct diff --git a/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md new file mode 100644 index 000000000..b89a9eb4f --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Contributing to NYTimes/gziphandler + +This is an open source project started by handful of developers at The New York Times and open to the entire Go community. + +We really appreciate your help! + +## Filing issues + +When filing an issue, make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +## Contributing code + +Before submitting changes, please follow these guidelines: + +1. Check the open issues and pull requests for existing discussions. +2. Open an issue to discuss a new feature. +3. Write tests. +4. Make sure code follows the ['Go Code Review Comments'](https://github.com/golang/go/wiki/CodeReviewComments). +5. Make sure your changes pass `go test`. +6. Make sure the entire test suite passes locally and on Travis CI. +7. Open a Pull Request. +8. [Squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) after receiving feedback and add a [great commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +Unless otherwise noted, the gziphandler source files are distributed under the Apache 2.0-style license found in the LICENSE.md file. diff --git a/vendor/github.com/NYTimes/gziphandler/LICENSE.md b/vendor/github.com/NYTimes/gziphandler/LICENSE.md new file mode 100644 index 000000000..b7e2ecb63 --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/LICENSE.md @@ -0,0 +1,13 @@ +Copyright (c) 2015 The New York Times Company + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this library except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/NYTimes/gziphandler/README.md b/vendor/github.com/NYTimes/gziphandler/README.md new file mode 100644 index 000000000..6d7246070 --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/README.md @@ -0,0 +1,52 @@ +Gzip Handler +============ + +This is a tiny Go package which wraps HTTP handlers to transparently gzip the +response body, for clients which support it. Although it's usually simpler to +leave that to a reverse proxy (like nginx or Varnish), this package is useful +when that's undesirable. + + +## Usage + +Call `GzipHandler` with any handler (an object which implements the +`http.Handler` interface), and it'll return a new handler which gzips the +response. For example: + +```go +package main + +import ( + "io" + "net/http" + "github.com/NYTimes/gziphandler" +) + +func main() { + withoutGz := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + io.WriteString(w, "Hello, World") + }) + + withGz := gziphandler.GzipHandler(withoutGz) + + http.Handle("/", withGz) + http.ListenAndServe("0.0.0.0:8000", nil) +} +``` + + +## Documentation + +The docs can be found at [godoc.org][docs], as usual. + + +## License + +[Apache 2.0][license]. + + + + +[docs]: https://godoc.org/github.com/nytimes/gziphandler +[license]: https://github.com/nytimes/gziphandler/blob/master/LICENSE.md diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go new file mode 100644 index 000000000..ea6dba1e7 --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/gzip.go @@ -0,0 +1,332 @@ +package gziphandler + +import ( + "bufio" + "compress/gzip" + "fmt" + "io" + "net" + "net/http" + "strconv" + "strings" + "sync" +) + +const ( + vary = "Vary" + acceptEncoding = "Accept-Encoding" + contentEncoding = "Content-Encoding" + contentType = "Content-Type" + contentLength = "Content-Length" +) + +type codings map[string]float64 + +const ( + // DefaultQValue is the default qvalue to assign to an encoding if no explicit qvalue is set. + // This is actually kind of ambiguous in RFC 2616, so hopefully it's correct. + // The examples seem to indicate that it is. + DefaultQValue = 1.0 + + // DefaultMinSize defines the minimum size to reach to enable compression. + // It's 512 bytes. + DefaultMinSize = 512 +) + +// gzipWriterPools stores a sync.Pool for each compression level for reuse of +// gzip.Writers. Use poolIndex to covert a compression level to an index into +// gzipWriterPools. +var gzipWriterPools [gzip.BestCompression - gzip.BestSpeed + 2]*sync.Pool + +func init() { + for i := gzip.BestSpeed; i <= gzip.BestCompression; i++ { + addLevelPool(i) + } + addLevelPool(gzip.DefaultCompression) +} + +// poolIndex maps a compression level to its index into gzipWriterPools. It +// assumes that level is a valid gzip compression level. +func poolIndex(level int) int { + // gzip.DefaultCompression == -1, so we need to treat it special. + if level == gzip.DefaultCompression { + return gzip.BestCompression - gzip.BestSpeed + 1 + } + return level - gzip.BestSpeed +} + +func addLevelPool(level int) { + gzipWriterPools[poolIndex(level)] = &sync.Pool{ + New: func() interface{} { + // NewWriterLevel only returns error on a bad level, we are guaranteeing + // that this will be a valid level so it is okay to ignore the returned + // error. + w, _ := gzip.NewWriterLevel(nil, level) + return w + }, + } +} + +// GzipResponseWriter provides an http.ResponseWriter interface, which gzips +// bytes before writing them to the underlying response. This doesn't close the +// writers, so don't forget to do that. +// It can be configured to skip response smaller than minSize. +type GzipResponseWriter struct { + http.ResponseWriter + index int // Index for gzipWriterPools. + gw *gzip.Writer + + code int // Saves the WriteHeader value. + + minSize int // Specifed the minimum response size to gzip. If the response length is bigger than this value, it is compressed. + buf []byte // Holds the first part of the write before reaching the minSize or the end of the write. +} + +// Write appends data to the gzip writer. +func (w *GzipResponseWriter) Write(b []byte) (int, error) { + // If content type is not set. + if _, ok := w.Header()[contentType]; !ok { + // It infer it from the uncompressed body. + w.Header().Set(contentType, http.DetectContentType(b)) + } + + // GZIP responseWriter is initialized. Use the GZIP responseWriter. + if w.gw != nil { + n, err := w.gw.Write(b) + return n, err + } + + // Save the write into a buffer for later use in GZIP responseWriter (if content is long enough) or at close with regular responseWriter. + // On the first write, w.buf changes from nil to a valid slice + w.buf = append(w.buf, b...) + + // If the global writes are bigger than the minSize, compression is enable. + if len(w.buf) >= w.minSize { + err := w.startGzip() + if err != nil { + return 0, err + } + } + + return len(b), nil +} + +// startGzip initialize any GZIP specific informations. +func (w *GzipResponseWriter) startGzip() error { + + // Set the GZIP header. + w.Header().Set(contentEncoding, "gzip") + + // if the Content-Length is already set, then calls to Write on gzip + // will fail to set the Content-Length header since its already set + // See: https://github.com/golang/go/issues/14975. + w.Header().Del(contentLength) + + // Write the header to gzip response. + if w.code != 0 { + w.ResponseWriter.WriteHeader(w.code) + } + + // Initialize the GZIP response. + w.init() + + // Flush the buffer into the gzip reponse. + n, err := w.gw.Write(w.buf) + + // This should never happen (per io.Writer docs), but if the write didn't + // accept the entire buffer but returned no specific error, we have no clue + // what's going on, so abort just to be safe. + if err == nil && n < len(w.buf) { + return io.ErrShortWrite + } + + w.buf = nil + return err +} + +// WriteHeader just saves the response code until close or GZIP effective writes. +func (w *GzipResponseWriter) WriteHeader(code int) { + w.code = code +} + +// init graps a new gzip writer from the gzipWriterPool and writes the correct +// content encoding header. +func (w *GzipResponseWriter) init() { + // Bytes written during ServeHTTP are redirected to this gzip writer + // before being written to the underlying response. + gzw := gzipWriterPools[w.index].Get().(*gzip.Writer) + gzw.Reset(w.ResponseWriter) + w.gw = gzw +} + +// Close will close the gzip.Writer and will put it back in the gzipWriterPool. +func (w *GzipResponseWriter) Close() error { + if w.gw == nil { + // Gzip not trigged yet, write out regular response. + if w.code != 0 { + w.ResponseWriter.WriteHeader(w.code) + } + if w.buf != nil { + _, writeErr := w.ResponseWriter.Write(w.buf) + // Returns the error if any at write. + if writeErr != nil { + return fmt.Errorf("gziphandler: write to regular responseWriter at close gets error: %q", writeErr.Error()) + } + } + return nil + } + + err := w.gw.Close() + gzipWriterPools[w.index].Put(w.gw) + w.gw = nil + return err +} + +// Flush flushes the underlying *gzip.Writer and then the underlying +// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter +// an http.Flusher. +func (w *GzipResponseWriter) Flush() { + if w.gw != nil { + w.gw.Flush() + } + + if fw, ok := w.ResponseWriter.(http.Flusher); ok { + fw.Flush() + } +} + +// Hijack implements http.Hijacker. If the underlying ResponseWriter is a +// Hijacker, its Hijack method is returned. Otherwise an error is returned. +func (w *GzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if hj, ok := w.ResponseWriter.(http.Hijacker); ok { + return hj.Hijack() + } + return nil, nil, fmt.Errorf("http.Hijacker interface is not supported") +} + +// verify Hijacker interface implementation +var _ http.Hijacker = &GzipResponseWriter{} + +// MustNewGzipLevelHandler behaves just like NewGzipLevelHandler except that in +// an error case it panics rather than returning an error. +func MustNewGzipLevelHandler(level int) func(http.Handler) http.Handler { + wrap, err := NewGzipLevelHandler(level) + if err != nil { + panic(err) + } + return wrap +} + +// NewGzipLevelHandler returns a wrapper function (often known as middleware) +// which can be used to wrap an HTTP handler to transparently gzip the response +// body if the client supports it (via the Accept-Encoding header). Responses will +// be encoded at the given gzip compression level. An error will be returned only +// if an invalid gzip compression level is given, so if one can ensure the level +// is valid, the returned error can be safely ignored. +func NewGzipLevelHandler(level int) (func(http.Handler) http.Handler, error) { + return NewGzipLevelAndMinSize(level, DefaultMinSize) +} + +// NewGzipLevelAndMinSize behave as NewGzipLevelHandler except it let the caller +// specify the minimum size before compression. +func NewGzipLevelAndMinSize(level, minSize int) (func(http.Handler) http.Handler, error) { + if level != gzip.DefaultCompression && (level < gzip.BestSpeed || level > gzip.BestCompression) { + return nil, fmt.Errorf("invalid compression level requested: %d", level) + } + if minSize < 0 { + return nil, fmt.Errorf("minimum size must be more than zero") + } + return func(h http.Handler) http.Handler { + index := poolIndex(level) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add(vary, acceptEncoding) + + if acceptsGzip(r) { + gw := &GzipResponseWriter{ + ResponseWriter: w, + index: index, + minSize: minSize, + } + defer gw.Close() + + h.ServeHTTP(gw, r) + } else { + h.ServeHTTP(w, r) + } + }) + }, nil +} + +// GzipHandler wraps an HTTP handler, to transparently gzip the response body if +// the client supports it (via the Accept-Encoding header). This will compress at +// the default compression level. +func GzipHandler(h http.Handler) http.Handler { + wrapper, _ := NewGzipLevelHandler(gzip.DefaultCompression) + return wrapper(h) +} + +// acceptsGzip returns true if the given HTTP request indicates that it will +// accept a gzipped response. +func acceptsGzip(r *http.Request) bool { + acceptedEncodings, _ := parseEncodings(r.Header.Get(acceptEncoding)) + return acceptedEncodings["gzip"] > 0.0 +} + +// parseEncodings attempts to parse a list of codings, per RFC 2616, as might +// appear in an Accept-Encoding header. It returns a map of content-codings to +// quality values, and an error containing the errors encountered. It's probably +// safe to ignore those, because silently ignoring errors is how the internet +// works. +// +// See: http://tools.ietf.org/html/rfc2616#section-14.3. +func parseEncodings(s string) (codings, error) { + c := make(codings) + var e []string + + for _, ss := range strings.Split(s, ",") { + coding, qvalue, err := parseCoding(ss) + + if err != nil { + e = append(e, err.Error()) + } else { + c[coding] = qvalue + } + } + + // TODO (adammck): Use a proper multi-error struct, so the individual errors + // can be extracted if anyone cares. + if len(e) > 0 { + return c, fmt.Errorf("errors while parsing encodings: %s", strings.Join(e, ", ")) + } + + return c, nil +} + +// parseCoding parses a single conding (content-coding with an optional qvalue), +// as might appear in an Accept-Encoding header. It attempts to forgive minor +// formatting errors. +func parseCoding(s string) (coding string, qvalue float64, err error) { + for n, part := range strings.Split(s, ";") { + part = strings.TrimSpace(part) + qvalue = DefaultQValue + + if n == 0 { + coding = strings.ToLower(part) + } else if strings.HasPrefix(part, "q=") { + qvalue, err = strconv.ParseFloat(strings.TrimPrefix(part, "q="), 64) + + if qvalue < 0.0 { + qvalue = 0.0 + } else if qvalue > 1.0 { + qvalue = 1.0 + } + } + } + + if coding == "" { + err = fmt.Errorf("empty content-coding") + } + + return +} diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_go18.go b/vendor/github.com/NYTimes/gziphandler/gzip_go18.go new file mode 100644 index 000000000..fa9665b7e --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/gzip_go18.go @@ -0,0 +1,43 @@ +// +build go1.8 + +package gziphandler + +import "net/http" + +// Push initiates an HTTP/2 server push. +// Push returns ErrNotSupported if the client has disabled push or if push +// is not supported on the underlying connection. +func (w *GzipResponseWriter) Push(target string, opts *http.PushOptions) error { + pusher, ok := w.ResponseWriter.(http.Pusher) + if ok && pusher != nil { + return pusher.Push(target, setAcceptEncodingForPushOptions(opts)) + } + return http.ErrNotSupported +} + +// setAcceptEncodingForPushOptions sets "Accept-Encoding" : "gzip" for PushOptions without overriding existing headers. +func setAcceptEncodingForPushOptions(opts *http.PushOptions) *http.PushOptions { + + if opts == nil { + opts = &http.PushOptions{ + Header: http.Header{ + acceptEncoding: []string{"gzip"}, + }, + } + return opts + } + + if opts.Header == nil { + opts.Header = http.Header{ + acceptEncoding: []string{"gzip"}, + } + return opts + } + + if encoding := opts.Header.Get(acceptEncoding); encoding == "" { + opts.Header.Add(acceptEncoding, "gzip") + return opts + } + + return opts +} diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_go18_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_go18_test.go new file mode 100644 index 000000000..412b2918e --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/gzip_go18_test.go @@ -0,0 +1,70 @@ +// +build go1.8 + +package gziphandler + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetAcceptEncodingForPushOptionsWithoutHeaders(t *testing.T) { + var opts *http.PushOptions + opts = setAcceptEncodingForPushOptions(opts) + + assert.NotNil(t, opts) + assert.NotNil(t, opts.Header) + + for k, v := range opts.Header { + assert.Equal(t, "Accept-Encoding", k) + assert.Len(t, v, 1) + assert.Equal(t, "gzip", v[0]) + } + + opts = &http.PushOptions{} + opts = setAcceptEncodingForPushOptions(opts) + + assert.NotNil(t, opts) + assert.NotNil(t, opts.Header) + + for k, v := range opts.Header { + assert.Equal(t, "Accept-Encoding", k) + assert.Len(t, v, 1) + assert.Equal(t, "gzip", v[0]) + } +} + +func TestSetAcceptEncodingForPushOptionsWithHeaders(t *testing.T) { + opts := &http.PushOptions{ + Header: http.Header{ + "User-Agent": []string{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36"}, + }, + } + opts = setAcceptEncodingForPushOptions(opts) + + assert.NotNil(t, opts) + assert.NotNil(t, opts.Header) + + assert.Equal(t, "gzip", opts.Header.Get("Accept-Encoding")) + assert.Equal(t, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36", opts.Header.Get("User-Agent")) + + opts = &http.PushOptions{ + Header: http.Header{ + "User-Agent": []string{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36"}, + acceptEncoding: []string{"deflate"}, + }, + } + opts = setAcceptEncodingForPushOptions(opts) + + assert.NotNil(t, opts) + assert.NotNil(t, opts.Header) + + e, found := opts.Header["Accept-Encoding"] + if !found { + assert.Fail(t, "Missing Accept-Encoding header value") + } + assert.Len(t, e, 1) + assert.Equal(t, "deflate", e[0]) + assert.Equal(t, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36", opts.Header.Get("User-Agent")) +} diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_test.go new file mode 100644 index 000000000..0f6488cae --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/gzip_test.go @@ -0,0 +1,380 @@ +package gziphandler + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + smallTestBody = "aaabbcaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbccc" + testBody = "aaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbcccaaabbbccc" +) + +func TestParseEncodings(t *testing.T) { + examples := map[string]codings{ + + // Examples from RFC 2616 + "compress, gzip": {"compress": 1.0, "gzip": 1.0}, + "": {}, + "*": {"*": 1.0}, + "compress;q=0.5, gzip;q=1.0": {"compress": 0.5, "gzip": 1.0}, + "gzip;q=1.0, identity; q=0.5, *;q=0": {"gzip": 1.0, "identity": 0.5, "*": 0.0}, + + // More random stuff + "AAA;q=1": {"aaa": 1.0}, + "BBB ; q = 2": {"bbb": 1.0}, + } + + for eg, exp := range examples { + act, _ := parseEncodings(eg) + assert.Equal(t, exp, act) + } +} + +func TestGzipHandler(t *testing.T) { + // This just exists to provide something for GzipHandler to wrap. + handler := newTestHandler(testBody) + + // requests without accept-encoding are passed along as-is + + req1, _ := http.NewRequest("GET", "/whatever", nil) + resp1 := httptest.NewRecorder() + handler.ServeHTTP(resp1, req1) + res1 := resp1.Result() + + assert.Equal(t, 200, res1.StatusCode) + assert.Equal(t, "", res1.Header.Get("Content-Encoding")) + assert.Equal(t, "Accept-Encoding", res1.Header.Get("Vary")) + assert.Equal(t, testBody, resp1.Body.String()) + + // but requests with accept-encoding:gzip are compressed if possible + + req2, _ := http.NewRequest("GET", "/whatever", nil) + req2.Header.Set("Accept-Encoding", "gzip") + resp2 := httptest.NewRecorder() + handler.ServeHTTP(resp2, req2) + res2 := resp2.Result() + + assert.Equal(t, 200, res2.StatusCode) + assert.Equal(t, "gzip", res2.Header.Get("Content-Encoding")) + assert.Equal(t, "Accept-Encoding", res2.Header.Get("Vary")) + assert.Equal(t, gzipStrLevel(testBody, gzip.DefaultCompression), resp2.Body.Bytes()) + + // content-type header is correctly set based on uncompressed body + + req3, _ := http.NewRequest("GET", "/whatever", nil) + req3.Header.Set("Accept-Encoding", "gzip") + res3 := httptest.NewRecorder() + handler.ServeHTTP(res3, req3) + + assert.Equal(t, http.DetectContentType([]byte(testBody)), res3.Header().Get("Content-Type")) +} + +func TestNewGzipLevelHandler(t *testing.T) { + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + io.WriteString(w, testBody) + }) + + for lvl := gzip.BestSpeed; lvl <= gzip.BestCompression; lvl++ { + wrapper, err := NewGzipLevelHandler(lvl) + if !assert.Nil(t, err, "NewGzipLevleHandler returned error for level:", lvl) { + continue + } + + req, _ := http.NewRequest("GET", "/whatever", nil) + req.Header.Set("Accept-Encoding", "gzip") + resp := httptest.NewRecorder() + wrapper(handler).ServeHTTP(resp, req) + res := resp.Result() + + assert.Equal(t, 200, res.StatusCode) + assert.Equal(t, "gzip", res.Header.Get("Content-Encoding")) + assert.Equal(t, "Accept-Encoding", res.Header.Get("Vary")) + assert.Equal(t, gzipStrLevel(testBody, lvl), resp.Body.Bytes()) + } +} + +func TestNewGzipLevelHandlerReturnsErrorForInvalidLevels(t *testing.T) { + var err error + _, err = NewGzipLevelHandler(-42) + assert.NotNil(t, err) + + _, err = NewGzipLevelHandler(42) + assert.NotNil(t, err) +} + +func TestMustNewGzipLevelHandlerWillPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("panic was not called") + } + }() + + _ = MustNewGzipLevelHandler(-42) +} + +func TestGzipHandlerNoBody(t *testing.T) { + tests := []struct { + statusCode int + contentEncoding string + bodyLen int + }{ + // Body must be empty. + {http.StatusNoContent, "", 0}, + {http.StatusNotModified, "", 0}, + // Body is going to get gzip'd no matter what. + {http.StatusOK, "", 0}, + } + + for num, test := range tests { + handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(test.statusCode) + })) + + rec := httptest.NewRecorder() + // TODO: in Go1.7 httptest.NewRequest was introduced this should be used + // once 1.6 is not longer supported. + req := &http.Request{ + Method: "GET", + URL: &url.URL{Path: "/"}, + Proto: "HTTP/1.1", + ProtoMinor: 1, + RemoteAddr: "192.0.2.1:1234", + Header: make(http.Header), + } + req.Header.Set("Accept-Encoding", "gzip") + handler.ServeHTTP(rec, req) + + body, err := ioutil.ReadAll(rec.Body) + if err != nil { + t.Fatalf("Unexpected error reading response body: %v", err) + } + + header := rec.Header() + assert.Equal(t, test.contentEncoding, header.Get("Content-Encoding"), fmt.Sprintf("for test iteration %d", num)) + assert.Equal(t, "Accept-Encoding", header.Get("Vary"), fmt.Sprintf("for test iteration %d", num)) + assert.Equal(t, test.bodyLen, len(body), fmt.Sprintf("for test iteration %d", num)) + } +} + +func TestGzipHandlerContentLength(t *testing.T) { + b := []byte(testBody) + handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Length", strconv.Itoa(len(b))) + w.Write(b) + })) + // httptest.NewRecorder doesn't give you access to the Content-Length + // header so instead, we create a server on a random port and make + // a request to that instead + ln, err := net.Listen("tcp", "127.0.0.1:") + if err != nil { + t.Fatalf("failed creating listen socket: %v", err) + } + defer ln.Close() + srv := &http.Server{ + Handler: handler, + } + go srv.Serve(ln) + + req := &http.Request{ + Method: "GET", + URL: &url.URL{Path: "/", Scheme: "http", Host: ln.Addr().String()}, + Header: make(http.Header), + Close: true, + } + req.Header.Set("Accept-Encoding", "gzip") + res, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("Unexpected error making http request: %v", err) + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatalf("Unexpected error reading response body: %v", err) + } + + l, err := strconv.Atoi(res.Header.Get("Content-Length")) + if err != nil { + t.Fatalf("Unexpected error parsing Content-Length: %v", err) + } + assert.Len(t, body, l) + assert.Equal(t, "gzip", res.Header.Get("Content-Encoding")) + assert.NotEqual(t, b, body) +} + +func TestGzipHandlerMinSizeMustBePositive(t *testing.T) { + _, err := NewGzipLevelAndMinSize(gzip.DefaultCompression, -1) + assert.Error(t, err) +} + +func TestGzipHandlerMinSize(t *testing.T) { + responseLength := 0 + b := []byte{'x'} + + wrapper, _ := NewGzipLevelAndMinSize(gzip.DefaultCompression, 128) + handler := wrapper(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + // Write responses one byte at a time to ensure that the flush + // mechanism, if used, is working properly. + for i := 0; i < responseLength; i++ { + n, err := w.Write(b) + assert.Equal(t, 1, n) + assert.Nil(t, err) + } + }, + )) + + r, _ := http.NewRequest("GET", "/whatever", &bytes.Buffer{}) + r.Header.Add("Accept-Encoding", "gzip") + + // Short response is not compressed + responseLength = 127 + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + if w.Result().Header.Get(contentEncoding) == "gzip" { + t.Error("Expected uncompressed response, got compressed") + } + + // Long response is not compressed + responseLength = 128 + w = httptest.NewRecorder() + handler.ServeHTTP(w, r) + if w.Result().Header.Get(contentEncoding) != "gzip" { + t.Error("Expected compressed response, got uncompressed") + } +} + +func TestGzipDoubleClose(t *testing.T) { + // reset the pool for the default compression so we can make sure duplicates + // aren't added back by double close + addLevelPool(gzip.DefaultCompression) + + handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // call close here and it'll get called again interally by + // NewGzipLevelHandler's handler defer + w.Write([]byte("test")) + w.(io.Closer).Close() + })) + + r := httptest.NewRequest("GET", "/", nil) + r.Header.Set("Accept-Encoding", "gzip") + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + // the second close shouldn't have added the same writer + // so we pull out 2 writers from the pool and make sure they're different + w1 := gzipWriterPools[poolIndex(gzip.DefaultCompression)].Get() + w2 := gzipWriterPools[poolIndex(gzip.DefaultCompression)].Get() + // assert.NotEqual looks at the value and not the address, so we use regular == + assert.False(t, w1 == w2) +} + +func TestStatusCodes(t *testing.T) { + handler := GzipHandler(http.NotFoundHandler()) + r := httptest.NewRequest("GET", "/", nil) + r.Header.Set("Accept-Encoding", "gzip") + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + result := w.Result() + if result.StatusCode != 404 { + t.Errorf("StatusCode should have been 404 but was %d", result.StatusCode) + } +} + +func TestDontWriteWhenNotWrittenTo(t *testing.T) { + // When using gzip as middleware without ANY writes in the handler, + // ensure the gzip middleware doesn't touch the actual ResponseWriter + // either. + + handler0 := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + })) + + handler1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler0.ServeHTTP(w, r) + w.WriteHeader(404) // this only works if gzip didn't do a WriteHeader(200) + }) + + r := httptest.NewRequest("GET", "/", nil) + r.Header.Set("Accept-Encoding", "gzip") + w := httptest.NewRecorder() + handler1.ServeHTTP(w, r) + + result := w.Result() + if result.StatusCode != 404 { + t.Errorf("StatusCode should have been 404 but was %d", result.StatusCode) + } +} + +// -------------------------------------------------------------------- + +func BenchmarkGzipHandler_S2k(b *testing.B) { benchmark(b, false, 2048) } +func BenchmarkGzipHandler_S20k(b *testing.B) { benchmark(b, false, 20480) } +func BenchmarkGzipHandler_S100k(b *testing.B) { benchmark(b, false, 102400) } +func BenchmarkGzipHandler_P2k(b *testing.B) { benchmark(b, true, 2048) } +func BenchmarkGzipHandler_P20k(b *testing.B) { benchmark(b, true, 20480) } +func BenchmarkGzipHandler_P100k(b *testing.B) { benchmark(b, true, 102400) } + +// -------------------------------------------------------------------- + +func gzipStrLevel(s string, lvl int) []byte { + var b bytes.Buffer + w, _ := gzip.NewWriterLevel(&b, lvl) + io.WriteString(w, s) + w.Close() + return b.Bytes() +} + +func benchmark(b *testing.B, parallel bool, size int) { + bin, err := ioutil.ReadFile("testdata/benchmark.json") + if err != nil { + b.Fatal(err) + } + + req, _ := http.NewRequest("GET", "/whatever", nil) + req.Header.Set("Accept-Encoding", "gzip") + handler := newTestHandler(string(bin[:size])) + + if parallel { + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + runBenchmark(b, req, handler) + } + }) + } else { + b.ResetTimer() + for i := 0; i < b.N; i++ { + runBenchmark(b, req, handler) + } + } +} + +func runBenchmark(b *testing.B, req *http.Request, handler http.Handler) { + res := httptest.NewRecorder() + handler.ServeHTTP(res, req) + if code := res.Code; code != 200 { + b.Fatalf("Expected 200 but got %d", code) + } else if blen := res.Body.Len(); blen < 500 { + b.Fatalf("Expected complete response body, but got %d bytes", blen) + } +} + +func newTestHandler(body string) http.Handler { + return GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, body) + })) +} diff --git a/vendor/github.com/NYTimes/gziphandler/testdata/benchmark.json b/vendor/github.com/NYTimes/gziphandler/testdata/benchmark.json new file mode 100644 index 000000000..d9180d6ac --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/testdata/benchmark.json @@ -0,0 +1,5456 @@ +[ + { + "_id": "55d2fc86da7c3f96f90aa005", + "age": 20, + "name": "Luann Grant", + "gender": "female", + "company": "ZOGAK", + "email": "luanngrant@zogak.com", + "phone": "+1 (915) 479-2908" + }, + { + "_id": "55d2fc8653953bc9a0958a92", + "age": 34, + "name": "Sanders Gonzalez", + "gender": "male", + "company": "PIVITOL", + "email": "sandersgonzalez@pivitol.com", + "phone": "+1 (914) 563-2007" + }, + { + "_id": "55d2fc86a38634b0954fe3c0", + "age": 26, + "name": "Compton Terry", + "gender": "male", + "company": "KOZGENE", + "email": "comptonterry@kozgene.com", + "phone": "+1 (812) 558-2536" + }, + { + "_id": "55d2fc86edf1be88253e4e2e", + "age": 29, + "name": "Erma Armstrong", + "gender": "female", + "company": "DYNO", + "email": "ermaarmstrong@dyno.com", + "phone": "+1 (811) 556-3980" + }, + { + "_id": "55d2fc86bee1bf1f233f8170", + "age": 20, + "name": "Carson Garcia", + "gender": "male", + "company": "JUNIPOOR", + "email": "carsongarcia@junipoor.com", + "phone": "+1 (820) 410-3221" + }, + { + "_id": "55d2fc864810db4a3738dea8", + "age": 38, + "name": "Henson Townsend", + "gender": "male", + "company": "OVIUM", + "email": "hensontownsend@ovium.com", + "phone": "+1 (982) 412-3108" + }, + { + "_id": "55d2fc86d714d77af8ed3fe4", + "age": 34, + "name": "Yesenia Garner", + "gender": "female", + "company": "TERRAGO", + "email": "yeseniagarner@terrago.com", + "phone": "+1 (878) 561-2314" + }, + { + "_id": "55d2fc867651311b2e11925a", + "age": 31, + "name": "Rachelle Stanton", + "gender": "female", + "company": "UNISURE", + "email": "rachellestanton@unisure.com", + "phone": "+1 (961) 504-3072" + }, + { + "_id": "55d2fc866415f6c03d5228eb", + "age": 30, + "name": "Franklin Rasmussen", + "gender": "male", + "company": "CAPSCREEN", + "email": "franklinrasmussen@capscreen.com", + "phone": "+1 (886) 525-2217" + }, + { + "_id": "55d2fc86e2e0fa5f81fe5279", + "age": 32, + "name": "Fischer Humphrey", + "gender": "male", + "company": "ATGEN", + "email": "fischerhumphrey@atgen.com", + "phone": "+1 (855) 424-3693" + }, + { + "_id": "55d2fc862c2a416777837c76", + "age": 29, + "name": "Olsen Moran", + "gender": "male", + "company": "SULTRAX", + "email": "olsenmoran@sultrax.com", + "phone": "+1 (860) 583-3380" + }, + { + "_id": "55d2fc868c3c9e44d59ec2a2", + "age": 29, + "name": "Mattie Myers", + "gender": "female", + "company": "VALPREAL", + "email": "mattiemyers@valpreal.com", + "phone": "+1 (834) 587-2707" + }, + { + "_id": "55d2fc860a6afc8beebe477d", + "age": 39, + "name": "Cleveland Jordan", + "gender": "male", + "company": "XLEEN", + "email": "clevelandjordan@xleen.com", + "phone": "+1 (848) 449-2037" + }, + { + "_id": "55d2fc8605766af1b531b5ea", + "age": 35, + "name": "Gordon Rios", + "gender": "male", + "company": "BULLZONE", + "email": "gordonrios@bullzone.com", + "phone": "+1 (882) 436-2216" + }, + { + "_id": "55d2fc86e052bfb7cf9b5a38", + "age": 33, + "name": "Todd Burch", + "gender": "male", + "company": "MOTOVATE", + "email": "toddburch@motovate.com", + "phone": "+1 (911) 470-2129" + }, + { + "_id": "55d2fc86212924928b4112d6", + "age": 31, + "name": "Autumn Strong", + "gender": "female", + "company": "NURPLEX", + "email": "autumnstrong@nurplex.com", + "phone": "+1 (827) 483-2571" + }, + { + "_id": "55d2fc86286c9bc87359e326", + "age": 27, + "name": "Rochelle Fitzgerald", + "gender": "female", + "company": "RAMEON", + "email": "rochellefitzgerald@rameon.com", + "phone": "+1 (820) 402-3411" + }, + { + "_id": "55d2fc86511439244d21c569", + "age": 24, + "name": "Perry Hopkins", + "gender": "male", + "company": "KRAGGLE", + "email": "perryhopkins@kraggle.com", + "phone": "+1 (826) 469-3928" + }, + { + "_id": "55d2fc868b8652b54c66051c", + "age": 37, + "name": "Guzman Williamson", + "gender": "male", + "company": "OCTOCORE", + "email": "guzmanwilliamson@octocore.com", + "phone": "+1 (826) 529-3380" + }, + { + "_id": "55d2fc86f16a5a4c310483df", + "age": 28, + "name": "Sheri Thompson", + "gender": "female", + "company": "AUTOGRATE", + "email": "sherithompson@autograte.com", + "phone": "+1 (942) 463-2727" + }, + { + "_id": "55d2fc86675d3040fc35daa8", + "age": 24, + "name": "Walton Macdonald", + "gender": "male", + "company": "ZIZZLE", + "email": "waltonmacdonald@zizzle.com", + "phone": "+1 (990) 510-2656" + }, + { + "_id": "55d2fc864e90e236f9e5a174", + "age": 39, + "name": "Gwendolyn Ross", + "gender": "female", + "company": "XIIX", + "email": "gwendolynross@xiix.com", + "phone": "+1 (869) 565-2774" + }, + { + "_id": "55d2fc866f306c575a36cb97", + "age": 23, + "name": "Sexton Herring", + "gender": "male", + "company": "MEDIOT", + "email": "sextonherring@mediot.com", + "phone": "+1 (935) 510-2049" + }, + { + "_id": "55d2fc867526db78550711a3", + "age": 26, + "name": "Twila Vang", + "gender": "female", + "company": "ARCTIQ", + "email": "twilavang@arctiq.com", + "phone": "+1 (979) 429-2135" + }, + { + "_id": "55d2fc86fe5e24c1b4b0dc96", + "age": 26, + "name": "Marjorie Snider", + "gender": "female", + "company": "QUARX", + "email": "marjoriesnider@quarx.com", + "phone": "+1 (913) 469-2916" + }, + { + "_id": "55d2fc861e85e0104fa7d33e", + "age": 36, + "name": "Malone Diaz", + "gender": "male", + "company": "COMVEYOR", + "email": "malonediaz@comveyor.com", + "phone": "+1 (882) 541-3306" + }, + { + "_id": "55d2fc86bc04a4fa0a338403", + "age": 39, + "name": "Amelia Sanford", + "gender": "female", + "company": "IPLAX", + "email": "ameliasanford@iplax.com", + "phone": "+1 (872) 589-3509" + }, + { + "_id": "55d2fc86f7854f672e80c1dd", + "age": 27, + "name": "Kristie Fernandez", + "gender": "female", + "company": "RONBERT", + "email": "kristiefernandez@ronbert.com", + "phone": "+1 (983) 419-3564" + }, + { + "_id": "55d2fc867faa140f152b7229", + "age": 23, + "name": "Bray Wyatt", + "gender": "male", + "company": "DEMINIMUM", + "email": "braywyatt@deminimum.com", + "phone": "+1 (943) 485-3961" + }, + { + "_id": "55d2fc863a42779d68c0dd53", + "age": 36, + "name": "Meyer Pickett", + "gender": "male", + "company": "GENEKOM", + "email": "meyerpickett@genekom.com", + "phone": "+1 (999) 534-3038" + }, + { + "_id": "55d2fc86095f5ceb3c57efeb", + "age": 22, + "name": "Carlson Ramirez", + "gender": "male", + "company": "KANGLE", + "email": "carlsonramirez@kangle.com", + "phone": "+1 (972) 590-3152" + }, + { + "_id": "55d2fc868036a76e42f30954", + "age": 24, + "name": "Roth Murray", + "gender": "male", + "company": "DEEPENDS", + "email": "rothmurray@deepends.com", + "phone": "+1 (944) 408-2208" + }, + { + "_id": "55d2fc8688ac278604ea592b", + "age": 24, + "name": "Cecelia Lambert", + "gender": "female", + "company": "SLOFAST", + "email": "cecelialambert@slofast.com", + "phone": "+1 (907) 485-2284" + }, + { + "_id": "55d2fc86b05fc3906ce838a2", + "age": 31, + "name": "Leah Ferguson", + "gender": "female", + "company": "NEBULEAN", + "email": "leahferguson@nebulean.com", + "phone": "+1 (883) 574-2101" + }, + { + "_id": "55d2fc86d21f9d73f11076b9", + "age": 28, + "name": "Therese Mueller", + "gender": "female", + "company": "STEELFAB", + "email": "theresemueller@steelfab.com", + "phone": "+1 (920) 485-2265" + }, + { + "_id": "55d2fc866254f87d0389dc98", + "age": 32, + "name": "Wanda Byrd", + "gender": "female", + "company": "HOTCAKES", + "email": "wandabyrd@hotcakes.com", + "phone": "+1 (893) 579-3658" + }, + { + "_id": "55d2fc868cf04450063aba0e", + "age": 25, + "name": "Felecia Mccall", + "gender": "female", + "company": "GINK", + "email": "feleciamccall@gink.com", + "phone": "+1 (848) 486-3047" + }, + { + "_id": "55d2fc86e0ee0341850d35ab", + "age": 37, + "name": "Goldie Stafford", + "gender": "female", + "company": "FIREWAX", + "email": "goldiestafford@firewax.com", + "phone": "+1 (831) 507-3578" + }, + { + "_id": "55d2fc868acfa20489a61720", + "age": 20, + "name": "Amy Patterson", + "gender": "female", + "company": "KNEEDLES", + "email": "amypatterson@kneedles.com", + "phone": "+1 (950) 478-3558" + }, + { + "_id": "55d2fc8680c104681b074336", + "age": 29, + "name": "Robles Alford", + "gender": "male", + "company": "TALENDULA", + "email": "roblesalford@talendula.com", + "phone": "+1 (813) 424-3650" + }, + { + "_id": "55d2fc86faeb57024907ea70", + "age": 28, + "name": "Adkins Hampton", + "gender": "male", + "company": "SYNKGEN", + "email": "adkinshampton@synkgen.com", + "phone": "+1 (974) 522-2517" + }, + { + "_id": "55d2fc866ba878e18ee65c0f", + "age": 32, + "name": "Bernadine Trevino", + "gender": "female", + "company": "EVENTEX", + "email": "bernadinetrevino@eventex.com", + "phone": "+1 (914) 577-2655" + }, + { + "_id": "55d2fc86524364ec8cb2d0c7", + "age": 38, + "name": "Robin Le", + "gender": "female", + "company": "GALLAXIA", + "email": "robinle@gallaxia.com", + "phone": "+1 (821) 472-2416" + }, + { + "_id": "55d2fc86c53dc98dacabc399", + "age": 23, + "name": "Leanna Hicks", + "gender": "female", + "company": "ZEROLOGY", + "email": "leannahicks@zerology.com", + "phone": "+1 (946) 531-3368" + }, + { + "_id": "55d2fc86e293b8cfb2d1a5bd", + "age": 31, + "name": "Herman Bridges", + "gender": "male", + "company": "DADABASE", + "email": "hermanbridges@dadabase.com", + "phone": "+1 (836) 408-3169" + }, + { + "_id": "55d2fc865dfc0cc61ec50b41", + "age": 30, + "name": "Olive Terrell", + "gender": "female", + "company": "ACCRUEX", + "email": "oliveterrell@accruex.com", + "phone": "+1 (837) 573-2059" + }, + { + "_id": "55d2fc8669dc78cdeb374ff5", + "age": 26, + "name": "Miranda Banks", + "gender": "female", + "company": "GEOFARM", + "email": "mirandabanks@geofarm.com", + "phone": "+1 (984) 574-2877" + }, + { + "_id": "55d2fc86f471f565df37a756", + "age": 31, + "name": "Noelle Wolf", + "gender": "female", + "company": "ENDIPINE", + "email": "noellewolf@endipine.com", + "phone": "+1 (880) 548-2427" + }, + { + "_id": "55d2fc8603e133136e5ca325", + "age": 38, + "name": "Amber Klein", + "gender": "female", + "company": "ISOPLEX", + "email": "amberklein@isoplex.com", + "phone": "+1 (853) 548-3856" + }, + { + "_id": "55d2fc86962b00cfd86e9d8d", + "age": 32, + "name": "Jodie Mcclain", + "gender": "female", + "company": "BESTO", + "email": "jodiemcclain@besto.com", + "phone": "+1 (857) 417-2691" + }, + { + "_id": "55d2fc8622eaf8052986a5bb", + "age": 35, + "name": "Margo Melendez", + "gender": "female", + "company": "KLUGGER", + "email": "margomelendez@klugger.com", + "phone": "+1 (823) 445-3570" + }, + { + "_id": "55d2fc861e37cd298741e801", + "age": 37, + "name": "Castaneda Dudley", + "gender": "male", + "company": "URBANSHEE", + "email": "castanedadudley@urbanshee.com", + "phone": "+1 (865) 449-2445" + }, + { + "_id": "55d2fc86e4c07ff933f7b531", + "age": 40, + "name": "Sherman Combs", + "gender": "male", + "company": "GRACKER", + "email": "shermancombs@gracker.com", + "phone": "+1 (817) 472-2316" + }, + { + "_id": "55d2fc86c4613e0a622727e6", + "age": 31, + "name": "Louise Nichols", + "gender": "female", + "company": "SPORTAN", + "email": "louisenichols@sportan.com", + "phone": "+1 (823) 543-2230" + }, + { + "_id": "55d2fc86d40dd218587cc632", + "age": 29, + "name": "Daniels Jacobs", + "gender": "male", + "company": "CALCULA", + "email": "danielsjacobs@calcula.com", + "phone": "+1 (979) 448-2244" + }, + { + "_id": "55d2fc86b44f3ec927f3b2e1", + "age": 22, + "name": "Cristina Crosby", + "gender": "female", + "company": "PHUEL", + "email": "cristinacrosby@phuel.com", + "phone": "+1 (816) 504-3557" + }, + { + "_id": "55d2fc86ba635f5436325487", + "age": 23, + "name": "Tisha Sawyer", + "gender": "female", + "company": "IMAGEFLOW", + "email": "tishasawyer@imageflow.com", + "phone": "+1 (894) 551-2933" + }, + { + "_id": "55d2fc8640e8af981a47216a", + "age": 32, + "name": "Janice Graham", + "gender": "female", + "company": "PYRAMIS", + "email": "janicegraham@pyramis.com", + "phone": "+1 (986) 409-2529" + }, + { + "_id": "55d2fc86cdbe80c5cb184731", + "age": 22, + "name": "Holman Joyce", + "gender": "male", + "company": "VERTON", + "email": "holmanjoyce@verton.com", + "phone": "+1 (955) 445-2054" + }, + { + "_id": "55d2fc860f70e0d0890e8e47", + "age": 21, + "name": "Webb Sears", + "gender": "male", + "company": "ENQUILITY", + "email": "webbsears@enquility.com", + "phone": "+1 (961) 406-3600" + }, + { + "_id": "55d2fc86150c8385661fde6f", + "age": 29, + "name": "Bush Farrell", + "gender": "male", + "company": "ENDICIL", + "email": "bushfarrell@endicil.com", + "phone": "+1 (894) 550-2963" + }, + { + "_id": "55d2fc862e2f1ca9662b03e3", + "age": 36, + "name": "Gay Walters", + "gender": "male", + "company": "ZENSURE", + "email": "gaywalters@zensure.com", + "phone": "+1 (859) 467-3816" + }, + { + "_id": "55d2fc86da1249ca6d3a068f", + "age": 33, + "name": "Marquez Palmer", + "gender": "male", + "company": "TURNLING", + "email": "marquezpalmer@turnling.com", + "phone": "+1 (878) 522-3859" + }, + { + "_id": "55d2fc86a61a66a69db5c8a9", + "age": 29, + "name": "Huber Parrish", + "gender": "male", + "company": "DELPHIDE", + "email": "huberparrish@delphide.com", + "phone": "+1 (947) 406-3267" + }, + { + "_id": "55d2fc86fff10fda8f106c2a", + "age": 22, + "name": "Foreman Cohen", + "gender": "male", + "company": "TROPOLIS", + "email": "foremancohen@tropolis.com", + "phone": "+1 (802) 409-2459" + }, + { + "_id": "55d2fc8632b185024494802d", + "age": 31, + "name": "Kathryn Peterson", + "gender": "female", + "company": "PROSURE", + "email": "kathrynpeterson@prosure.com", + "phone": "+1 (811) 531-3085" + }, + { + "_id": "55d2fc86f8e11410bb5b8e88", + "age": 34, + "name": "Aisha Duke", + "gender": "female", + "company": "APPLICA", + "email": "aishaduke@applica.com", + "phone": "+1 (977) 438-2754" + }, + { + "_id": "55d2fc86190a0097728af887", + "age": 37, + "name": "Maynard Henderson", + "gender": "male", + "company": "ILLUMITY", + "email": "maynardhenderson@illumity.com", + "phone": "+1 (832) 472-2261" + }, + { + "_id": "55d2fc86d298e6e1cf9332df", + "age": 33, + "name": "Villarreal Santiago", + "gender": "male", + "company": "QUAILCOM", + "email": "villarrealsantiago@quailcom.com", + "phone": "+1 (981) 422-2572" + }, + { + "_id": "55d2fc86b7eb3b9ed01fc186", + "age": 30, + "name": "Diaz Allison", + "gender": "male", + "company": "ISOSTREAM", + "email": "diazallison@isostream.com", + "phone": "+1 (883) 530-2186" + }, + { + "_id": "55d2fc86da92ac006d69c236", + "age": 20, + "name": "Kirkland Mccullough", + "gender": "male", + "company": "ADORNICA", + "email": "kirklandmccullough@adornica.com", + "phone": "+1 (950) 556-3562" + }, + { + "_id": "55d2fc86195f59929397f2fc", + "age": 32, + "name": "Dolores Howard", + "gender": "female", + "company": "ZINCA", + "email": "doloreshoward@zinca.com", + "phone": "+1 (904) 450-2101" + }, + { + "_id": "55d2fc860c9f707c22eaca7c", + "age": 26, + "name": "Mills Gamble", + "gender": "male", + "company": "BLEEKO", + "email": "millsgamble@bleeko.com", + "phone": "+1 (942) 402-2630" + }, + { + "_id": "55d2fc86b539183079fcab65", + "age": 35, + "name": "Walls Dotson", + "gender": "male", + "company": "GRUPOLI", + "email": "wallsdotson@grupoli.com", + "phone": "+1 (979) 477-3065" + }, + { + "_id": "55d2fc866377b14a737261c3", + "age": 29, + "name": "Hannah Coleman", + "gender": "female", + "company": "COMCUR", + "email": "hannahcoleman@comcur.com", + "phone": "+1 (944) 595-2415" + }, + { + "_id": "55d2fc867b77f0e8d5f08276", + "age": 25, + "name": "Kinney Oneill", + "gender": "male", + "company": "ELPRO", + "email": "kinneyoneill@elpro.com", + "phone": "+1 (851) 570-2363" + }, + { + "_id": "55d2fc868745f5f71578624c", + "age": 32, + "name": "Bennett Henson", + "gender": "male", + "company": "ZISIS", + "email": "bennetthenson@zisis.com", + "phone": "+1 (958) 584-2257" + }, + { + "_id": "55d2fc8604d1e7576b1a83fb", + "age": 32, + "name": "Ross Chavez", + "gender": "male", + "company": "DUOFLEX", + "email": "rosschavez@duoflex.com", + "phone": "+1 (890) 556-2052" + }, + { + "_id": "55d2fc86bd556093136802dd", + "age": 26, + "name": "Raymond Rutledge", + "gender": "male", + "company": "BUNGA", + "email": "raymondrutledge@bunga.com", + "phone": "+1 (994) 504-2118" + }, + { + "_id": "55d2fc86a122c7d89560c130", + "age": 31, + "name": "Rosemary Larsen", + "gender": "female", + "company": "EXOPLODE", + "email": "rosemarylarsen@exoplode.com", + "phone": "+1 (864) 558-3569" + }, + { + "_id": "55d2fc86dd3fe9ea62fdbea1", + "age": 27, + "name": "Tara Roth", + "gender": "female", + "company": "GAPTEC", + "email": "tararoth@gaptec.com", + "phone": "+1 (893) 531-2962" + }, + { + "_id": "55d2fc866f372ca411cd425d", + "age": 30, + "name": "Ronda Sheppard", + "gender": "female", + "company": "GREEKER", + "email": "rondasheppard@greeker.com", + "phone": "+1 (963) 569-2851" + }, + { + "_id": "55d2fc860a0dc369ac8bed9f", + "age": 37, + "name": "Nita Washington", + "gender": "female", + "company": "AQUASURE", + "email": "nitawashington@aquasure.com", + "phone": "+1 (858) 579-3734" + }, + { + "_id": "55d2fc863d59ecccd8b482b7", + "age": 34, + "name": "Shannon Sanchez", + "gender": "male", + "company": "QUORDATE", + "email": "shannonsanchez@quordate.com", + "phone": "+1 (880) 542-2259" + }, + { + "_id": "55d2fc861c73e366d8983f98", + "age": 23, + "name": "Amalia George", + "gender": "female", + "company": "QUINEX", + "email": "amaliageorge@quinex.com", + "phone": "+1 (918) 412-3805" + }, + { + "_id": "55d2fc866910c4329a167746", + "age": 30, + "name": "West Parsons", + "gender": "male", + "company": "RODEMCO", + "email": "westparsons@rodemco.com", + "phone": "+1 (869) 480-3404" + }, + { + "_id": "55d2fc861d97ff31a8eb7f06", + "age": 30, + "name": "Day Mercado", + "gender": "male", + "company": "ENJOLA", + "email": "daymercado@enjola.com", + "phone": "+1 (861) 546-2601" + }, + { + "_id": "55d2fc861e100baa15994161", + "age": 20, + "name": "Hubbard Randolph", + "gender": "male", + "company": "INSOURCE", + "email": "hubbardrandolph@insource.com", + "phone": "+1 (939) 422-2753" + }, + { + "_id": "55d2fc86dc33a0101292ea57", + "age": 32, + "name": "Ward Patrick", + "gender": "male", + "company": "ZUVY", + "email": "wardpatrick@zuvy.com", + "phone": "+1 (852) 461-3079" + }, + { + "_id": "55d2fc863074ff55ec22d7ac", + "age": 26, + "name": "Barker Small", + "gender": "male", + "company": "ZIDANT", + "email": "barkersmall@zidant.com", + "phone": "+1 (901) 514-3653" + }, + { + "_id": "55d2fc86008067de21a65447", + "age": 26, + "name": "Margret Porter", + "gender": "female", + "company": "DYMI", + "email": "margretporter@dymi.com", + "phone": "+1 (820) 404-2199" + }, + { + "_id": "55d2fc8694b7f8670d10781e", + "age": 40, + "name": "Roslyn Richmond", + "gender": "female", + "company": "SONGBIRD", + "email": "roslynrichmond@songbird.com", + "phone": "+1 (913) 406-3720" + }, + { + "_id": "55d2fc86ffff28aef88e1d69", + "age": 29, + "name": "Jimenez Yates", + "gender": "male", + "company": "IDETICA", + "email": "jimenezyates@idetica.com", + "phone": "+1 (803) 421-2358" + }, + { + "_id": "55d2fc8604bfeab3a975b2d8", + "age": 21, + "name": "Mcneil Jensen", + "gender": "male", + "company": "EXOBLUE", + "email": "mcneiljensen@exoblue.com", + "phone": "+1 (856) 581-3756" + }, + { + "_id": "55d2fc86bee368958f567c45", + "age": 40, + "name": "Millicent Trujillo", + "gender": "female", + "company": "ARTWORLDS", + "email": "millicenttrujillo@artworlds.com", + "phone": "+1 (926) 456-2237" + }, + { + "_id": "55d2fc86de2a5a6babe51d48", + "age": 26, + "name": "Dorothea Duffy", + "gender": "female", + "company": "EBIDCO", + "email": "dorotheaduffy@ebidco.com", + "phone": "+1 (873) 458-2694" + }, + { + "_id": "55d2fc86f8793320727d2020", + "age": 34, + "name": "Gloria Ward", + "gender": "female", + "company": "EXOVENT", + "email": "gloriaward@exovent.com", + "phone": "+1 (948) 552-3275" + }, + { + "_id": "55d2fc86967e5599adde68e8", + "age": 33, + "name": "Denise Hogan", + "gender": "female", + "company": "GLEAMINK", + "email": "denisehogan@gleamink.com", + "phone": "+1 (902) 544-2943" + }, + { + "_id": "55d2fc862833baf2918f7860", + "age": 23, + "name": "Clemons Berger", + "gender": "male", + "company": "ARTIQ", + "email": "clemonsberger@artiq.com", + "phone": "+1 (873) 516-2440" + }, + { + "_id": "55d2fc8610af4c20d426854a", + "age": 40, + "name": "Colette Scott", + "gender": "female", + "company": "INVENTURE", + "email": "colettescott@inventure.com", + "phone": "+1 (814) 556-3466" + }, + { + "_id": "55d2fc8697af4d0d06a2c4a6", + "age": 34, + "name": "Chavez Aguilar", + "gender": "male", + "company": "ISBOL", + "email": "chavezaguilar@isbol.com", + "phone": "+1 (952) 543-3992" + }, + { + "_id": "55d2fc861da12d86036c3e48", + "age": 29, + "name": "Arnold Collier", + "gender": "male", + "company": "MELBACOR", + "email": "arnoldcollier@melbacor.com", + "phone": "+1 (907) 404-2090" + }, + { + "_id": "55d2fc86deb921c78b56fec1", + "age": 24, + "name": "Nixon Ingram", + "gender": "male", + "company": "ELENTRIX", + "email": "nixoningram@elentrix.com", + "phone": "+1 (884) 596-3023" + }, + { + "_id": "55d2fc86a8b288ba6b8aa48f", + "age": 28, + "name": "Hurst Hull", + "gender": "male", + "company": "INCUBUS", + "email": "hursthull@incubus.com", + "phone": "+1 (970) 445-2279" + }, + { + "_id": "55d2fc864dc593d113336b1b", + "age": 32, + "name": "Giles Jacobson", + "gender": "male", + "company": "MIXERS", + "email": "gilesjacobson@mixers.com", + "phone": "+1 (868) 402-3161" + }, + { + "_id": "55d2fc86fd0246fcd0c8f864", + "age": 25, + "name": "Millie Giles", + "gender": "female", + "company": "ZAJ", + "email": "milliegiles@zaj.com", + "phone": "+1 (831) 535-3535" + }, + { + "_id": "55d2fc86dceeca0deb9f6d67", + "age": 21, + "name": "Sargent Morse", + "gender": "male", + "company": "ANARCO", + "email": "sargentmorse@anarco.com", + "phone": "+1 (994) 536-2563" + }, + { + "_id": "55d2fc86abbd1add64df6aa2", + "age": 26, + "name": "Reed Camacho", + "gender": "male", + "company": "ICOLOGY", + "email": "reedcamacho@icology.com", + "phone": "+1 (999) 408-3042" + }, + { + "_id": "55d2fc86a2593db6d584f022", + "age": 21, + "name": "Bryant Colon", + "gender": "male", + "company": "ZANILLA", + "email": "bryantcolon@zanilla.com", + "phone": "+1 (964) 570-3418" + }, + { + "_id": "55d2fc86f980f74ba093fde6", + "age": 38, + "name": "Rosella Pierce", + "gender": "female", + "company": "PRIMORDIA", + "email": "rosellapierce@primordia.com", + "phone": "+1 (936) 579-3014" + }, + { + "_id": "55d2fc86797a0001329071fb", + "age": 20, + "name": "Patrice Mckee", + "gender": "female", + "company": "GEEKY", + "email": "patricemckee@geeky.com", + "phone": "+1 (866) 565-3764" + }, + { + "_id": "55d2fc86bcc6181d0b8cae17", + "age": 29, + "name": "Wilkinson Levy", + "gender": "male", + "company": "FISHLAND", + "email": "wilkinsonlevy@fishland.com", + "phone": "+1 (998) 509-3800" + }, + { + "_id": "55d2fc86784b1e22004d637d", + "age": 28, + "name": "Isabella Snyder", + "gender": "female", + "company": "TETRATREX", + "email": "isabellasnyder@tetratrex.com", + "phone": "+1 (998) 451-2871" + }, + { + "_id": "55d2fc8603abab523ff1f02d", + "age": 35, + "name": "Claudine Berg", + "gender": "female", + "company": "ZYTREK", + "email": "claudineberg@zytrek.com", + "phone": "+1 (836) 563-3376" + }, + { + "_id": "55d2fc862796e94846a82a8b", + "age": 27, + "name": "Hyde Simon", + "gender": "male", + "company": "FUELWORKS", + "email": "hydesimon@fuelworks.com", + "phone": "+1 (872) 594-2291" + }, + { + "_id": "55d2fc86f2fa84a4f7ca4548", + "age": 29, + "name": "Wendy Roberts", + "gender": "female", + "company": "OPTICALL", + "email": "wendyroberts@opticall.com", + "phone": "+1 (895) 534-3852" + }, + { + "_id": "55d2fc8651dbb76674b6d2df", + "age": 40, + "name": "Tasha Erickson", + "gender": "female", + "company": "PURIA", + "email": "tashaerickson@puria.com", + "phone": "+1 (905) 526-2175" + }, + { + "_id": "55d2fc86b7f867e76e709d68", + "age": 32, + "name": "Marisa Dunlap", + "gender": "female", + "company": "GLUKGLUK", + "email": "marisadunlap@glukgluk.com", + "phone": "+1 (855) 400-2200" + }, + { + "_id": "55d2fc8695b6ccc0707570af", + "age": 38, + "name": "Kaitlin Baldwin", + "gender": "female", + "company": "EGYPTO", + "email": "kaitlinbaldwin@egypto.com", + "phone": "+1 (980) 482-3256" + }, + { + "_id": "55d2fc86f36b539d4494395d", + "age": 38, + "name": "Abigail Kirkland", + "gender": "female", + "company": "INRT", + "email": "abigailkirkland@inrt.com", + "phone": "+1 (916) 440-3469" + }, + { + "_id": "55d2fc86e401343dbf1de380", + "age": 34, + "name": "Marci Maynard", + "gender": "female", + "company": "INSURITY", + "email": "marcimaynard@insurity.com", + "phone": "+1 (917) 482-3601" + }, + { + "_id": "55d2fc86ba3f7fdb738899f4", + "age": 31, + "name": "Tanya Michael", + "gender": "female", + "company": "ENVIRE", + "email": "tanyamichael@envire.com", + "phone": "+1 (826) 436-3177" + }, + { + "_id": "55d2fc861cb8a1d3151ae2c1", + "age": 34, + "name": "Farrell Irwin", + "gender": "male", + "company": "EXTRAGEN", + "email": "farrellirwin@extragen.com", + "phone": "+1 (948) 442-3796" + }, + { + "_id": "55d2fc86b90f948949af3d8e", + "age": 27, + "name": "Dickson Shepherd", + "gender": "male", + "company": "COMTOURS", + "email": "dicksonshepherd@comtours.com", + "phone": "+1 (871) 458-2972" + }, + { + "_id": "55d2fc862065aeaf17581fa6", + "age": 40, + "name": "Fowler Adams", + "gender": "male", + "company": "NEPTIDE", + "email": "fowleradams@neptide.com", + "phone": "+1 (836) 431-3585" + }, + { + "_id": "55d2fc866aab8b242a30acfd", + "age": 22, + "name": "Durham Frost", + "gender": "male", + "company": "SNOWPOKE", + "email": "durhamfrost@snowpoke.com", + "phone": "+1 (801) 510-2084" + }, + { + "_id": "55d2fc86d3a4aafedc274ffa", + "age": 27, + "name": "Delia Barnes", + "gender": "female", + "company": "CONFERIA", + "email": "deliabarnes@conferia.com", + "phone": "+1 (999) 471-2182" + }, + { + "_id": "55d2fc8601ed1a317e289785", + "age": 27, + "name": "Gill Tillman", + "gender": "male", + "company": "MATRIXITY", + "email": "gilltillman@matrixity.com", + "phone": "+1 (880) 419-3960" + }, + { + "_id": "55d2fc86098dda4fa3812793", + "age": 27, + "name": "Loraine Saunders", + "gender": "female", + "company": "DOGTOWN", + "email": "lorainesaunders@dogtown.com", + "phone": "+1 (868) 500-3061" + }, + { + "_id": "55d2fc865f9615b558837892", + "age": 34, + "name": "Lou Mcdonald", + "gender": "female", + "company": "COMCUBINE", + "email": "loumcdonald@comcubine.com", + "phone": "+1 (857) 568-3427" + }, + { + "_id": "55d2fc865a5a752bb88dbcd8", + "age": 28, + "name": "Beth Lester", + "gender": "female", + "company": "EARTHWAX", + "email": "bethlester@earthwax.com", + "phone": "+1 (805) 565-2363" + }, + { + "_id": "55d2fc862e7d03a2f76c9e58", + "age": 37, + "name": "Karin Mason", + "gender": "female", + "company": "GEEKNET", + "email": "karinmason@geeknet.com", + "phone": "+1 (923) 438-3684" + }, + { + "_id": "55d2fc8617970ba4987ed4b3", + "age": 39, + "name": "Jeanine Watson", + "gender": "female", + "company": "LINGOAGE", + "email": "jeaninewatson@lingoage.com", + "phone": "+1 (919) 444-3722" + }, + { + "_id": "55d2fc86445181571721c6c9", + "age": 20, + "name": "Soto Wilkins", + "gender": "male", + "company": "JUMPSTACK", + "email": "sotowilkins@jumpstack.com", + "phone": "+1 (818) 518-3028" + }, + { + "_id": "55d2fc86b8b146361e17fa83", + "age": 20, + "name": "Mabel Fields", + "gender": "female", + "company": "MAGNINA", + "email": "mabelfields@magnina.com", + "phone": "+1 (861) 583-2161" + }, + { + "_id": "55d2fc8667abfe5721a74176", + "age": 26, + "name": "Ruthie Bailey", + "gender": "female", + "company": "ROCKLOGIC", + "email": "ruthiebailey@rocklogic.com", + "phone": "+1 (872) 402-3619" + }, + { + "_id": "55d2fc865cfa092adb20dc0f", + "age": 22, + "name": "Vaughan Vincent", + "gender": "male", + "company": "ACIUM", + "email": "vaughanvincent@acium.com", + "phone": "+1 (919) 548-3948" + }, + { + "_id": "55d2fc86fe25d347c15e1749", + "age": 38, + "name": "Myrtle Burris", + "gender": "female", + "company": "IRACK", + "email": "myrtleburris@irack.com", + "phone": "+1 (840) 526-2646" + }, + { + "_id": "55d2fc8689cd98b47e44118d", + "age": 37, + "name": "Peggy Mercer", + "gender": "female", + "company": "ESCENTA", + "email": "peggymercer@escenta.com", + "phone": "+1 (817) 574-3310" + }, + { + "_id": "55d2fc86ae662f567ae09404", + "age": 24, + "name": "Mollie Simmons", + "gender": "female", + "company": "STOCKPOST", + "email": "molliesimmons@stockpost.com", + "phone": "+1 (854) 461-2851" + }, + { + "_id": "55d2fc86f738f2bd6ebebd1c", + "age": 37, + "name": "Case Cox", + "gender": "male", + "company": "ACCEL", + "email": "casecox@accel.com", + "phone": "+1 (853) 473-2780" + }, + { + "_id": "55d2fc86c80907be4ff386d5", + "age": 26, + "name": "Snyder Mcclure", + "gender": "male", + "company": "PLASMOX", + "email": "snydermcclure@plasmox.com", + "phone": "+1 (980) 583-3213" + }, + { + "_id": "55d2fc86ed956580fa2cff25", + "age": 31, + "name": "Kelly Malone", + "gender": "male", + "company": "XANIDE", + "email": "kellymalone@xanide.com", + "phone": "+1 (871) 436-2431" + }, + { + "_id": "55d2fc86fcd2b8d749bd1feb", + "age": 20, + "name": "Jackie Carr", + "gender": "female", + "company": "VOIPA", + "email": "jackiecarr@voipa.com", + "phone": "+1 (807) 431-3436" + }, + { + "_id": "55d2fc86d9aac03a007489ef", + "age": 34, + "name": "Cochran Walter", + "gender": "male", + "company": "NIKUDA", + "email": "cochranwalter@nikuda.com", + "phone": "+1 (977) 410-3770" + }, + { + "_id": "55d2fc86140fefcd667c533f", + "age": 22, + "name": "Ellen Ortiz", + "gender": "female", + "company": "NEWCUBE", + "email": "ellenortiz@newcube.com", + "phone": "+1 (980) 541-3099" + }, + { + "_id": "55d2fc86d374c6a649e5877e", + "age": 35, + "name": "Concetta Beard", + "gender": "female", + "company": "PLASMOS", + "email": "concettabeard@plasmos.com", + "phone": "+1 (839) 539-2423" + }, + { + "_id": "55d2fc861ee809861b7c2b38", + "age": 33, + "name": "Josephine Alexander", + "gender": "female", + "company": "LOVEPAD", + "email": "josephinealexander@lovepad.com", + "phone": "+1 (880) 452-2208" + }, + { + "_id": "55d2fc8677db4f8446d43041", + "age": 39, + "name": "Melisa Dean", + "gender": "female", + "company": "GEOFORM", + "email": "melisadean@geoform.com", + "phone": "+1 (934) 452-2532" + }, + { + "_id": "55d2fc861dc0856e2ec744ab", + "age": 26, + "name": "Morgan Galloway", + "gender": "male", + "company": "ELEMANTRA", + "email": "morgangalloway@elemantra.com", + "phone": "+1 (888) 461-2261" + }, + { + "_id": "55d2fc869bc97691cec7d569", + "age": 26, + "name": "Curtis Griffith", + "gender": "male", + "company": "TINGLES", + "email": "curtisgriffith@tingles.com", + "phone": "+1 (881) 517-2174" + }, + { + "_id": "55d2fc8624b80b5986e1de83", + "age": 40, + "name": "Gentry Mccarthy", + "gender": "male", + "company": "GEEKOLA", + "email": "gentrymccarthy@geekola.com", + "phone": "+1 (908) 559-3049" + }, + { + "_id": "55d2fc869c2a158da79f9a7f", + "age": 37, + "name": "Lancaster Justice", + "gender": "male", + "company": "NAXDIS", + "email": "lancasterjustice@naxdis.com", + "phone": "+1 (980) 456-3515" + }, + { + "_id": "55d2fc867c8e30a12fddcb79", + "age": 28, + "name": "Jenifer Barr", + "gender": "female", + "company": "ZORROMOP", + "email": "jeniferbarr@zorromop.com", + "phone": "+1 (901) 440-3979" + }, + { + "_id": "55d2fc8696d7c8b59507f10c", + "age": 35, + "name": "Benjamin Nolan", + "gender": "male", + "company": "DIGITALUS", + "email": "benjaminnolan@digitalus.com", + "phone": "+1 (828) 582-3041" + }, + { + "_id": "55d2fc86b7e719a5c07b9f9e", + "age": 27, + "name": "Beach Valentine", + "gender": "male", + "company": "KAGGLE", + "email": "beachvalentine@kaggle.com", + "phone": "+1 (961) 563-3631" + }, + { + "_id": "55d2fc860bb827b4eab70038", + "age": 27, + "name": "Brady Moore", + "gender": "male", + "company": "CORPULSE", + "email": "bradymoore@corpulse.com", + "phone": "+1 (859) 434-2962" + }, + { + "_id": "55d2fc862fc00a2ce6ab7f32", + "age": 27, + "name": "Page Ray", + "gender": "male", + "company": "BEZAL", + "email": "pageray@bezal.com", + "phone": "+1 (845) 492-2182" + }, + { + "_id": "55d2fc861b57330b56d5b3f8", + "age": 40, + "name": "Lupe Gould", + "gender": "female", + "company": "DOGNOSIS", + "email": "lupegould@dognosis.com", + "phone": "+1 (955) 579-2141" + }, + { + "_id": "55d2fc864a8fabbf89d106cd", + "age": 23, + "name": "Melva Abbott", + "gender": "female", + "company": "CODACT", + "email": "melvaabbott@codact.com", + "phone": "+1 (801) 478-2678" + }, + { + "_id": "55d2fc86107ad0ad66c9d3ea", + "age": 22, + "name": "Mendez Middleton", + "gender": "male", + "company": "CUJO", + "email": "mendezmiddleton@cujo.com", + "phone": "+1 (908) 430-2032" + }, + { + "_id": "55d2fc86d2f5edd1222e87cc", + "age": 31, + "name": "Meredith Ayers", + "gender": "female", + "company": "OPTYK", + "email": "meredithayers@optyk.com", + "phone": "+1 (875) 472-2514" + }, + { + "_id": "55d2fc86551f2a796de0f3ad", + "age": 21, + "name": "Burns Serrano", + "gender": "male", + "company": "ZOUNDS", + "email": "burnsserrano@zounds.com", + "phone": "+1 (939) 558-2221" + }, + { + "_id": "55d2fc86540849ad56aeac98", + "age": 30, + "name": "Barr Sykes", + "gender": "male", + "company": "CORECOM", + "email": "barrsykes@corecom.com", + "phone": "+1 (982) 523-3577" + }, + { + "_id": "55d2fc86e7d43d06e4d135f2", + "age": 28, + "name": "Julie Johnson", + "gender": "female", + "company": "BIOTICA", + "email": "juliejohnson@biotica.com", + "phone": "+1 (918) 487-3230" + }, + { + "_id": "55d2fc86450521acf4a465d9", + "age": 23, + "name": "Dawn Vinson", + "gender": "female", + "company": "MENBRAIN", + "email": "dawnvinson@menbrain.com", + "phone": "+1 (936) 525-3273" + }, + { + "_id": "55d2fc86df3e093a104f498b", + "age": 30, + "name": "Ginger Ryan", + "gender": "female", + "company": "ENTHAZE", + "email": "gingerryan@enthaze.com", + "phone": "+1 (865) 530-2726" + }, + { + "_id": "55d2fc86b6a6fed233908500", + "age": 40, + "name": "Mcconnell Prince", + "gender": "male", + "company": "TRIPSCH", + "email": "mcconnellprince@tripsch.com", + "phone": "+1 (923) 586-2117" + }, + { + "_id": "55d2fc8619de2514561b7ac1", + "age": 23, + "name": "Peck Blackwell", + "gender": "male", + "company": "ASSISTIA", + "email": "peckblackwell@assistia.com", + "phone": "+1 (988) 549-3418" + }, + { + "_id": "55d2fc8619b2c263aec32e51", + "age": 32, + "name": "Simmons Benton", + "gender": "male", + "company": "TROLLERY", + "email": "simmonsbenton@trollery.com", + "phone": "+1 (924) 439-2962" + }, + { + "_id": "55d2fc8612c77e18fa3c743d", + "age": 36, + "name": "Mari Silva", + "gender": "female", + "company": "MAGMINA", + "email": "marisilva@magmina.com", + "phone": "+1 (863) 509-3186" + }, + { + "_id": "55d2fc86226a40ce862e518e", + "age": 40, + "name": "Erin Jefferson", + "gender": "female", + "company": "VIAGREAT", + "email": "erinjefferson@viagreat.com", + "phone": "+1 (921) 408-2295" + }, + { + "_id": "55d2fc8601013536fc55a097", + "age": 37, + "name": "Nellie Ballard", + "gender": "female", + "company": "GEOFORMA", + "email": "nellieballard@geoforma.com", + "phone": "+1 (918) 406-2600" + }, + { + "_id": "55d2fc864402965704b3453a", + "age": 27, + "name": "Austin Brewer", + "gender": "male", + "company": "METROZ", + "email": "austinbrewer@metroz.com", + "phone": "+1 (968) 584-2959" + }, + { + "_id": "55d2fc8690a7d6957a09f6c8", + "age": 34, + "name": "Pickett Buckley", + "gender": "male", + "company": "SUREPLEX", + "email": "pickettbuckley@sureplex.com", + "phone": "+1 (975) 520-3259" + }, + { + "_id": "55d2fc8695f3020c96b14f95", + "age": 39, + "name": "Coleen Herman", + "gender": "female", + "company": "NORALI", + "email": "coleenherman@norali.com", + "phone": "+1 (916) 506-2704" + }, + { + "_id": "55d2fc869b58b96d1d2cdfcc", + "age": 37, + "name": "Roy Guerrero", + "gender": "male", + "company": "PLUTORQUE", + "email": "royguerrero@plutorque.com", + "phone": "+1 (922) 541-3741" + }, + { + "_id": "55d2fc86ba1ed1189e9020ee", + "age": 29, + "name": "Oneal Curtis", + "gender": "male", + "company": "DATAGEN", + "email": "onealcurtis@datagen.com", + "phone": "+1 (847) 421-3483" + }, + { + "_id": "55d2fc86b5c5e89eb9fe1e79", + "age": 29, + "name": "Chaney Christian", + "gender": "male", + "company": "XPLOR", + "email": "chaneychristian@xplor.com", + "phone": "+1 (847) 517-3918" + }, + { + "_id": "55d2fc86f6b5a3d91952d941", + "age": 29, + "name": "Cantu Richard", + "gender": "male", + "company": "ZANYMAX", + "email": "canturichard@zanymax.com", + "phone": "+1 (972) 487-2616" + }, + { + "_id": "55d2fc8609b0f98cbc2e101d", + "age": 37, + "name": "Newton Barrera", + "gender": "male", + "company": "PORTALINE", + "email": "newtonbarrera@portaline.com", + "phone": "+1 (964) 527-3130" + }, + { + "_id": "55d2fc86d0e063ca7629c11b", + "age": 28, + "name": "Jodi Pollard", + "gender": "female", + "company": "GOKO", + "email": "jodipollard@goko.com", + "phone": "+1 (957) 468-2658" + }, + { + "_id": "55d2fc8688d95c5c579bd328", + "age": 35, + "name": "Effie Nunez", + "gender": "female", + "company": "SNACKTION", + "email": "effienunez@snacktion.com", + "phone": "+1 (805) 576-3749" + }, + { + "_id": "55d2fc86f36c0eab69222b17", + "age": 38, + "name": "Haley Battle", + "gender": "male", + "company": "TERRAGEN", + "email": "haleybattle@terragen.com", + "phone": "+1 (955) 581-3931" + }, + { + "_id": "55d2fc86ee6900b197860a35", + "age": 29, + "name": "Cathy Vaughn", + "gender": "female", + "company": "CANOPOLY", + "email": "cathyvaughn@canopoly.com", + "phone": "+1 (875) 539-3578" + }, + { + "_id": "55d2fc86613f698b355e87bc", + "age": 26, + "name": "Mendoza Maxwell", + "gender": "male", + "company": "TERAPRENE", + "email": "mendozamaxwell@teraprene.com", + "phone": "+1 (820) 471-3500" + }, + { + "_id": "55d2fc86f86a80f78c45936a", + "age": 28, + "name": "Rosetta Hughes", + "gender": "female", + "company": "DEVILTOE", + "email": "rosettahughes@deviltoe.com", + "phone": "+1 (911) 418-2439" + }, + { + "_id": "55d2fc8683d3c4a377a984a5", + "age": 28, + "name": "Frazier Larson", + "gender": "male", + "company": "DAIDO", + "email": "frazierlarson@daido.com", + "phone": "+1 (995) 459-3756" + }, + { + "_id": "55d2fc8669baef03c4d6a4a5", + "age": 27, + "name": "Brittney Ratliff", + "gender": "female", + "company": "TALKALOT", + "email": "brittneyratliff@talkalot.com", + "phone": "+1 (865) 568-2986" + }, + { + "_id": "55d2fc86404583ccc6b6a4f4", + "age": 21, + "name": "Hancock Gilliam", + "gender": "male", + "company": "EXPOSA", + "email": "hancockgilliam@exposa.com", + "phone": "+1 (894) 519-3139" + }, + { + "_id": "55d2fc8675478e0b84bb5ab3", + "age": 20, + "name": "Lilia Mccormick", + "gender": "female", + "company": "DENTREX", + "email": "liliamccormick@dentrex.com", + "phone": "+1 (997) 552-3944" + }, + { + "_id": "55d2fc8658bf3954cdf52ebb", + "age": 20, + "name": "Chrystal Mcneil", + "gender": "female", + "company": "LIMOZEN", + "email": "chrystalmcneil@limozen.com", + "phone": "+1 (943) 519-2952" + }, + { + "_id": "55d2fc865ebec9e8c7adc383", + "age": 31, + "name": "Hebert Alston", + "gender": "male", + "company": "IZZBY", + "email": "hebertalston@izzby.com", + "phone": "+1 (953) 482-2029" + }, + { + "_id": "55d2fc86bf1085e6fd2e9ea1", + "age": 23, + "name": "Mcfarland Carrillo", + "gender": "male", + "company": "EQUITOX", + "email": "mcfarlandcarrillo@equitox.com", + "phone": "+1 (999) 478-3822" + }, + { + "_id": "55d2fc8641d4f2d09f539bd6", + "age": 40, + "name": "Porter Weaver", + "gender": "male", + "company": "QUILM", + "email": "porterweaver@quilm.com", + "phone": "+1 (831) 501-2739" + }, + { + "_id": "55d2fc869ee5331a50039e30", + "age": 23, + "name": "Dale Sims", + "gender": "male", + "company": "SCENTRIC", + "email": "dalesims@scentric.com", + "phone": "+1 (845) 597-2120" + }, + { + "_id": "55d2fc86cb96d31fe71e4a18", + "age": 22, + "name": "William Dixon", + "gender": "male", + "company": "NAMEBOX", + "email": "williamdixon@namebox.com", + "phone": "+1 (970) 448-2651" + }, + { + "_id": "55d2fc86e88daeb1e198671d", + "age": 33, + "name": "Patrica Reed", + "gender": "female", + "company": "CORPORANA", + "email": "patricareed@corporana.com", + "phone": "+1 (817) 457-2413" + }, + { + "_id": "55d2fc86c33d9f422bf8bfcc", + "age": 20, + "name": "Marylou Mcmillan", + "gender": "female", + "company": "RETRACK", + "email": "maryloumcmillan@retrack.com", + "phone": "+1 (908) 568-2328" + }, + { + "_id": "55d2fc86c4b1b139887e76f1", + "age": 20, + "name": "Maritza David", + "gender": "female", + "company": "ORBEAN", + "email": "maritzadavid@orbean.com", + "phone": "+1 (851) 503-3737" + }, + { + "_id": "55d2fc86ac0f360d91c740e6", + "age": 23, + "name": "Lorie Moses", + "gender": "female", + "company": "ROCKABYE", + "email": "loriemoses@rockabye.com", + "phone": "+1 (823) 431-2387" + }, + { + "_id": "55d2fc86d7d406569b386b17", + "age": 35, + "name": "Shields Weiss", + "gender": "male", + "company": "RODEOCEAN", + "email": "shieldsweiss@rodeocean.com", + "phone": "+1 (957) 490-3725" + }, + { + "_id": "55d2fc86e4f66ce770ae2c93", + "age": 25, + "name": "Eugenia Berry", + "gender": "female", + "company": "BISBA", + "email": "eugeniaberry@bisba.com", + "phone": "+1 (948) 403-3403" + }, + { + "_id": "55d2fc8618f2f4f428223522", + "age": 33, + "name": "Howard Compton", + "gender": "male", + "company": "NAMEGEN", + "email": "howardcompton@namegen.com", + "phone": "+1 (827) 439-3667" + }, + { + "_id": "55d2fc86f5adc0f6dd535ea6", + "age": 28, + "name": "Key Davis", + "gender": "male", + "company": "PORTICO", + "email": "keydavis@portico.com", + "phone": "+1 (873) 533-2980" + }, + { + "_id": "55d2fc86876669f4e9431417", + "age": 33, + "name": "Phillips Solis", + "gender": "male", + "company": "DANCITY", + "email": "phillipssolis@dancity.com", + "phone": "+1 (883) 481-3114" + }, + { + "_id": "55d2fc86f2bb610d7ad9ea36", + "age": 40, + "name": "Cash Pugh", + "gender": "male", + "company": "STUCCO", + "email": "cashpugh@stucco.com", + "phone": "+1 (873) 512-2106" + }, + { + "_id": "55d2fc863be1649d5bd3be39", + "age": 21, + "name": "Elinor Warner", + "gender": "female", + "company": "FOSSIEL", + "email": "elinorwarner@fossiel.com", + "phone": "+1 (950) 431-3679" + }, + { + "_id": "55d2fc86fdad2af5536237e2", + "age": 35, + "name": "Jacquelyn Doyle", + "gender": "female", + "company": "CYTREX", + "email": "jacquelyndoyle@cytrex.com", + "phone": "+1 (924) 569-2919" + }, + { + "_id": "55d2fc86f3affa20ab27edff", + "age": 33, + "name": "Jeannine Mosley", + "gender": "female", + "company": "ACUSAGE", + "email": "jeanninemosley@acusage.com", + "phone": "+1 (954) 517-2805" + }, + { + "_id": "55d2fc8670dd0dbdd6e4d195", + "age": 37, + "name": "Logan Brady", + "gender": "male", + "company": "TELLIFLY", + "email": "loganbrady@tellifly.com", + "phone": "+1 (861) 576-2313" + }, + { + "_id": "55d2fc86b9a15e4721982a39", + "age": 26, + "name": "Houston Joseph", + "gender": "male", + "company": "BOILICON", + "email": "houstonjoseph@boilicon.com", + "phone": "+1 (822) 519-3430" + }, + { + "_id": "55d2fc86f225999b0b8742d2", + "age": 38, + "name": "Rita Lindsey", + "gender": "female", + "company": "FIBEROX", + "email": "ritalindsey@fiberox.com", + "phone": "+1 (805) 551-3755" + }, + { + "_id": "55d2fc86e9dad38b6873b807", + "age": 22, + "name": "Strong Poole", + "gender": "male", + "company": "KINDALOO", + "email": "strongpoole@kindaloo.com", + "phone": "+1 (918) 426-2076" + }, + { + "_id": "55d2fc861608b965b2283827", + "age": 38, + "name": "Hines Mathews", + "gender": "male", + "company": "INTRADISK", + "email": "hinesmathews@intradisk.com", + "phone": "+1 (932) 420-2236" + }, + { + "_id": "55d2fc863079075f91241a16", + "age": 28, + "name": "Trina Wiley", + "gender": "female", + "company": "HATOLOGY", + "email": "trinawiley@hatology.com", + "phone": "+1 (855) 466-3287" + }, + { + "_id": "55d2fc86f2fae1a79253fb61", + "age": 23, + "name": "Kirby Tucker", + "gender": "male", + "company": "AQUAMATE", + "email": "kirbytucker@aquamate.com", + "phone": "+1 (935) 456-3272" + }, + { + "_id": "55d2fc86c42bf49f8202b2fa", + "age": 28, + "name": "Ballard Stein", + "gender": "male", + "company": "KOOGLE", + "email": "ballardstein@koogle.com", + "phone": "+1 (943) 586-2225" + }, + { + "_id": "55d2fc865db815da198c0776", + "age": 36, + "name": "Wagner Mcfarland", + "gender": "male", + "company": "ACCIDENCY", + "email": "wagnermcfarland@accidency.com", + "phone": "+1 (920) 533-2157" + }, + { + "_id": "55d2fc866aeb268fe48fd6be", + "age": 22, + "name": "Wiley Wilder", + "gender": "male", + "company": "KIDSTOCK", + "email": "wileywilder@kidstock.com", + "phone": "+1 (957) 459-3416" + }, + { + "_id": "55d2fc8606f67a423d303437", + "age": 37, + "name": "Rosario Slater", + "gender": "female", + "company": "SPRINGBEE", + "email": "rosarioslater@springbee.com", + "phone": "+1 (950) 506-3454" + }, + { + "_id": "55d2fc86510fd16a269a0201", + "age": 37, + "name": "Walker Mcdowell", + "gender": "male", + "company": "ONTAGENE", + "email": "walkermcdowell@ontagene.com", + "phone": "+1 (953) 579-3429" + }, + { + "_id": "55d2fc867d419d30f9394f56", + "age": 32, + "name": "Booth Pratt", + "gender": "male", + "company": "ZIGGLES", + "email": "boothpratt@ziggles.com", + "phone": "+1 (835) 453-3707" + }, + { + "_id": "55d2fc86e631b8f71bbe7b35", + "age": 33, + "name": "Georgia Carpenter", + "gender": "female", + "company": "STRALUM", + "email": "georgiacarpenter@stralum.com", + "phone": "+1 (923) 536-3557" + }, + { + "_id": "55d2fc866df6437afa4cfaf6", + "age": 26, + "name": "Harding Powers", + "gender": "male", + "company": "BEADZZA", + "email": "hardingpowers@beadzza.com", + "phone": "+1 (855) 467-2993" + }, + { + "_id": "55d2fc867ade492afcfc24a6", + "age": 37, + "name": "Kaye Brown", + "gender": "female", + "company": "AMTAS", + "email": "kayebrown@amtas.com", + "phone": "+1 (926) 444-3936" + }, + { + "_id": "55d2fc862bf33dd3169710ff", + "age": 24, + "name": "Mccray Padilla", + "gender": "male", + "company": "FUTURIS", + "email": "mccraypadilla@futuris.com", + "phone": "+1 (969) 561-3819" + }, + { + "_id": "55d2fc8666f74690303abf65", + "age": 35, + "name": "Moon Moss", + "gender": "male", + "company": "EURON", + "email": "moonmoss@euron.com", + "phone": "+1 (885) 514-2872" + }, + { + "_id": "55d2fc860a8b5abfdf57bd37", + "age": 26, + "name": "Lane Gregory", + "gender": "male", + "company": "SKINSERVE", + "email": "lanegregory@skinserve.com", + "phone": "+1 (818) 455-3048" + }, + { + "_id": "55d2fc86f17541fe3b770b26", + "age": 30, + "name": "Cummings Good", + "gender": "male", + "company": "GEEKOLOGY", + "email": "cummingsgood@geekology.com", + "phone": "+1 (821) 426-3476" + }, + { + "_id": "55d2fc865b6232d788278e1f", + "age": 26, + "name": "Lottie Soto", + "gender": "female", + "company": "INTERGEEK", + "email": "lottiesoto@intergeek.com", + "phone": "+1 (905) 516-2928" + }, + { + "_id": "55d2fc868388a50b97dda5c2", + "age": 38, + "name": "Bridges Bell", + "gender": "male", + "company": "MIRACULA", + "email": "bridgesbell@miracula.com", + "phone": "+1 (917) 438-3079" + }, + { + "_id": "55d2fc86cc2120d10b75c41b", + "age": 23, + "name": "Marcella Lancaster", + "gender": "female", + "company": "NAVIR", + "email": "marcellalancaster@navir.com", + "phone": "+1 (851) 478-2535" + }, + { + "_id": "55d2fc86f52bd008c87c6993", + "age": 32, + "name": "Foley Yang", + "gender": "male", + "company": "APEXTRI", + "email": "foleyyang@apextri.com", + "phone": "+1 (978) 504-2003" + }, + { + "_id": "55d2fc86088b65117b293eef", + "age": 21, + "name": "Debora Levine", + "gender": "female", + "company": "VANTAGE", + "email": "deboralevine@vantage.com", + "phone": "+1 (820) 472-2507" + }, + { + "_id": "55d2fc86765d079d8584c281", + "age": 30, + "name": "Jill Durham", + "gender": "female", + "company": "FUTURITY", + "email": "jilldurham@futurity.com", + "phone": "+1 (996) 499-2910" + }, + { + "_id": "55d2fc860ed183243d043f79", + "age": 28, + "name": "Della Sherman", + "gender": "female", + "company": "EXTRO", + "email": "dellasherman@extro.com", + "phone": "+1 (893) 541-2867" + }, + { + "_id": "55d2fc8646733a05fa448c6e", + "age": 30, + "name": "Tamara Albert", + "gender": "female", + "company": "ECOLIGHT", + "email": "tamaraalbert@ecolight.com", + "phone": "+1 (870) 514-2615" + }, + { + "_id": "55d2fc86b8bf0a0f7ffb702e", + "age": 39, + "name": "Lynn Green", + "gender": "male", + "company": "SNIPS", + "email": "lynngreen@snips.com", + "phone": "+1 (938) 464-2073" + }, + { + "_id": "55d2fc863e577905fc3ea8e7", + "age": 29, + "name": "Barbra Tate", + "gender": "female", + "company": "ACRUEX", + "email": "barbratate@acruex.com", + "phone": "+1 (809) 418-2604" + }, + { + "_id": "55d2fc86335b53151fc242b5", + "age": 33, + "name": "Potts Dickerson", + "gender": "male", + "company": "SHADEASE", + "email": "pottsdickerson@shadease.com", + "phone": "+1 (967) 539-3330" + }, + { + "_id": "55d2fc86716df5cb28925d59", + "age": 36, + "name": "Nancy Woodard", + "gender": "female", + "company": "ZOSIS", + "email": "nancywoodard@zosis.com", + "phone": "+1 (811) 434-3223" + }, + { + "_id": "55d2fc86058113d9a4909796", + "age": 29, + "name": "Park Evans", + "gender": "male", + "company": "XUMONK", + "email": "parkevans@xumonk.com", + "phone": "+1 (836) 443-2361" + }, + { + "_id": "55d2fc8659b6d92fb2880f83", + "age": 25, + "name": "Nicole Sullivan", + "gender": "female", + "company": "QUALITEX", + "email": "nicolesullivan@qualitex.com", + "phone": "+1 (823) 584-2994" + }, + { + "_id": "55d2fc86510772cfe78617a9", + "age": 33, + "name": "Bowers Barnett", + "gender": "male", + "company": "HOUSEDOWN", + "email": "bowersbarnett@housedown.com", + "phone": "+1 (872) 466-3548" + }, + { + "_id": "55d2fc868dec3ac619cfc262", + "age": 21, + "name": "Jeri Nielsen", + "gender": "female", + "company": "MOBILDATA", + "email": "jerinielsen@mobildata.com", + "phone": "+1 (886) 581-2045" + }, + { + "_id": "55d2fc863e89d05123e90aaf", + "age": 26, + "name": "Delores Farmer", + "gender": "female", + "company": "XERONK", + "email": "deloresfarmer@xeronk.com", + "phone": "+1 (872) 556-2716" + }, + { + "_id": "55d2fc8618c10cef39c48f97", + "age": 38, + "name": "Mathis Walsh", + "gender": "male", + "company": "VURBO", + "email": "mathiswalsh@vurbo.com", + "phone": "+1 (837) 459-2909" + }, + { + "_id": "55d2fc868b3487cb71c8bac4", + "age": 20, + "name": "Ingrid Shelton", + "gender": "female", + "company": "ORBIN", + "email": "ingridshelton@orbin.com", + "phone": "+1 (914) 592-2364" + }, + { + "_id": "55d2fc86bcf5c885edacef50", + "age": 36, + "name": "Socorro Burns", + "gender": "female", + "company": "NETAGY", + "email": "socorroburns@netagy.com", + "phone": "+1 (931) 523-3116" + }, + { + "_id": "55d2fc8693f704f5c95c48a7", + "age": 39, + "name": "Jo Ware", + "gender": "female", + "company": "FLYBOYZ", + "email": "joware@flyboyz.com", + "phone": "+1 (844) 467-2192" + }, + { + "_id": "55d2fc86e87485075df33029", + "age": 38, + "name": "Emilia Flores", + "gender": "female", + "company": "ZAGGLES", + "email": "emiliaflores@zaggles.com", + "phone": "+1 (992) 408-2629" + }, + { + "_id": "55d2fc861ab925ca0b9b15c6", + "age": 31, + "name": "Burks Haney", + "gender": "male", + "company": "ZYPLE", + "email": "burkshaney@zyple.com", + "phone": "+1 (882) 401-2811" + }, + { + "_id": "55d2fc86e9c252588455b811", + "age": 31, + "name": "Holly Snow", + "gender": "female", + "company": "FURNAFIX", + "email": "hollysnow@furnafix.com", + "phone": "+1 (802) 592-2798" + }, + { + "_id": "55d2fc86e07c753096021423", + "age": 25, + "name": "Frances Hayden", + "gender": "female", + "company": "SNORUS", + "email": "franceshayden@snorus.com", + "phone": "+1 (818) 481-2431" + }, + { + "_id": "55d2fc86496884876065c346", + "age": 28, + "name": "Lila Lewis", + "gender": "female", + "company": "ZOMBOID", + "email": "lilalewis@zomboid.com", + "phone": "+1 (893) 593-2423" + }, + { + "_id": "55d2fc865c6ea080a4d0a5b5", + "age": 34, + "name": "Torres Finley", + "gender": "male", + "company": "MAXIMIND", + "email": "torresfinley@maximind.com", + "phone": "+1 (888) 504-3674" + }, + { + "_id": "55d2fc86391c44c545fe989f", + "age": 23, + "name": "Horne James", + "gender": "male", + "company": "PETICULAR", + "email": "hornejames@peticular.com", + "phone": "+1 (865) 558-2517" + }, + { + "_id": "55d2fc86c8c2529939eada03", + "age": 28, + "name": "Jennings Wallace", + "gender": "male", + "company": "CAXT", + "email": "jenningswallace@caxt.com", + "phone": "+1 (833) 599-3895" + }, + { + "_id": "55d2fc86ee656fe865fbde29", + "age": 33, + "name": "Lily Gilmore", + "gender": "female", + "company": "RADIANTIX", + "email": "lilygilmore@radiantix.com", + "phone": "+1 (854) 561-3148" + }, + { + "_id": "55d2fc868455126d596ab537", + "age": 35, + "name": "Kristy Delacruz", + "gender": "female", + "company": "EQUICOM", + "email": "kristydelacruz@equicom.com", + "phone": "+1 (871) 502-3732" + }, + { + "_id": "55d2fc863096983ef370ca49", + "age": 31, + "name": "England Vance", + "gender": "male", + "company": "VOLAX", + "email": "englandvance@volax.com", + "phone": "+1 (840) 593-3417" + }, + { + "_id": "55d2fc8671f3c1e30bee852f", + "age": 23, + "name": "Rodriguez Foreman", + "gender": "male", + "company": "EXTRAWEAR", + "email": "rodriguezforeman@extrawear.com", + "phone": "+1 (804) 497-2101" + }, + { + "_id": "55d2fc86bea38e2b0cd970cb", + "age": 35, + "name": "Richard Garrett", + "gender": "male", + "company": "SUPREMIA", + "email": "richardgarrett@supremia.com", + "phone": "+1 (925) 461-3414" + }, + { + "_id": "55d2fc862c5193ab7b4668b7", + "age": 40, + "name": "Connie Ortega", + "gender": "female", + "company": "ZILODYNE", + "email": "connieortega@zilodyne.com", + "phone": "+1 (838) 582-3241" + }, + { + "_id": "55d2fc865a6b25d6bc09180e", + "age": 24, + "name": "Solomon Bates", + "gender": "male", + "company": "EXOSPACE", + "email": "solomonbates@exospace.com", + "phone": "+1 (897) 496-2243" + }, + { + "_id": "55d2fc86a16a4e077136e4b5", + "age": 32, + "name": "Valencia Andrews", + "gender": "male", + "company": "NORSUP", + "email": "valenciaandrews@norsup.com", + "phone": "+1 (891) 503-3593" + }, + { + "_id": "55d2fc86bc5f1bf697a90465", + "age": 24, + "name": "Briggs Vasquez", + "gender": "male", + "company": "ZENTIA", + "email": "briggsvasquez@zentia.com", + "phone": "+1 (802) 415-3377" + }, + { + "_id": "55d2fc86c088d6466d83f8fc", + "age": 26, + "name": "Hester Rice", + "gender": "female", + "company": "CYTREK", + "email": "hesterrice@cytrek.com", + "phone": "+1 (855) 544-3905" + }, + { + "_id": "55d2fc8622fc3e78977288af", + "age": 25, + "name": "Shelly Hendrix", + "gender": "female", + "company": "POWERNET", + "email": "shellyhendrix@powernet.com", + "phone": "+1 (912) 431-2318" + }, + { + "_id": "55d2fc862cdec6cd321df6e0", + "age": 22, + "name": "Alison Newman", + "gender": "female", + "company": "COMTOUR", + "email": "alisonnewman@comtour.com", + "phone": "+1 (836) 582-3513" + }, + { + "_id": "55d2fc86e0070d54d4712ca4", + "age": 30, + "name": "French Rivera", + "gender": "male", + "company": "ACUMENTOR", + "email": "frenchrivera@acumentor.com", + "phone": "+1 (902) 579-2193" + }, + { + "_id": "55d2fc860f64b4423a9d6ffc", + "age": 38, + "name": "Terrell Mendez", + "gender": "male", + "company": "IMAGINART", + "email": "terrellmendez@imaginart.com", + "phone": "+1 (967) 494-2713" + }, + { + "_id": "55d2fc86548793116b225b3b", + "age": 30, + "name": "Parsons Robertson", + "gender": "male", + "company": "OBONES", + "email": "parsonsrobertson@obones.com", + "phone": "+1 (984) 434-2810" + }, + { + "_id": "55d2fc865b8b0d6a59db876c", + "age": 33, + "name": "Livingston Barry", + "gender": "male", + "company": "TYPHONICA", + "email": "livingstonbarry@typhonica.com", + "phone": "+1 (976) 560-3878" + }, + { + "_id": "55d2fc865ce23fb8b34cae53", + "age": 40, + "name": "Valeria Stout", + "gender": "female", + "company": "AVENETRO", + "email": "valeriastout@avenetro.com", + "phone": "+1 (885) 557-3624" + }, + { + "_id": "55d2fc86c03ea1d6e81563ac", + "age": 39, + "name": "Grimes Dyer", + "gender": "male", + "company": "GEOLOGIX", + "email": "grimesdyer@geologix.com", + "phone": "+1 (896) 533-2919" + }, + { + "_id": "55d2fc8655c0acb356a06c8f", + "age": 29, + "name": "Higgins Short", + "gender": "male", + "company": "BICOL", + "email": "higginsshort@bicol.com", + "phone": "+1 (976) 444-3073" + }, + { + "_id": "55d2fc865b6db005487c52bb", + "age": 34, + "name": "Gilmore Campos", + "gender": "male", + "company": "PASTURIA", + "email": "gilmorecampos@pasturia.com", + "phone": "+1 (862) 442-2147" + }, + { + "_id": "55d2fc863df4791bcb269217", + "age": 29, + "name": "Sloan Kane", + "gender": "male", + "company": "XELEGYL", + "email": "sloankane@xelegyl.com", + "phone": "+1 (946) 526-2275" + }, + { + "_id": "55d2fc86b2eb7dedbd5a9e8d", + "age": 26, + "name": "Mcpherson Thornton", + "gender": "male", + "company": "KAGE", + "email": "mcphersonthornton@kage.com", + "phone": "+1 (803) 478-2690" + }, + { + "_id": "55d2fc8603f6a8c17148c8dd", + "age": 31, + "name": "Christi Welch", + "gender": "female", + "company": "WARETEL", + "email": "christiwelch@waretel.com", + "phone": "+1 (999) 552-3114" + }, + { + "_id": "55d2fc86118d83cb9d06aa2e", + "age": 29, + "name": "Padilla Travis", + "gender": "male", + "company": "ENERVATE", + "email": "padillatravis@enervate.com", + "phone": "+1 (897) 577-3387" + }, + { + "_id": "55d2fc86aba06801708bed65", + "age": 22, + "name": "Stanton Casey", + "gender": "male", + "company": "BUZZMAKER", + "email": "stantoncasey@buzzmaker.com", + "phone": "+1 (858) 571-2667" + }, + { + "_id": "55d2fc86184810b00043a4b7", + "age": 29, + "name": "Krista Hernandez", + "gender": "female", + "company": "BIOHAB", + "email": "kristahernandez@biohab.com", + "phone": "+1 (832) 510-3654" + }, + { + "_id": "55d2fc86e9c951b5bcea3938", + "age": 36, + "name": "Deleon Oliver", + "gender": "male", + "company": "NETBOOK", + "email": "deleonoliver@netbook.com", + "phone": "+1 (934) 504-2964" + }, + { + "_id": "55d2fc86923000f3ea91ae38", + "age": 36, + "name": "Vasquez Fowler", + "gender": "male", + "company": "ORGANICA", + "email": "vasquezfowler@organica.com", + "phone": "+1 (949) 546-2722" + }, + { + "_id": "55d2fc861e12cd0fa6207a9e", + "age": 33, + "name": "Rutledge Keith", + "gender": "male", + "company": "COLAIRE", + "email": "rutledgekeith@colaire.com", + "phone": "+1 (936) 472-3739" + }, + { + "_id": "55d2fc86927eca39ef0c7ae9", + "age": 26, + "name": "Kirsten Valenzuela", + "gender": "female", + "company": "SEQUITUR", + "email": "kirstenvalenzuela@sequitur.com", + "phone": "+1 (958) 564-3259" + }, + { + "_id": "55d2fc869e922239ce293d2c", + "age": 40, + "name": "Garza Gutierrez", + "gender": "male", + "company": "SPLINX", + "email": "garzagutierrez@splinx.com", + "phone": "+1 (850) 525-3114" + }, + { + "_id": "55d2fc869dd88389d4785283", + "age": 27, + "name": "Shawna Peck", + "gender": "female", + "company": "UNQ", + "email": "shawnapeck@unq.com", + "phone": "+1 (961) 579-3704" + }, + { + "_id": "55d2fc86722d8e2a714bf7f2", + "age": 23, + "name": "Aurelia Mcpherson", + "gender": "female", + "company": "BOINK", + "email": "aureliamcpherson@boink.com", + "phone": "+1 (946) 479-2080" + }, + { + "_id": "55d2fc862324d173c26dfc68", + "age": 39, + "name": "Maryellen Daugherty", + "gender": "female", + "company": "ZILCH", + "email": "maryellendaugherty@zilch.com", + "phone": "+1 (817) 577-3290" + }, + { + "_id": "55d2fc86d311107f869da748", + "age": 35, + "name": "Zelma Hancock", + "gender": "female", + "company": "EVENTAGE", + "email": "zelmahancock@eventage.com", + "phone": "+1 (845) 578-3887" + }, + { + "_id": "55d2fc86afb4ede1f20a6d15", + "age": 34, + "name": "Tessa Adkins", + "gender": "female", + "company": "CONJURICA", + "email": "tessaadkins@conjurica.com", + "phone": "+1 (807) 497-2845" + }, + { + "_id": "55d2fc8648a2e36d6f97fad1", + "age": 22, + "name": "Wong Shaffer", + "gender": "male", + "company": "ACCUFARM", + "email": "wongshaffer@accufarm.com", + "phone": "+1 (865) 589-3833" + }, + { + "_id": "55d2fc86c4c10d58f1fe357f", + "age": 32, + "name": "Ivy Suarez", + "gender": "female", + "company": "UNCORP", + "email": "ivysuarez@uncorp.com", + "phone": "+1 (851) 582-2829" + }, + { + "_id": "55d2fc867c11d1885ca4d8f9", + "age": 25, + "name": "Sosa Barber", + "gender": "male", + "company": "FUELTON", + "email": "sosabarber@fuelton.com", + "phone": "+1 (831) 404-2343" + }, + { + "_id": "55d2fc866405b373cff4d477", + "age": 23, + "name": "Rosalind Craft", + "gender": "female", + "company": "OTHERSIDE", + "email": "rosalindcraft@otherside.com", + "phone": "+1 (847) 455-2079" + }, + { + "_id": "55d2fc863a038b06c712b4b9", + "age": 30, + "name": "Bean Mathis", + "gender": "male", + "company": "QUILK", + "email": "beanmathis@quilk.com", + "phone": "+1 (974) 596-2868" + }, + { + "_id": "55d2fc86922e4a50dfef56ac", + "age": 21, + "name": "Kelley Ruiz", + "gender": "female", + "company": "APEX", + "email": "kelleyruiz@apex.com", + "phone": "+1 (889) 522-2938" + }, + { + "_id": "55d2fc868c7bf8c05a228366", + "age": 22, + "name": "Georgette Chaney", + "gender": "female", + "company": "EXOTECHNO", + "email": "georgettechaney@exotechno.com", + "phone": "+1 (920) 591-3934" + }, + { + "_id": "55d2fc86988c5be0c8ba5412", + "age": 38, + "name": "Tami Bullock", + "gender": "female", + "company": "ISOTRONIC", + "email": "tamibullock@isotronic.com", + "phone": "+1 (828) 567-2857" + }, + { + "_id": "55d2fc862264a1d2de2e8a6b", + "age": 39, + "name": "Castillo Rosario", + "gender": "male", + "company": "KONNECT", + "email": "castillorosario@konnect.com", + "phone": "+1 (938) 402-3484" + }, + { + "_id": "55d2fc8622ee680ff3c522e1", + "age": 31, + "name": "George Weber", + "gender": "male", + "company": "FARMAGE", + "email": "georgeweber@farmage.com", + "phone": "+1 (895) 502-2654" + }, + { + "_id": "55d2fc86a0d45d2916aacb5a", + "age": 28, + "name": "Wheeler Villarreal", + "gender": "male", + "company": "IMPERIUM", + "email": "wheelervillarreal@imperium.com", + "phone": "+1 (889) 507-3796" + }, + { + "_id": "55d2fc866c6e8e85c17c61ca", + "age": 36, + "name": "Arlene Bean", + "gender": "female", + "company": "UNIA", + "email": "arlenebean@unia.com", + "phone": "+1 (970) 463-2147" + }, + { + "_id": "55d2fc86c3c95fd98562a429", + "age": 30, + "name": "Oneil Madden", + "gender": "male", + "company": "COMBOGENE", + "email": "oneilmadden@combogene.com", + "phone": "+1 (849) 507-3555" + }, + { + "_id": "55d2fc86b0f7cc31af45078a", + "age": 35, + "name": "Vaughn Merritt", + "gender": "male", + "company": "ACCUPHARM", + "email": "vaughnmerritt@accupharm.com", + "phone": "+1 (886) 428-2966" + }, + { + "_id": "55d2fc86ad825cb66f2a2feb", + "age": 21, + "name": "Duran Bradford", + "gender": "male", + "company": "SQUISH", + "email": "duranbradford@squish.com", + "phone": "+1 (930) 434-2976" + }, + { + "_id": "55d2fc86efa80c332066194d", + "age": 30, + "name": "Tanisha Knox", + "gender": "female", + "company": "FARMEX", + "email": "tanishaknox@farmex.com", + "phone": "+1 (924) 540-2066" + }, + { + "_id": "55d2fc861dbd55c1fd4bdf23", + "age": 38, + "name": "Esther Foster", + "gender": "female", + "company": "SENSATE", + "email": "estherfoster@sensate.com", + "phone": "+1 (812) 417-2687" + }, + { + "_id": "55d2fc86115b9a01067db6ad", + "age": 35, + "name": "Marion Gray", + "gender": "female", + "company": "HINWAY", + "email": "mariongray@hinway.com", + "phone": "+1 (850) 526-2167" + }, + { + "_id": "55d2fc864b40086c2e4963e2", + "age": 38, + "name": "Ava Flowers", + "gender": "female", + "company": "REVERSUS", + "email": "avaflowers@reversus.com", + "phone": "+1 (989) 415-2504" + }, + { + "_id": "55d2fc8686c59f7395222b11", + "age": 35, + "name": "Katina Burnett", + "gender": "female", + "company": "DUFLEX", + "email": "katinaburnett@duflex.com", + "phone": "+1 (843) 464-3718" + }, + { + "_id": "55d2fc86e012c977bc5b57d6", + "age": 37, + "name": "Ester Cooley", + "gender": "female", + "company": "RUBADUB", + "email": "estercooley@rubadub.com", + "phone": "+1 (856) 407-3009" + }, + { + "_id": "55d2fc865688875c75a158b5", + "age": 36, + "name": "Dennis Mccray", + "gender": "male", + "company": "PETIGEMS", + "email": "dennismccray@petigems.com", + "phone": "+1 (989) 525-3768" + }, + { + "_id": "55d2fc86ca333a1c715a35c7", + "age": 25, + "name": "Mitzi Carson", + "gender": "female", + "company": "KENEGY", + "email": "mitzicarson@kenegy.com", + "phone": "+1 (819) 450-2923" + }, + { + "_id": "55d2fc86a8eb68257312e735", + "age": 33, + "name": "Guthrie Tyson", + "gender": "male", + "company": "GLUID", + "email": "guthrietyson@gluid.com", + "phone": "+1 (878) 496-3831" + }, + { + "_id": "55d2fc865d4f3b3777fc1573", + "age": 38, + "name": "Sellers Hodges", + "gender": "male", + "company": "BALOOBA", + "email": "sellershodges@balooba.com", + "phone": "+1 (895) 557-2331" + }, + { + "_id": "55d2fc860a91cf55298e2a24", + "age": 32, + "name": "Hawkins Hardin", + "gender": "male", + "company": "ZILLANET", + "email": "hawkinshardin@zillanet.com", + "phone": "+1 (852) 511-2796" + }, + { + "_id": "55d2fc867b1c618fcb9cb2c3", + "age": 26, + "name": "Bowman Buck", + "gender": "male", + "company": "APPLIDEC", + "email": "bowmanbuck@applidec.com", + "phone": "+1 (995) 500-2863" + }, + { + "_id": "55d2fc8666610d156551484b", + "age": 23, + "name": "Mcgee Delgado", + "gender": "male", + "company": "MANTRO", + "email": "mcgeedelgado@mantro.com", + "phone": "+1 (917) 490-2295" + }, + { + "_id": "55d2fc8685385c63f5a509b3", + "age": 24, + "name": "Petty Pena", + "gender": "male", + "company": "EXOSPEED", + "email": "pettypena@exospeed.com", + "phone": "+1 (929) 470-2022" + }, + { + "_id": "55d2fc864296df53bb778e52", + "age": 38, + "name": "Ray Mclaughlin", + "gender": "male", + "company": "PYRAMAX", + "email": "raymclaughlin@pyramax.com", + "phone": "+1 (935) 453-3720" + }, + { + "_id": "55d2fc86b157acc34692412b", + "age": 22, + "name": "Hopkins Wells", + "gender": "male", + "company": "NORALEX", + "email": "hopkinswells@noralex.com", + "phone": "+1 (986) 421-2293" + }, + { + "_id": "55d2fc861febd65bb3c91219", + "age": 38, + "name": "Patsy Strickland", + "gender": "female", + "company": "POLARIA", + "email": "patsystrickland@polaria.com", + "phone": "+1 (885) 408-2213" + }, + { + "_id": "55d2fc8693fbc24aa2bcc5a8", + "age": 31, + "name": "Wolf Delaney", + "gender": "male", + "company": "EXERTA", + "email": "wolfdelaney@exerta.com", + "phone": "+1 (969) 537-3201" + }, + { + "_id": "55d2fc86b923e0543d39fed4", + "age": 30, + "name": "Fulton Hewitt", + "gender": "male", + "company": "TWIGGERY", + "email": "fultonhewitt@twiggery.com", + "phone": "+1 (894) 483-2549" + }, + { + "_id": "55d2fc8672aff3d2369b2749", + "age": 40, + "name": "Nona Meadows", + "gender": "female", + "company": "ULTRIMAX", + "email": "nonameadows@ultrimax.com", + "phone": "+1 (997) 459-2012" + }, + { + "_id": "55d2fc86a3b6922e61cdcd72", + "age": 24, + "name": "Irwin Russo", + "gender": "male", + "company": "QUINTITY", + "email": "irwinrusso@quintity.com", + "phone": "+1 (985) 597-3841" + }, + { + "_id": "55d2fc86c28f4a90a41581b7", + "age": 34, + "name": "Mara Bowman", + "gender": "female", + "company": "ATOMICA", + "email": "marabowman@atomica.com", + "phone": "+1 (927) 578-2958" + }, + { + "_id": "55d2fc86ef827e1bbb5b3ceb", + "age": 40, + "name": "Leigh Schroeder", + "gender": "female", + "company": "ZIORE", + "email": "leighschroeder@ziore.com", + "phone": "+1 (963) 484-2519" + }, + { + "_id": "55d2fc86921329e8e044472a", + "age": 27, + "name": "Sweeney Riddle", + "gender": "male", + "company": "ELITA", + "email": "sweeneyriddle@elita.com", + "phone": "+1 (974) 536-2132" + }, + { + "_id": "55d2fc864b6067f7d828f1ba", + "age": 23, + "name": "Bell Kline", + "gender": "male", + "company": "ORBOID", + "email": "bellkline@orboid.com", + "phone": "+1 (827) 461-3466" + }, + { + "_id": "55d2fc86a541995fa67027ae", + "age": 20, + "name": "Morgan Aguirre", + "gender": "female", + "company": "AEORA", + "email": "morganaguirre@aeora.com", + "phone": "+1 (987) 494-2357" + }, + { + "_id": "55d2fc86af8d98e486bda0f5", + "age": 24, + "name": "Morrison Mcbride", + "gender": "male", + "company": "TECHMANIA", + "email": "morrisonmcbride@techmania.com", + "phone": "+1 (994) 470-2394" + }, + { + "_id": "55d2fc86371a2691da434cdd", + "age": 22, + "name": "Miles Salinas", + "gender": "male", + "company": "RODEOLOGY", + "email": "milessalinas@rodeology.com", + "phone": "+1 (898) 461-3008" + }, + { + "_id": "55d2fc865196aa926884d957", + "age": 36, + "name": "Lang Riggs", + "gender": "male", + "company": "PHOTOBIN", + "email": "langriggs@photobin.com", + "phone": "+1 (849) 503-2335" + }, + { + "_id": "55d2fc86b4e30cd686840e28", + "age": 30, + "name": "Kathy Phelps", + "gender": "female", + "company": "INTRAWEAR", + "email": "kathyphelps@intrawear.com", + "phone": "+1 (992) 499-2474" + }, + { + "_id": "55d2fc86f09accb089f91415", + "age": 24, + "name": "Moss Jimenez", + "gender": "male", + "company": "PROFLEX", + "email": "mossjimenez@proflex.com", + "phone": "+1 (842) 546-3491" + }, + { + "_id": "55d2fc86e3ba881a25584928", + "age": 20, + "name": "Moody Sexton", + "gender": "male", + "company": "CENTREGY", + "email": "moodysexton@centregy.com", + "phone": "+1 (856) 581-3293" + }, + { + "_id": "55d2fc861c85cf26c6d21a64", + "age": 31, + "name": "Nannie Price", + "gender": "female", + "company": "GEOSTELE", + "email": "nannieprice@geostele.com", + "phone": "+1 (936) 447-3486" + }, + { + "_id": "55d2fc86f1e23452254fda91", + "age": 32, + "name": "Summer Johnston", + "gender": "female", + "company": "EXTRAGENE", + "email": "summerjohnston@extragene.com", + "phone": "+1 (808) 508-2748" + }, + { + "_id": "55d2fc86940fcc0f17d5f213", + "age": 24, + "name": "Genevieve Lynch", + "gender": "female", + "company": "COREPAN", + "email": "genevievelynch@corepan.com", + "phone": "+1 (921) 532-2893" + }, + { + "_id": "55d2fc86cdf6056cd058466c", + "age": 23, + "name": "Stuart Oconnor", + "gender": "male", + "company": "PUSHCART", + "email": "stuartoconnor@pushcart.com", + "phone": "+1 (925) 515-3434" + }, + { + "_id": "55d2fc863bca896e3e2ac1a7", + "age": 21, + "name": "Adela Nieves", + "gender": "female", + "company": "MEDICROIX", + "email": "adelanieves@medicroix.com", + "phone": "+1 (910) 568-3916" + }, + { + "_id": "55d2fc86826d7c7fcfc2562e", + "age": 28, + "name": "Eaton Mcintosh", + "gender": "male", + "company": "MEGALL", + "email": "eatonmcintosh@megall.com", + "phone": "+1 (806) 440-2196" + }, + { + "_id": "55d2fc863d76c0458de8afb2", + "age": 35, + "name": "Sharron Hood", + "gender": "female", + "company": "UTARIAN", + "email": "sharronhood@utarian.com", + "phone": "+1 (824) 477-3364" + }, + { + "_id": "55d2fc86c0f317fe0cdd6f66", + "age": 24, + "name": "Allison Osborn", + "gender": "female", + "company": "TUBALUM", + "email": "allisonosborn@tubalum.com", + "phone": "+1 (913) 546-3966" + }, + { + "_id": "55d2fc86e1521a5a9896e0fa", + "age": 38, + "name": "Chelsea Jarvis", + "gender": "female", + "company": "SOPRANO", + "email": "chelseajarvis@soprano.com", + "phone": "+1 (834) 417-2904" + }, + { + "_id": "55d2fc86a5f4df72a31c9201", + "age": 22, + "name": "Finley Freeman", + "gender": "male", + "company": "COMBOT", + "email": "finleyfreeman@combot.com", + "phone": "+1 (886) 448-2820" + }, + { + "_id": "55d2fc86c4c2c783570c50eb", + "age": 31, + "name": "Lynn Miles", + "gender": "female", + "company": "ZENTIX", + "email": "lynnmiles@zentix.com", + "phone": "+1 (891) 450-2505" + }, + { + "_id": "55d2fc863e649b4a99367f40", + "age": 39, + "name": "Montoya Greene", + "gender": "male", + "company": "MEMORA", + "email": "montoyagreene@memora.com", + "phone": "+1 (809) 402-3541" + }, + { + "_id": "55d2fc86b82e96b2275df9f3", + "age": 28, + "name": "Castro Huff", + "gender": "male", + "company": "BLUPLANET", + "email": "castrohuff@bluplanet.com", + "phone": "+1 (966) 554-2469" + }, + { + "_id": "55d2fc86f4a8a7700a99e31c", + "age": 23, + "name": "Guadalupe Harmon", + "gender": "female", + "company": "ISOLOGIA", + "email": "guadalupeharmon@isologia.com", + "phone": "+1 (937) 497-2022" + }, + { + "_id": "55d2fc86551e7044f31f2520", + "age": 25, + "name": "Heidi Navarro", + "gender": "female", + "company": "POLARIUM", + "email": "heidinavarro@polarium.com", + "phone": "+1 (830) 493-3328" + }, + { + "_id": "55d2fc861ee43e4303351a4b", + "age": 25, + "name": "Fry Webster", + "gender": "male", + "company": "RENOVIZE", + "email": "frywebster@renovize.com", + "phone": "+1 (960) 600-3488" + }, + { + "_id": "55d2fc86e0d6778a1c6d7195", + "age": 35, + "name": "Candice Sharpe", + "gender": "female", + "company": "UNDERTAP", + "email": "candicesharpe@undertap.com", + "phone": "+1 (989) 436-2856" + }, + { + "_id": "55d2fc86267005541d225276", + "age": 25, + "name": "Vonda Hansen", + "gender": "female", + "company": "PROVIDCO", + "email": "vondahansen@providco.com", + "phone": "+1 (883) 484-2047" + }, + { + "_id": "55d2fc8605000bc9329d28e0", + "age": 21, + "name": "Lara Dominguez", + "gender": "male", + "company": "VIXO", + "email": "laradominguez@vixo.com", + "phone": "+1 (998) 440-3632" + }, + { + "_id": "55d2fc868b364833d935b192", + "age": 36, + "name": "Jillian Gibbs", + "gender": "female", + "company": "CUIZINE", + "email": "jilliangibbs@cuizine.com", + "phone": "+1 (996) 447-3083" + }, + { + "_id": "55d2fc863690a0784d2e8bc1", + "age": 35, + "name": "Frankie Cervantes", + "gender": "female", + "company": "ISODRIVE", + "email": "frankiecervantes@isodrive.com", + "phone": "+1 (895) 533-2371" + }, + { + "_id": "55d2fc86efb91d645101592f", + "age": 36, + "name": "Nieves Walton", + "gender": "male", + "company": "ZILPHUR", + "email": "nieveswalton@zilphur.com", + "phone": "+1 (866) 412-2377" + }, + { + "_id": "55d2fc86cba2fb0dfd9eb8c3", + "age": 38, + "name": "Celina Orr", + "gender": "female", + "company": "COMVEX", + "email": "celinaorr@comvex.com", + "phone": "+1 (960) 433-2380" + }, + { + "_id": "55d2fc8650eaf118ae048bce", + "age": 33, + "name": "Moreno Conway", + "gender": "male", + "company": "VIOCULAR", + "email": "morenoconway@viocular.com", + "phone": "+1 (808) 535-2624" + }, + { + "_id": "55d2fc860e41c77df0ea1151", + "age": 33, + "name": "Wilkerson Dodson", + "gender": "male", + "company": "COMTRAK", + "email": "wilkersondodson@comtrak.com", + "phone": "+1 (932) 427-2400" + }, + { + "_id": "55d2fc86175167613833c577", + "age": 21, + "name": "Crane Lloyd", + "gender": "male", + "company": "ARCHITAX", + "email": "cranelloyd@architax.com", + "phone": "+1 (984) 467-3498" + }, + { + "_id": "55d2fc863aa593013e09d04b", + "age": 25, + "name": "Marguerite Dorsey", + "gender": "female", + "company": "UNI", + "email": "margueritedorsey@uni.com", + "phone": "+1 (989) 558-2105" + }, + { + "_id": "55d2fc86248902726f0d1c53", + "age": 35, + "name": "Dillon William", + "gender": "male", + "company": "ROOFORIA", + "email": "dillonwilliam@rooforia.com", + "phone": "+1 (879) 600-3589" + }, + { + "_id": "55d2fc86caa1b7374cd83a72", + "age": 32, + "name": "Small Floyd", + "gender": "male", + "company": "ANACHO", + "email": "smallfloyd@anacho.com", + "phone": "+1 (906) 463-2357" + }, + { + "_id": "55d2fc86c086267c74616688", + "age": 32, + "name": "Dominique Horn", + "gender": "female", + "company": "ENTOGROK", + "email": "dominiquehorn@entogrok.com", + "phone": "+1 (871) 556-2943" + }, + { + "_id": "55d2fc86d3f7731c49d48fa7", + "age": 35, + "name": "Haley Shannon", + "gender": "female", + "company": "UNEEQ", + "email": "haleyshannon@uneeq.com", + "phone": "+1 (984) 471-3688" + }, + { + "_id": "55d2fc8678e81807871b2b13", + "age": 24, + "name": "Flowers Richards", + "gender": "male", + "company": "QUILITY", + "email": "flowersrichards@quility.com", + "phone": "+1 (956) 548-3667" + }, + { + "_id": "55d2fc86b4b462a2743c10d3", + "age": 33, + "name": "Hopper Rush", + "gender": "male", + "company": "VERBUS", + "email": "hopperrush@verbus.com", + "phone": "+1 (866) 578-2948" + }, + { + "_id": "55d2fc86d660e6f5bf9c1ce1", + "age": 31, + "name": "Randall Kelley", + "gender": "male", + "company": "INSURON", + "email": "randallkelley@insuron.com", + "phone": "+1 (922) 520-3464" + }, + { + "_id": "55d2fc86e70ad03e58c3e01f", + "age": 36, + "name": "Owens Drake", + "gender": "male", + "company": "FROLIX", + "email": "owensdrake@frolix.com", + "phone": "+1 (982) 516-3575" + }, + { + "_id": "55d2fc86ef78c8ad566afe4f", + "age": 28, + "name": "Irma Garrison", + "gender": "female", + "company": "EXOZENT", + "email": "irmagarrison@exozent.com", + "phone": "+1 (882) 536-2614" + }, + { + "_id": "55d2fc8675d6b4b33c5e482f", + "age": 34, + "name": "Baldwin Carver", + "gender": "male", + "company": "PHEAST", + "email": "baldwincarver@pheast.com", + "phone": "+1 (824) 521-2892" + }, + { + "_id": "55d2fc86d382b214a715305f", + "age": 23, + "name": "Short Harding", + "gender": "male", + "company": "LIQUICOM", + "email": "shortharding@liquicom.com", + "phone": "+1 (836) 578-2063" + }, + { + "_id": "55d2fc860420ee3ca95e2166", + "age": 35, + "name": "Christy Roberson", + "gender": "female", + "company": "BITREX", + "email": "christyroberson@bitrex.com", + "phone": "+1 (840) 426-2954" + }, + { + "_id": "55d2fc86dd67ce4e4f7b6d0c", + "age": 21, + "name": "Fern Knight", + "gender": "female", + "company": "ENTROPIX", + "email": "fernknight@entropix.com", + "phone": "+1 (944) 546-2456" + }, + { + "_id": "55d2fc8651510fb366709f12", + "age": 40, + "name": "Elva Taylor", + "gender": "female", + "company": "COMFIRM", + "email": "elvataylor@comfirm.com", + "phone": "+1 (834) 468-2999" + }, + { + "_id": "55d2fc869603515857919a7c", + "age": 26, + "name": "Carmen Bentley", + "gender": "female", + "company": "LIMAGE", + "email": "carmenbentley@limage.com", + "phone": "+1 (882) 425-3588" + }, + { + "_id": "55d2fc860991d6904808f8b3", + "age": 28, + "name": "Blair Dunn", + "gender": "male", + "company": "ESSENSIA", + "email": "blairdunn@essensia.com", + "phone": "+1 (896) 477-2617" + }, + { + "_id": "55d2fc862c99e4fefd17ab8d", + "age": 23, + "name": "Minerva Swanson", + "gender": "female", + "company": "EMTRAC", + "email": "minervaswanson@emtrac.com", + "phone": "+1 (905) 427-3942" + }, + { + "_id": "55d2fc86a2b4277d171e6ed6", + "age": 40, + "name": "Alicia Lawrence", + "gender": "female", + "company": "CONCILITY", + "email": "alicialawrence@concility.com", + "phone": "+1 (903) 452-2010" + }, + { + "_id": "55d2fc86f98eb74fe02a49f5", + "age": 33, + "name": "Bernice Fitzpatrick", + "gender": "female", + "company": "JASPER", + "email": "bernicefitzpatrick@jasper.com", + "phone": "+1 (987) 518-2248" + }, + { + "_id": "55d2fc86070af0b0109b6a4c", + "age": 20, + "name": "Wilder Blackburn", + "gender": "male", + "company": "ZENTILITY", + "email": "wilderblackburn@zentility.com", + "phone": "+1 (921) 548-2995" + }, + { + "_id": "55d2fc86249796ecd0b25829", + "age": 25, + "name": "Lambert Wilson", + "gender": "male", + "company": "ANIVET", + "email": "lambertwilson@anivet.com", + "phone": "+1 (920) 592-2261" + }, + { + "_id": "55d2fc863c6bdb691f62bdf4", + "age": 24, + "name": "Cook Lee", + "gender": "male", + "company": "ZILLADYNE", + "email": "cooklee@zilladyne.com", + "phone": "+1 (812) 566-2988" + }, + { + "_id": "55d2fc86cdeae66cf4fe6582", + "age": 29, + "name": "Karyn Horne", + "gender": "female", + "company": "OBLIQ", + "email": "karynhorne@obliq.com", + "phone": "+1 (909) 452-3599" + }, + { + "_id": "55d2fc86bcbabc9eb159e1e4", + "age": 23, + "name": "Rowe Stokes", + "gender": "male", + "company": "ANDERSHUN", + "email": "rowestokes@andershun.com", + "phone": "+1 (948) 531-2335" + }, + { + "_id": "55d2fc86a1b344b621d41baa", + "age": 27, + "name": "Gonzalez Merrill", + "gender": "male", + "company": "PLAYCE", + "email": "gonzalezmerrill@playce.com", + "phone": "+1 (989) 418-3057" + }, + { + "_id": "55d2fc86300074c184103b47", + "age": 33, + "name": "Kathie Preston", + "gender": "female", + "company": "LUMBREX", + "email": "kathiepreston@lumbrex.com", + "phone": "+1 (911) 586-3177" + }, + { + "_id": "55d2fc8646059e576c41ed1f", + "age": 26, + "name": "Antoinette Stevens", + "gender": "female", + "company": "APEXIA", + "email": "antoinettestevens@apexia.com", + "phone": "+1 (828) 597-3083" + }, + { + "_id": "55d2fc86b5ec5743baacc091", + "age": 22, + "name": "Schmidt Morton", + "gender": "male", + "company": "NORSUL", + "email": "schmidtmorton@norsul.com", + "phone": "+1 (821) 530-2454" + }, + { + "_id": "55d2fc86a521743243b01d2b", + "age": 37, + "name": "Joyner Wise", + "gender": "male", + "company": "MANGELICA", + "email": "joynerwise@mangelica.com", + "phone": "+1 (884) 448-3942" + }, + { + "_id": "55d2fc86d7c827b5ded4027f", + "age": 22, + "name": "Carpenter Carey", + "gender": "male", + "company": "CENTICE", + "email": "carpentercarey@centice.com", + "phone": "+1 (823) 585-2581" + }, + { + "_id": "55d2fc866866bf441f74abdf", + "age": 34, + "name": "Luz Hays", + "gender": "female", + "company": "KONGENE", + "email": "luzhays@kongene.com", + "phone": "+1 (999) 558-2282" + }, + { + "_id": "55d2fc86af95d619737eccb7", + "age": 27, + "name": "Lesley Frye", + "gender": "female", + "company": "SUREMAX", + "email": "lesleyfrye@suremax.com", + "phone": "+1 (982) 485-2811" + }, + { + "_id": "55d2fc86b41a0594552e4839", + "age": 31, + "name": "Jacqueline Ramsey", + "gender": "female", + "company": "LYRIA", + "email": "jacquelineramsey@lyria.com", + "phone": "+1 (961) 581-2500" + }, + { + "_id": "55d2fc8682aa54548863d4b1", + "age": 31, + "name": "Ina Ford", + "gender": "female", + "company": "ENERSOL", + "email": "inaford@enersol.com", + "phone": "+1 (895) 514-3441" + }, + { + "_id": "55d2fc866bc796d86ebd697b", + "age": 22, + "name": "Francisca Ashley", + "gender": "female", + "company": "SIGNIDYNE", + "email": "franciscaashley@signidyne.com", + "phone": "+1 (911) 566-2135" + }, + { + "_id": "55d2fc868ccc122c6a517033", + "age": 34, + "name": "Morton Owen", + "gender": "male", + "company": "SHOPABOUT", + "email": "mortonowen@shopabout.com", + "phone": "+1 (932) 576-3821" + }, + { + "_id": "55d2fc86751e661b56e6e3cd", + "age": 36, + "name": "Weber Manning", + "gender": "male", + "company": "CENTREE", + "email": "webermanning@centree.com", + "phone": "+1 (865) 582-3809" + }, + { + "_id": "55d2fc8620b9766f76797a13", + "age": 37, + "name": "Edwards Steele", + "gender": "male", + "company": "KATAKANA", + "email": "edwardssteele@katakana.com", + "phone": "+1 (959) 402-3657" + }, + { + "_id": "55d2fc860a13152e82b74839", + "age": 38, + "name": "Gabriela Boone", + "gender": "female", + "company": "OVOLO", + "email": "gabrielaboone@ovolo.com", + "phone": "+1 (910) 587-2744" + }, + { + "_id": "55d2fc864e9ca8a988600768", + "age": 32, + "name": "Tricia Guy", + "gender": "female", + "company": "TALKOLA", + "email": "triciaguy@talkola.com", + "phone": "+1 (960) 474-3508" + }, + { + "_id": "55d2fc86ce61b77671fd1f33", + "age": 32, + "name": "James Romero", + "gender": "female", + "company": "CANDECOR", + "email": "jamesromero@candecor.com", + "phone": "+1 (928) 478-3272" + }, + { + "_id": "55d2fc86f3cc105e20a44aa9", + "age": 25, + "name": "Casey Hammond", + "gender": "male", + "company": "PYRAMIA", + "email": "caseyhammond@pyramia.com", + "phone": "+1 (965) 539-2923" + }, + { + "_id": "55d2fc86429d859d9e54585f", + "age": 33, + "name": "Moran Wade", + "gender": "male", + "company": "BUGSALL", + "email": "moranwade@bugsall.com", + "phone": "+1 (996) 559-2965" + }, + { + "_id": "55d2fc86820b562eff651ebc", + "age": 34, + "name": "Earline Goff", + "gender": "female", + "company": "DATAGENE", + "email": "earlinegoff@datagene.com", + "phone": "+1 (976) 565-3513" + }, + { + "_id": "55d2fc865f6176e8301edcf3", + "age": 38, + "name": "Vickie Cherry", + "gender": "female", + "company": "SILODYNE", + "email": "vickiecherry@silodyne.com", + "phone": "+1 (943) 522-2438" + }, + { + "_id": "55d2fc8633cb19840995984c", + "age": 30, + "name": "Yates Avery", + "gender": "male", + "company": "DIGINETIC", + "email": "yatesavery@diginetic.com", + "phone": "+1 (813) 587-3611" + }, + { + "_id": "55d2fc8677d1a1c749d498eb", + "age": 24, + "name": "Hodges Langley", + "gender": "male", + "company": "QUOTEZART", + "email": "hodgeslangley@quotezart.com", + "phone": "+1 (857) 496-3905" + }, + { + "_id": "55d2fc86f49e5ceca6ff241d", + "age": 35, + "name": "Serrano Bartlett", + "gender": "male", + "company": "EXOSWITCH", + "email": "serranobartlett@exoswitch.com", + "phone": "+1 (809) 421-3677" + }, + { + "_id": "55d2fc86cd4921dcf0316691", + "age": 28, + "name": "Faye Wood", + "gender": "female", + "company": "EXOTERIC", + "email": "fayewood@exoteric.com", + "phone": "+1 (840) 417-2329" + }, + { + "_id": "55d2fc8697911f7ca9adfe37", + "age": 38, + "name": "Jamie Jennings", + "gender": "female", + "company": "SURETECH", + "email": "jamiejennings@suretech.com", + "phone": "+1 (945) 599-3768" + }, + { + "_id": "55d2fc8614e0225c06b40348", + "age": 34, + "name": "Levy Sellers", + "gender": "male", + "company": "STELAECOR", + "email": "levysellers@stelaecor.com", + "phone": "+1 (805) 519-2578" + }, + { + "_id": "55d2fc8635986d3994af8adc", + "age": 36, + "name": "Kelsey Montgomery", + "gender": "female", + "company": "IMANT", + "email": "kelseymontgomery@imant.com", + "phone": "+1 (894) 555-3420" + }, + { + "_id": "55d2fc8624dfa620d7b8a991", + "age": 33, + "name": "Rush Gates", + "gender": "male", + "company": "DIGIAL", + "email": "rushgates@digial.com", + "phone": "+1 (891) 477-2651" + }, + { + "_id": "55d2fc862d52fe806312e6dd", + "age": 40, + "name": "Holloway Gay", + "gender": "male", + "company": "EARTHMARK", + "email": "hollowaygay@earthmark.com", + "phone": "+1 (811) 540-3123" + }, + { + "_id": "55d2fc86a168313cea778ac0", + "age": 20, + "name": "Cotton Jackson", + "gender": "male", + "company": "ZOLARITY", + "email": "cottonjackson@zolarity.com", + "phone": "+1 (980) 445-3468" + }, + { + "_id": "55d2fc86577d2794043213ea", + "age": 38, + "name": "Lakeisha Blanchard", + "gender": "female", + "company": "SPEEDBOLT", + "email": "lakeishablanchard@speedbolt.com", + "phone": "+1 (839) 406-2400" + }, + { + "_id": "55d2fc8609e2760d5cc298f1", + "age": 39, + "name": "Alford Church", + "gender": "male", + "company": "GADTRON", + "email": "alfordchurch@gadtron.com", + "phone": "+1 (869) 400-2097" + }, + { + "_id": "55d2fc867e4a904c96668e08", + "age": 30, + "name": "Collins Carlson", + "gender": "male", + "company": "EVEREST", + "email": "collinscarlson@everest.com", + "phone": "+1 (944) 580-3905" + }, + { + "_id": "55d2fc86ddb3ea916627267a", + "age": 33, + "name": "Cain Hester", + "gender": "male", + "company": "IMMUNICS", + "email": "cainhester@immunics.com", + "phone": "+1 (899) 586-2934" + }, + { + "_id": "55d2fc86ffaa0a8952d1a400", + "age": 23, + "name": "Mia Baird", + "gender": "female", + "company": "NIPAZ", + "email": "miabaird@nipaz.com", + "phone": "+1 (940) 569-2850" + }, + { + "_id": "55d2fc865f259364ad5b4fff", + "age": 25, + "name": "Ramona Ewing", + "gender": "female", + "company": "COMTEST", + "email": "ramonaewing@comtest.com", + "phone": "+1 (879) 546-2754" + }, + { + "_id": "55d2fc862d6af75d6794bef5", + "age": 34, + "name": "Delacruz Goodwin", + "gender": "male", + "company": "SLOGANAUT", + "email": "delacruzgoodwin@sloganaut.com", + "phone": "+1 (928) 477-2688" + }, + { + "_id": "55d2fc86e34b19d6fd2ae261", + "age": 38, + "name": "Mcleod Moody", + "gender": "male", + "company": "ECRAZE", + "email": "mcleodmoody@ecraze.com", + "phone": "+1 (989) 598-3350" + }, + { + "_id": "55d2fc8608e305ebd55e0bac", + "age": 34, + "name": "Maddox Calhoun", + "gender": "male", + "company": "TELEPARK", + "email": "maddoxcalhoun@telepark.com", + "phone": "+1 (815) 593-3540" + }, + { + "_id": "55d2fc869e175ca83d7d6597", + "age": 36, + "name": "Cora Dale", + "gender": "female", + "company": "ZILLACTIC", + "email": "coradale@zillactic.com", + "phone": "+1 (866) 545-3632" + }, + { + "_id": "55d2fc861968b039322cb743", + "age": 27, + "name": "Knapp Miranda", + "gender": "male", + "company": "TOYLETRY", + "email": "knappmiranda@toyletry.com", + "phone": "+1 (835) 591-3111" + }, + { + "_id": "55d2fc86423f7b5a304d2175", + "age": 32, + "name": "Ida Petersen", + "gender": "female", + "company": "BILLMED", + "email": "idapetersen@billmed.com", + "phone": "+1 (898) 492-2148" + }, + { + "_id": "55d2fc862cfd92eb67375bba", + "age": 37, + "name": "Concepcion Wilcox", + "gender": "female", + "company": "MAXEMIA", + "email": "concepcionwilcox@maxemia.com", + "phone": "+1 (812) 516-2631" + }, + { + "_id": "55d2fc8695ffe246079f8f0c", + "age": 40, + "name": "Corine Daniel", + "gender": "female", + "company": "MEDCOM", + "email": "corinedaniel@medcom.com", + "phone": "+1 (991) 483-2257" + }, + { + "_id": "55d2fc861f1ff641b3aa7ee5", + "age": 31, + "name": "Latasha Byers", + "gender": "female", + "company": "RUGSTARS", + "email": "latashabyers@rugstars.com", + "phone": "+1 (817) 542-3231" + }, + { + "_id": "55d2fc86724fbfd025371582", + "age": 31, + "name": "Gayle Barrett", + "gender": "female", + "company": "PARAGONIA", + "email": "gaylebarrett@paragonia.com", + "phone": "+1 (870) 547-2454" + }, + { + "_id": "55d2fc869add6e70699650fa", + "age": 40, + "name": "Angelina Tyler", + "gender": "female", + "company": "VIRVA", + "email": "angelinatyler@virva.com", + "phone": "+1 (960) 425-3784" + }, + { + "_id": "55d2fc864c12d18424ac35be", + "age": 27, + "name": "Ratliff Franks", + "gender": "male", + "company": "CEMENTION", + "email": "ratlifffranks@cemention.com", + "phone": "+1 (958) 424-2396" + }, + { + "_id": "55d2fc86bad0be82f6e2f83b", + "age": 40, + "name": "Landry Zimmerman", + "gender": "male", + "company": "JETSILK", + "email": "landryzimmerman@jetsilk.com", + "phone": "+1 (947) 573-2755" + }, + { + "_id": "55d2fc86b56ff40ff0ed70e3", + "age": 23, + "name": "Greta West", + "gender": "female", + "company": "UBERLUX", + "email": "gretawest@uberlux.com", + "phone": "+1 (995) 542-3886" + }, + { + "_id": "55d2fc8667dfd03049bf08eb", + "age": 30, + "name": "Camacho Nelson", + "gender": "male", + "company": "KYAGORO", + "email": "camachonelson@kyagoro.com", + "phone": "+1 (881) 500-3970" + }, + { + "_id": "55d2fc865458ecf3d2995a63", + "age": 36, + "name": "June Turner", + "gender": "female", + "company": "RECOGNIA", + "email": "juneturner@recognia.com", + "phone": "+1 (976) 466-2777" + }, + { + "_id": "55d2fc8682f4304f0889c829", + "age": 31, + "name": "Mckinney Stark", + "gender": "male", + "company": "OPTICOM", + "email": "mckinneystark@opticom.com", + "phone": "+1 (951) 500-3946" + }, + { + "_id": "55d2fc86ff8211995849d831", + "age": 20, + "name": "Hammond Fletcher", + "gender": "male", + "company": "ERSUM", + "email": "hammondfletcher@ersum.com", + "phone": "+1 (974) 541-3273" + }, + { + "_id": "55d2fc86a9792a08912bdb8e", + "age": 23, + "name": "White Fischer", + "gender": "male", + "company": "REALMO", + "email": "whitefischer@realmo.com", + "phone": "+1 (963) 533-2428" + }, + { + "_id": "55d2fc86c5ba8287455030be", + "age": 36, + "name": "Kimberly Mcguire", + "gender": "female", + "company": "TETAK", + "email": "kimberlymcguire@tetak.com", + "phone": "+1 (846) 410-3414" + }, + { + "_id": "55d2fc862a665bca942115f3", + "age": 35, + "name": "Sara Hurst", + "gender": "female", + "company": "FORTEAN", + "email": "sarahurst@fortean.com", + "phone": "+1 (857) 530-3627" + }, + { + "_id": "55d2fc86b72bee240f10055e", + "age": 29, + "name": "Chandler English", + "gender": "male", + "company": "PAPRICUT", + "email": "chandlerenglish@papricut.com", + "phone": "+1 (978) 582-2348" + }, + { + "_id": "55d2fc861c2bccedeac29892", + "age": 29, + "name": "Waters Riley", + "gender": "male", + "company": "ECSTASIA", + "email": "watersriley@ecstasia.com", + "phone": "+1 (842) 579-3426" + }, + { + "_id": "55d2fc86526f36994de2038a", + "age": 34, + "name": "Wood Gomez", + "gender": "male", + "company": "ASSITIA", + "email": "woodgomez@assitia.com", + "phone": "+1 (954) 565-2413" + }, + { + "_id": "55d2fc86bec8fe1e60977c9c", + "age": 31, + "name": "Fields Decker", + "gender": "male", + "company": "EMERGENT", + "email": "fieldsdecker@emergent.com", + "phone": "+1 (992) 489-3712" + }, + { + "_id": "55d2fc868b903951ddd71703", + "age": 28, + "name": "Barry Woods", + "gender": "male", + "company": "OZEAN", + "email": "barrywoods@ozean.com", + "phone": "+1 (885) 433-3285" + }, + { + "_id": "55d2fc86ca3984177237f17e", + "age": 29, + "name": "Whitfield Higgins", + "gender": "male", + "company": "PEARLESEX", + "email": "whitfieldhiggins@pearlesex.com", + "phone": "+1 (869) 468-3186" + }, + { + "_id": "55d2fc8678dd83bf6cc16648", + "age": 27, + "name": "Haynes Mills", + "gender": "male", + "company": "ZOARERE", + "email": "haynesmills@zoarere.com", + "phone": "+1 (886) 576-3206" + }, + { + "_id": "55d2fc86b6ba36d7927b8765", + "age": 27, + "name": "Kellie Hurley", + "gender": "female", + "company": "GYNKO", + "email": "kelliehurley@gynko.com", + "phone": "+1 (844) 548-2894" + }, + { + "_id": "55d2fc869adb64b23212bdfc", + "age": 30, + "name": "Brandi Shields", + "gender": "female", + "company": "KENGEN", + "email": "brandishields@kengen.com", + "phone": "+1 (947) 447-3081" + }, + { + "_id": "55d2fc8617e038558bbd0e5f", + "age": 22, + "name": "Malinda Gordon", + "gender": "female", + "company": "QUILCH", + "email": "malindagordon@quilch.com", + "phone": "+1 (945) 466-2414" + }, + { + "_id": "55d2fc8614756992c50aabfd", + "age": 30, + "name": "Wooten Mcknight", + "gender": "male", + "company": "APPLIDECK", + "email": "wootenmcknight@applideck.com", + "phone": "+1 (994) 416-2156" + }, + { + "_id": "55d2fc86b44fbdb8c8ea3a67", + "age": 36, + "name": "Mona Thomas", + "gender": "female", + "company": "KROG", + "email": "monathomas@krog.com", + "phone": "+1 (924) 423-3381" + }, + { + "_id": "55d2fc86ded545b6b4f5e536", + "age": 22, + "name": "Bates Cole", + "gender": "male", + "company": "DIGIRANG", + "email": "batescole@digirang.com", + "phone": "+1 (956) 409-2471" + }, + { + "_id": "55d2fc864abd3a5951c8e07c", + "age": 33, + "name": "Shirley Potts", + "gender": "female", + "company": "OMATOM", + "email": "shirleypotts@omatom.com", + "phone": "+1 (804) 496-2921" + }, + { + "_id": "55d2fc864e3992c902987b9c", + "age": 22, + "name": "Adrian Branch", + "gender": "female", + "company": "MULTIFLEX", + "email": "adrianbranch@multiflex.com", + "phone": "+1 (817) 499-3955" + }, + { + "_id": "55d2fc8679037ccc9d0d84d0", + "age": 25, + "name": "Deanne Rosa", + "gender": "female", + "company": "QUONATA", + "email": "deannerosa@quonata.com", + "phone": "+1 (896) 463-2190" + }, + { + "_id": "55d2fc869ec47f3f9745bcbc", + "age": 28, + "name": "Sherrie Bowers", + "gender": "female", + "company": "GOLISTIC", + "email": "sherriebowers@golistic.com", + "phone": "+1 (854) 539-3836" + }, + { + "_id": "55d2fc86c953130389da55f9", + "age": 21, + "name": "Sharp Douglas", + "gender": "male", + "company": "CHILLIUM", + "email": "sharpdouglas@chillium.com", + "phone": "+1 (999) 513-3550" + }, + { + "_id": "55d2fc86d42d710fef2a7781", + "age": 22, + "name": "Sandy Dillard", + "gender": "female", + "company": "PHARMACON", + "email": "sandydillard@pharmacon.com", + "phone": "+1 (844) 433-2832" + }, + { + "_id": "55d2fc86aa4130e6998a333b", + "age": 20, + "name": "Naomi Willis", + "gender": "female", + "company": "SAVVY", + "email": "naomiwillis@savvy.com", + "phone": "+1 (826) 499-3221" + }, + { + "_id": "55d2fc869c2c97145040281b", + "age": 38, + "name": "Rivera Stone", + "gender": "male", + "company": "ORBIXTAR", + "email": "riverastone@orbixtar.com", + "phone": "+1 (994) 439-3810" + }, + { + "_id": "55d2fc86e7165c13cd5905c1", + "age": 22, + "name": "Oliver Day", + "gender": "male", + "company": "PORTALIS", + "email": "oliverday@portalis.com", + "phone": "+1 (844) 464-2363" + }, + { + "_id": "55d2fc86b6b619b4d04c7640", + "age": 39, + "name": "Rachael Owens", + "gender": "female", + "company": "NURALI", + "email": "rachaelowens@nurali.com", + "phone": "+1 (856) 418-3617" + }, + { + "_id": "55d2fc865f2612144e6f27e6", + "age": 30, + "name": "Winifred Molina", + "gender": "female", + "company": "NITRACYR", + "email": "winifredmolina@nitracyr.com", + "phone": "+1 (881) 417-3559" + }, + { + "_id": "55d2fc86c38ab2f341eb9717", + "age": 33, + "name": "Helen Callahan", + "gender": "female", + "company": "BOLAX", + "email": "helencallahan@bolax.com", + "phone": "+1 (929) 407-3095" + }, + { + "_id": "55d2fc86e6aa094f47df5373", + "age": 32, + "name": "Leblanc Christensen", + "gender": "male", + "company": "LIQUIDOC", + "email": "leblancchristensen@liquidoc.com", + "phone": "+1 (878) 568-2054" + }, + { + "_id": "55d2fc86b297912d153c4e8f", + "age": 31, + "name": "Hill Robbins", + "gender": "male", + "company": "QUANTALIA", + "email": "hillrobbins@quantalia.com", + "phone": "+1 (826) 430-2750" + }, + { + "_id": "55d2fc86e776e4075d7df74e", + "age": 36, + "name": "Tabitha Whitley", + "gender": "female", + "company": "ZILLIDIUM", + "email": "tabithawhitley@zillidium.com", + "phone": "+1 (838) 516-3637" + }, + { + "_id": "55d2fc86197a382bbf34e81f", + "age": 36, + "name": "May Pearson", + "gender": "male", + "company": "RODEOMAD", + "email": "maypearson@rodeomad.com", + "phone": "+1 (854) 429-3462" + }, + { + "_id": "55d2fc863ade7d3517aed2c6", + "age": 28, + "name": "Alvarez Austin", + "gender": "male", + "company": "CUBIX", + "email": "alvarezaustin@cubix.com", + "phone": "+1 (847) 594-3735" + }, + { + "_id": "55d2fc86b158d5d260362ac4", + "age": 31, + "name": "Misty Shepard", + "gender": "female", + "company": "COMVEYER", + "email": "mistyshepard@comveyer.com", + "phone": "+1 (901) 567-3881" + }, + { + "_id": "55d2fc8646bce5646a0b5258", + "age": 22, + "name": "Yvette Hensley", + "gender": "female", + "company": "MYOPIUM", + "email": "yvettehensley@myopium.com", + "phone": "+1 (890) 456-2157" + }, + { + "_id": "55d2fc86c362f848f08340c2", + "age": 36, + "name": "Hernandez Rowe", + "gender": "male", + "company": "EARTHPLEX", + "email": "hernandezrowe@earthplex.com", + "phone": "+1 (807) 502-2308" + }, + { + "_id": "55d2fc8640a621a6f035ce8d", + "age": 39, + "name": "Maura Harper", + "gender": "female", + "company": "ROBOID", + "email": "mauraharper@roboid.com", + "phone": "+1 (927) 506-2290" + }, + { + "_id": "55d2fc86965968be6314d56f", + "age": 20, + "name": "Luisa Gardner", + "gender": "female", + "company": "WAAB", + "email": "luisagardner@waab.com", + "phone": "+1 (964) 514-2189" + }, + { + "_id": "55d2fc86e12b5b70fffd430a", + "age": 36, + "name": "Christa Bradley", + "gender": "female", + "company": "FURNIGEER", + "email": "christabradley@furnigeer.com", + "phone": "+1 (871) 587-3404" + }, + { + "_id": "55d2fc86153d11b40a9bf8bc", + "age": 26, + "name": "Murphy Fleming", + "gender": "male", + "company": "COLLAIRE", + "email": "murphyfleming@collaire.com", + "phone": "+1 (909) 598-3130" + }, + { + "_id": "55d2fc863ec1e4fc29e5ce50", + "age": 28, + "name": "Bobbi Harrington", + "gender": "female", + "company": "MEDIFAX", + "email": "bobbiharrington@medifax.com", + "phone": "+1 (825) 598-2607" + }, + { + "_id": "55d2fc86f4b17262ea0129c6", + "age": 40, + "name": "Paige Flynn", + "gender": "female", + "company": "PULZE", + "email": "paigeflynn@pulze.com", + "phone": "+1 (956) 529-3295" + }, + { + "_id": "55d2fc861c8dc17cb598e70d", + "age": 21, + "name": "Nina Moon", + "gender": "female", + "company": "ZOLAVO", + "email": "ninamoon@zolavo.com", + "phone": "+1 (863) 540-3993" + }, + { + "_id": "55d2fc869d8ee9d95ab9fee4", + "age": 36, + "name": "Shauna Mckay", + "gender": "female", + "company": "LUNCHPOD", + "email": "shaunamckay@lunchpod.com", + "phone": "+1 (879) 435-3179" + }, + { + "_id": "55d2fc86a03f430c5e56194b", + "age": 36, + "name": "Amie Nicholson", + "gender": "female", + "company": "LETPRO", + "email": "amienicholson@letpro.com", + "phone": "+1 (839) 600-3014" + }, + { + "_id": "55d2fc86062b4615153832f6", + "age": 31, + "name": "Pennington Whitney", + "gender": "male", + "company": "TRI@TRIBALOG", + "email": "penningtonwhitney@tri@tribalog.com", + "phone": "+1 (950) 487-3727" + }, + { + "_id": "55d2fc868849f1c7f4f80541", + "age": 23, + "name": "Gena Barton", + "gender": "female", + "company": "VORTEXACO", + "email": "genabarton@vortexaco.com", + "phone": "+1 (889) 515-2172" + }, + { + "_id": "55d2fc860c13e786f86024fd", + "age": 27, + "name": "Ashley Stephens", + "gender": "female", + "company": "ZENSUS", + "email": "ashleystephens@zensus.com", + "phone": "+1 (949) 525-3726" + }, + { + "_id": "55d2fc86cca3638bdc9c942c", + "age": 38, + "name": "Cherie Morgan", + "gender": "female", + "company": "HELIXO", + "email": "cheriemorgan@helixo.com", + "phone": "+1 (815) 514-2167" + }, + { + "_id": "55d2fc8630d769398c9a0788", + "age": 31, + "name": "Ann Wiggins", + "gender": "female", + "company": "NIXELT", + "email": "annwiggins@nixelt.com", + "phone": "+1 (878) 567-2808" + }, + { + "_id": "55d2fc86f3d0744abd99ee4a", + "age": 36, + "name": "Hinton Keller", + "gender": "male", + "company": "VENOFLEX", + "email": "hintonkeller@venoflex.com", + "phone": "+1 (978) 499-2652" + }, + { + "_id": "55d2fc86714b2f2f7f59ff69", + "age": 32, + "name": "Marsh Mullins", + "gender": "male", + "company": "ZIALACTIC", + "email": "marshmullins@zialactic.com", + "phone": "+1 (908) 537-2112" + }, + { + "_id": "55d2fc8644c73b5870be171c", + "age": 28, + "name": "Holland Underwood", + "gender": "male", + "company": "ZILLACON", + "email": "hollandunderwood@zillacon.com", + "phone": "+1 (968) 454-2162" + }, + { + "_id": "55d2fc8648b8691a6a646f9e", + "age": 31, + "name": "Beverly Oneal", + "gender": "female", + "company": "BYTREX", + "email": "beverlyoneal@bytrex.com", + "phone": "+1 (969) 522-2598" + }, + { + "_id": "55d2fc86b3e9627aa4f5f88a", + "age": 24, + "name": "Leanne Frazier", + "gender": "female", + "company": "HOPELI", + "email": "leannefrazier@hopeli.com", + "phone": "+1 (923) 532-3379" + }, + { + "_id": "55d2fc867bdc2935055e4595", + "age": 31, + "name": "Rhodes Cash", + "gender": "male", + "company": "PAPRIKUT", + "email": "rhodescash@paprikut.com", + "phone": "+1 (830) 507-2776" + }, + { + "_id": "55d2fc86543e21b2bc15201d", + "age": 30, + "name": "Cherry Bush", + "gender": "male", + "company": "PROGENEX", + "email": "cherrybush@progenex.com", + "phone": "+1 (935) 577-2984" + }, + { + "_id": "55d2fc8660c1a32dfdf4fe67", + "age": 32, + "name": "Jacobs Clark", + "gender": "male", + "company": "COMDOM", + "email": "jacobsclark@comdom.com", + "phone": "+1 (947) 434-2665" + }, + { + "_id": "55d2fc861641831257904d9c", + "age": 37, + "name": "Nell Mcmahon", + "gender": "female", + "company": "SLAMBDA", + "email": "nellmcmahon@slambda.com", + "phone": "+1 (831) 462-2693" + }, + { + "_id": "55d2fc86835340478ec889e2", + "age": 37, + "name": "Palmer Livingston", + "gender": "male", + "company": "DIGIGEN", + "email": "palmerlivingston@digigen.com", + "phone": "+1 (817) 443-2049" + }, + { + "_id": "55d2fc8697f9fd666529fa34", + "age": 40, + "name": "Ayala Schmidt", + "gender": "male", + "company": "EWAVES", + "email": "ayalaschmidt@ewaves.com", + "phone": "+1 (899) 576-2845" + }, + { + "_id": "55d2fc8682936b03c7c044de", + "age": 26, + "name": "Lynch Beck", + "gender": "male", + "company": "INDEXIA", + "email": "lynchbeck@indexia.com", + "phone": "+1 (942) 411-3724" + }, + { + "_id": "55d2fc86bb74729fe35b2bcc", + "age": 20, + "name": "Yang Hickman", + "gender": "male", + "company": "UXMOX", + "email": "yanghickman@uxmox.com", + "phone": "+1 (944) 554-2948" + }, + { + "_id": "55d2fc86bb04a1e5e39143b1", + "age": 30, + "name": "Andrews Lucas", + "gender": "male", + "company": "CIPROMOX", + "email": "andrewslucas@cipromox.com", + "phone": "+1 (942) 401-2756" + }, + { + "_id": "55d2fc86b862c1492a4c5bc1", + "age": 34, + "name": "Rosa Valdez", + "gender": "female", + "company": "ONTALITY", + "email": "rosavaldez@ontality.com", + "phone": "+1 (963) 414-3056" + }, + { + "_id": "55d2fc868ab9f2f25a46a850", + "age": 30, + "name": "Maria Caldwell", + "gender": "female", + "company": "ACRODANCE", + "email": "mariacaldwell@acrodance.com", + "phone": "+1 (963) 433-2398" + }, + { + "_id": "55d2fc868d206b4d99f1f0b2", + "age": 36, + "name": "Gilda Chase", + "gender": "female", + "company": "KOFFEE", + "email": "gildachase@koffee.com", + "phone": "+1 (980) 591-3955" + }, + { + "_id": "55d2fc86e0c5a2f031b4a0d9", + "age": 24, + "name": "Dejesus Pittman", + "gender": "male", + "company": "SLAX", + "email": "dejesuspittman@slax.com", + "phone": "+1 (819) 574-2826" + }, + { + "_id": "55d2fc868190178a9af16ec5", + "age": 23, + "name": "Valdez Gibson", + "gender": "male", + "company": "ELECTONIC", + "email": "valdezgibson@electonic.com", + "phone": "+1 (809) 520-3985" + }, + { + "_id": "55d2fc86d3bc3cf86e16bc5b", + "age": 21, + "name": "Aguilar Bird", + "gender": "male", + "company": "ULTRASURE", + "email": "aguilarbird@ultrasure.com", + "phone": "+1 (813) 455-3814" + }, + { + "_id": "55d2fc86701c49cc235f0b49", + "age": 24, + "name": "Bentley Mooney", + "gender": "male", + "company": "BEDLAM", + "email": "bentleymooney@bedlam.com", + "phone": "+1 (870) 530-2188" + }, + { + "_id": "55d2fc863fe6d9fc492a1ca5", + "age": 28, + "name": "Ruby Wooten", + "gender": "female", + "company": "MARKETOID", + "email": "rubywooten@marketoid.com", + "phone": "+1 (813) 470-3521" + }, + { + "_id": "55d2fc8622f489f721743001", + "age": 28, + "name": "Garrison Blevins", + "gender": "male", + "company": "KEENGEN", + "email": "garrisonblevins@keengen.com", + "phone": "+1 (974) 538-2989" + }, + { + "_id": "55d2fc863dc60d226c55ece7", + "age": 33, + "name": "Harper Tanner", + "gender": "male", + "company": "QABOOS", + "email": "harpertanner@qaboos.com", + "phone": "+1 (953) 406-3082" + }, + { + "_id": "55d2fc86fecd601439c2702e", + "age": 32, + "name": "Best Robles", + "gender": "male", + "company": "OCEANICA", + "email": "bestrobles@oceanica.com", + "phone": "+1 (815) 539-3097" + }, + { + "_id": "55d2fc86ba25530a2149beab", + "age": 36, + "name": "Marian Bradshaw", + "gender": "female", + "company": "XYQAG", + "email": "marianbradshaw@xyqag.com", + "phone": "+1 (928) 410-3218" + }, + { + "_id": "55d2fc867352b6b799d365e4", + "age": 23, + "name": "Whitley Oneil", + "gender": "male", + "company": "XURBAN", + "email": "whitleyoneil@xurban.com", + "phone": "+1 (802) 578-3671" + }, + { + "_id": "55d2fc865ee137dbdee5cde2", + "age": 34, + "name": "Ella Fox", + "gender": "female", + "company": "TUBESYS", + "email": "ellafox@tubesys.com", + "phone": "+1 (920) 524-3066" + }, + { + "_id": "55d2fc860cc159486a822879", + "age": 30, + "name": "Farmer Castro", + "gender": "male", + "company": "QNEKT", + "email": "farmercastro@qnekt.com", + "phone": "+1 (866) 578-2968" + }, + { + "_id": "55d2fc863b57eefc3015d373", + "age": 28, + "name": "Guy Cochran", + "gender": "male", + "company": "VICON", + "email": "guycochran@vicon.com", + "phone": "+1 (840) 567-2191" + }, + { + "_id": "55d2fc863df7dfda22e99029", + "age": 32, + "name": "Leach Rocha", + "gender": "male", + "company": "DANJA", + "email": "leachrocha@danja.com", + "phone": "+1 (971) 589-3164" + }, + { + "_id": "55d2fc86aba3b9d7ce3f877c", + "age": 36, + "name": "Tanner Hayes", + "gender": "male", + "company": "TELEQUIET", + "email": "tannerhayes@telequiet.com", + "phone": "+1 (813) 526-2989" + }, + { + "_id": "55d2fc862cd6fb84f734fa0e", + "age": 30, + "name": "Keith Maldonado", + "gender": "male", + "company": "MAGNEATO", + "email": "keithmaldonado@magneato.com", + "phone": "+1 (997) 419-3200" + }, + { + "_id": "55d2fc8663d4dc1e43943f62", + "age": 29, + "name": "Winnie Harrell", + "gender": "female", + "company": "FRENEX", + "email": "winnieharrell@frenex.com", + "phone": "+1 (966) 565-2447" + }, + { + "_id": "55d2fc86b57f9312b0d28a1d", + "age": 28, + "name": "Sandoval Garza", + "gender": "male", + "company": "INTERLOO", + "email": "sandovalgarza@interloo.com", + "phone": "+1 (972) 597-3431" + }, + { + "_id": "55d2fc86a356d194d285d160", + "age": 23, + "name": "Lina Dejesus", + "gender": "female", + "company": "ORONOKO", + "email": "linadejesus@oronoko.com", + "phone": "+1 (910) 560-2515" + }, + { + "_id": "55d2fc862f4cd754495f93c7", + "age": 30, + "name": "Jana Spence", + "gender": "female", + "company": "ZILLACOM", + "email": "janaspence@zillacom.com", + "phone": "+1 (994) 436-2023" + }, + { + "_id": "55d2fc869b25329fae4936d0", + "age": 32, + "name": "Mcdowell Fisher", + "gender": "male", + "company": "GYNK", + "email": "mcdowellfisher@gynk.com", + "phone": "+1 (941) 587-3569" + }, + { + "_id": "55d2fc866b52b90a3758bdd3", + "age": 28, + "name": "Farley Bernard", + "gender": "male", + "company": "NETROPIC", + "email": "farleybernard@netropic.com", + "phone": "+1 (856) 540-2658" + }, + { + "_id": "55d2fc864086901eaeb80443", + "age": 25, + "name": "Lorna Howe", + "gender": "female", + "company": "ISOSWITCH", + "email": "lornahowe@isoswitch.com", + "phone": "+1 (851) 432-3160" + }, + { + "_id": "55d2fc86b4ac38891f11340b", + "age": 25, + "name": "English Watts", + "gender": "male", + "company": "INFOTRIPS", + "email": "englishwatts@infotrips.com", + "phone": "+1 (942) 481-2578" + }, + { + "_id": "55d2fc86e0d047d4eb3c224f", + "age": 35, + "name": "Burch Howell", + "gender": "male", + "company": "FANFARE", + "email": "burchhowell@fanfare.com", + "phone": "+1 (986) 507-2725" + }, + { + "_id": "55d2fc86330a8dab2ddbc0c4", + "age": 39, + "name": "Hudson Bender", + "gender": "male", + "company": "ENORMO", + "email": "hudsonbender@enormo.com", + "phone": "+1 (982) 553-3993" + }, + { + "_id": "55d2fc8600bb27f4ba215be8", + "age": 30, + "name": "Mcdonald Whitehead", + "gender": "male", + "company": "SENMAO", + "email": "mcdonaldwhitehead@senmao.com", + "phone": "+1 (837) 449-3264" + }, + { + "_id": "55d2fc8683787d8b7b400408", + "age": 31, + "name": "Hope Holden", + "gender": "female", + "company": "EVENTIX", + "email": "hopeholden@eventix.com", + "phone": "+1 (888) 436-2921" + }, + { + "_id": "55d2fc86cd7d2a5c962d2c05", + "age": 36, + "name": "Suarez Mejia", + "gender": "male", + "company": "BUZZWORKS", + "email": "suarezmejia@buzzworks.com", + "phone": "+1 (919) 526-3966" + }, + { + "_id": "55d2fc8612bf58c9b2d953cf", + "age": 27, + "name": "Michele Little", + "gender": "female", + "company": "VINCH", + "email": "michelelittle@vinch.com", + "phone": "+1 (817) 414-2165" + }, + { + "_id": "55d2fc864a8103126f905972", + "age": 25, + "name": "Patrick Cooke", + "gender": "male", + "company": "BEDDER", + "email": "patrickcooke@bedder.com", + "phone": "+1 (993) 587-2086" + }, + { + "_id": "55d2fc865ddf784cac1f023c", + "age": 31, + "name": "Holcomb Beasley", + "gender": "male", + "company": "TECHTRIX", + "email": "holcombbeasley@techtrix.com", + "phone": "+1 (879) 458-3507" + }, + { + "_id": "55d2fc86e27a28e9e9b0d232", + "age": 34, + "name": "Catalina Donovan", + "gender": "female", + "company": "FILODYNE", + "email": "catalinadonovan@filodyne.com", + "phone": "+1 (818) 542-2296" + }, + { + "_id": "55d2fc861801cbac57fa3186", + "age": 21, + "name": "Leslie Bryan", + "gender": "female", + "company": "LUXURIA", + "email": "lesliebryan@luxuria.com", + "phone": "+1 (917) 590-3272" + }, + { + "_id": "55d2fc86d3aa760444ec40cc", + "age": 37, + "name": "Hobbs Noel", + "gender": "male", + "company": "ZILLA", + "email": "hobbsnoel@zilla.com", + "phone": "+1 (917) 430-3792" + }, + { + "_id": "55d2fc86f53d0267e33ddb67", + "age": 38, + "name": "Nunez Meyers", + "gender": "male", + "company": "ENTROFLEX", + "email": "nunezmeyers@entroflex.com", + "phone": "+1 (940) 419-3943" + }, + { + "_id": "55d2fc867805cf4262e648a9", + "age": 37, + "name": "Sonya Sloan", + "gender": "female", + "company": "SURELOGIC", + "email": "sonyasloan@surelogic.com", + "phone": "+1 (924) 561-3268" + }, + { + "_id": "55d2fc86a3b756eaaef9f9fa", + "age": 31, + "name": "Angeline Sargent", + "gender": "female", + "company": "QUIZMO", + "email": "angelinesargent@quizmo.com", + "phone": "+1 (952) 539-3859" + }, + { + "_id": "55d2fc860f0e4be242c866a3", + "age": 40, + "name": "Norris Webb", + "gender": "male", + "company": "ZENCO", + "email": "norriswebb@zenco.com", + "phone": "+1 (834) 527-2399" + }, + { + "_id": "55d2fc86b2da030fb755d74c", + "age": 38, + "name": "Wise Bonner", + "gender": "male", + "company": "KINETICA", + "email": "wisebonner@kinetica.com", + "phone": "+1 (938) 416-3537" + }, + { + "_id": "55d2fc8699036f35ee214843", + "age": 25, + "name": "Imogene Blankenship", + "gender": "female", + "company": "POLARAX", + "email": "imogeneblankenship@polarax.com", + "phone": "+1 (877) 476-3735" + }, + { + "_id": "55d2fc86009b5a1658986a92", + "age": 27, + "name": "Silva Schneider", + "gender": "male", + "company": "MINGA", + "email": "silvaschneider@minga.com", + "phone": "+1 (884) 420-2111" + }, + { + "_id": "55d2fc86e3dcb6d4996e9813", + "age": 40, + "name": "Lawanda Cortez", + "gender": "female", + "company": "HOMETOWN", + "email": "lawandacortez@hometown.com", + "phone": "+1 (946) 525-3826" + }, + { + "_id": "55d2fc8641977ef422e73176", + "age": 40, + "name": "Clements Waters", + "gender": "male", + "company": "FLEETMIX", + "email": "clementswaters@fleetmix.com", + "phone": "+1 (973) 523-2395" + }, + { + "_id": "55d2fc869d0f5363ad055935", + "age": 20, + "name": "Ofelia Gilbert", + "gender": "female", + "company": "ECRATER", + "email": "ofeliagilbert@ecrater.com", + "phone": "+1 (828) 404-2646" + }, + { + "_id": "55d2fc86c39b876162269895", + "age": 23, + "name": "Valenzuela Carney", + "gender": "male", + "company": "HYDROCOM", + "email": "valenzuelacarney@hydrocom.com", + "phone": "+1 (842) 566-3650" + }, + { + "_id": "55d2fc86546c31933b02dd85", + "age": 28, + "name": "Wells Santana", + "gender": "male", + "company": "ZIDOX", + "email": "wellssantana@zidox.com", + "phone": "+1 (886) 527-2963" + }, + { + "_id": "55d2fc86a981c3741ad50f8c", + "age": 29, + "name": "Karla Carroll", + "gender": "female", + "company": "MAGNEMO", + "email": "karlacarroll@magnemo.com", + "phone": "+1 (922) 418-3361" + }, + { + "_id": "55d2fc861054327ef76378e3", + "age": 29, + "name": "Juliet Butler", + "gender": "female", + "company": "ORBAXTER", + "email": "julietbutler@orbaxter.com", + "phone": "+1 (838) 554-2269" + }, + { + "_id": "55d2fc868120c8a8e4eb9149", + "age": 26, + "name": "Lisa Copeland", + "gender": "female", + "company": "SOFTMICRO", + "email": "lisacopeland@softmicro.com", + "phone": "+1 (915) 577-2302" + }, + { + "_id": "55d2fc86b0eb908d67787f43", + "age": 37, + "name": "Mcmahon Spencer", + "gender": "male", + "company": "BOILCAT", + "email": "mcmahonspencer@boilcat.com", + "phone": "+1 (871) 501-2558" + }, + { + "_id": "55d2fc860c0b6f5fb520ad2a", + "age": 38, + "name": "Campbell Baxter", + "gender": "male", + "company": "DREAMIA", + "email": "campbellbaxter@dreamia.com", + "phone": "+1 (858) 524-3012" + }, + { + "_id": "55d2fc86460508fbed08e924", + "age": 23, + "name": "Lindsay Sharp", + "gender": "male", + "company": "SYBIXTEX", + "email": "lindsaysharp@sybixtex.com", + "phone": "+1 (974) 573-3073" + }, + { + "_id": "55d2fc860bf5295ce5679523", + "age": 35, + "name": "Kathrine Browning", + "gender": "female", + "company": "LUNCHPAD", + "email": "kathrinebrowning@lunchpad.com", + "phone": "+1 (922) 458-2466" + }, + { + "_id": "55d2fc86d2311e0208cd17a2", + "age": 35, + "name": "Alice Faulkner", + "gender": "female", + "company": "PLEXIA", + "email": "alicefaulkner@plexia.com", + "phone": "+1 (939) 419-3621" + }, + { + "_id": "55d2fc86e85116cc8d6c0d70", + "age": 32, + "name": "Ford Mclean", + "gender": "male", + "company": "QUADEEBO", + "email": "fordmclean@quadeebo.com", + "phone": "+1 (974) 521-2540" + }, + { + "_id": "55d2fc86f60393147a3a8a07", + "age": 29, + "name": "Ollie Cannon", + "gender": "female", + "company": "RAMJOB", + "email": "olliecannon@ramjob.com", + "phone": "+1 (961) 497-3201" + }, + { + "_id": "55d2fc8661d02152a5ba425a", + "age": 36, + "name": "Calderon Vaughan", + "gender": "male", + "company": "DIGIQUE", + "email": "calderonvaughan@digique.com", + "phone": "+1 (912) 553-3902" + }, + { + "_id": "55d2fc86a55890c6ce345bf0", + "age": 28, + "name": "Warren Henry", + "gender": "male", + "company": "ZERBINA", + "email": "warrenhenry@zerbina.com", + "phone": "+1 (850) 464-3656" + }, + { + "_id": "55d2fc86ccead2741ea21e0e", + "age": 21, + "name": "Liliana York", + "gender": "female", + "company": "ANIXANG", + "email": "lilianayork@anixang.com", + "phone": "+1 (956) 403-2096" + }, + { + "_id": "55d2fc86d72dd184f6884371", + "age": 33, + "name": "Lora Alvarez", + "gender": "female", + "company": "BLURRYBUS", + "email": "loraalvarez@blurrybus.com", + "phone": "+1 (917) 457-2866" + }, + { + "_id": "55d2fc86fda0c180ccc9598a", + "age": 22, + "name": "Luna Ellis", + "gender": "male", + "company": "SLUMBERIA", + "email": "lunaellis@slumberia.com", + "phone": "+1 (878) 589-3511" + }, + { + "_id": "55d2fc860dd81b364fc1c2a9", + "age": 30, + "name": "Phoebe Chang", + "gender": "female", + "company": "OPTICON", + "email": "phoebechang@opticon.com", + "phone": "+1 (962) 559-3475" + }, + { + "_id": "55d2fc8657954cc73c166579", + "age": 40, + "name": "Anna Crane", + "gender": "female", + "company": "AQUAFIRE", + "email": "annacrane@aquafire.com", + "phone": "+1 (989) 567-3649" + }, + { + "_id": "55d2fc86d996f0f466a006c8", + "age": 39, + "name": "Matthews French", + "gender": "male", + "company": "CINESANCT", + "email": "matthewsfrench@cinesanct.com", + "phone": "+1 (896) 518-2965" + }, + { + "_id": "55d2fc8601aad1428aa65531", + "age": 34, + "name": "Hutchinson Ellison", + "gender": "male", + "company": "MOREGANIC", + "email": "hutchinsonellison@moreganic.com", + "phone": "+1 (860) 563-2707" + }, + { + "_id": "55d2fc86ec14a6c798e22d72", + "age": 21, + "name": "Gwen Russell", + "gender": "female", + "company": "COMVOY", + "email": "gwenrussell@comvoy.com", + "phone": "+1 (873) 468-2314" + }, + { + "_id": "55d2fc865123af00125fd9bd", + "age": 22, + "name": "Natalie Stuart", + "gender": "female", + "company": "MOMENTIA", + "email": "nataliestuart@momentia.com", + "phone": "+1 (908) 573-2177" + }, + { + "_id": "55d2fc8688af31f1e9ff0d20", + "age": 28, + "name": "Brianna Meyer", + "gender": "female", + "company": "VIASIA", + "email": "briannameyer@viasia.com", + "phone": "+1 (860) 475-3139" + }, + { + "_id": "55d2fc8687f0ff5daa16cbbd", + "age": 28, + "name": "Trisha Castillo", + "gender": "female", + "company": "CYCLONICA", + "email": "trishacastillo@cyclonica.com", + "phone": "+1 (869) 564-2957" + }, + { + "_id": "55d2fc863b1c51f11ce4e921", + "age": 36, + "name": "Powers Weeks", + "gender": "male", + "company": "BIZMATIC", + "email": "powersweeks@bizmatic.com", + "phone": "+1 (981) 464-3668" + }, + { + "_id": "55d2fc86d8f5d2bef6f84bba", + "age": 23, + "name": "Young Cabrera", + "gender": "female", + "company": "CINASTER", + "email": "youngcabrera@cinaster.com", + "phone": "+1 (897) 528-3924" + }, + { + "_id": "55d2fc86861238b57e2932fd", + "age": 27, + "name": "Maxine Rodgers", + "gender": "female", + "company": "CHORIZON", + "email": "maxinerodgers@chorizon.com", + "phone": "+1 (996) 449-2805" + }, + { + "_id": "55d2fc86fe5b2f6823cc295f", + "age": 26, + "name": "Davis Norris", + "gender": "male", + "company": "CORIANDER", + "email": "davisnorris@coriander.com", + "phone": "+1 (947) 512-2093" + }, + { + "_id": "55d2fc86862e7d0bba1ab524", + "age": 25, + "name": "Ericka Conner", + "gender": "female", + "company": "STRALOY", + "email": "erickaconner@straloy.com", + "phone": "+1 (922) 565-2956" + }, + { + "_id": "55d2fc8625bf91e39382ab4c", + "age": 21, + "name": "Payne Joyner", + "gender": "male", + "company": "OMNIGOG", + "email": "paynejoyner@omnigog.com", + "phone": "+1 (998) 521-3917" + }, + { + "_id": "55d2fc866835ee51ccea79bb", + "age": 24, + "name": "Fletcher Payne", + "gender": "male", + "company": "AMTAP", + "email": "fletcherpayne@amtap.com", + "phone": "+1 (991) 517-3798" + }, + { + "_id": "55d2fc863df3424f70db684c", + "age": 38, + "name": "Mosley Cobb", + "gender": "male", + "company": "HONOTRON", + "email": "mosleycobb@honotron.com", + "phone": "+1 (873) 593-2248" + }, + { + "_id": "55d2fc867c4ad9b233d15983", + "age": 24, + "name": "Webster Sandoval", + "gender": "male", + "company": "HOMELUX", + "email": "webstersandoval@homelux.com", + "phone": "+1 (967) 431-2940" + }, + { + "_id": "55d2fc8613bf2fe49b1a1f8c", + "age": 36, + "name": "Colon Mcgee", + "gender": "male", + "company": "ZAPPIX", + "email": "colonmcgee@zappix.com", + "phone": "+1 (806) 444-2451" + }, + { + "_id": "55d2fc8681a8ccebe8aacd93", + "age": 32, + "name": "Monique Logan", + "gender": "female", + "company": "CALLFLEX", + "email": "moniquelogan@callflex.com", + "phone": "+1 (957) 577-3780" + }, + { + "_id": "55d2fc868756d302f29fddb2", + "age": 38, + "name": "Stewart Ball", + "gender": "male", + "company": "NETPLODE", + "email": "stewartball@netplode.com", + "phone": "+1 (966) 435-2206" + }, + { + "_id": "55d2fc86457f4d474388d2c8", + "age": 23, + "name": "Montgomery Carter", + "gender": "male", + "company": "OLUCORE", + "email": "montgomerycarter@olucore.com", + "phone": "+1 (894) 556-2662" + }, + { + "_id": "55d2fc866da04e0d15b12b36", + "age": 28, + "name": "Brenda Mccoy", + "gender": "female", + "company": "AQUAZURE", + "email": "brendamccoy@aquazure.com", + "phone": "+1 (837) 483-3741" + }, + { + "_id": "55d2fc86e2a61a730a8d5c8f", + "age": 34, + "name": "Eddie Buchanan", + "gender": "female", + "company": "COGNICODE", + "email": "eddiebuchanan@cognicode.com", + "phone": "+1 (924) 479-3753" + }, + { + "_id": "55d2fc8616e7989042f61488", + "age": 23, + "name": "Eva Mendoza", + "gender": "female", + "company": "SOLGAN", + "email": "evamendoza@solgan.com", + "phone": "+1 (899) 522-3051" + }, + { + "_id": "55d2fc86152fd9e4c5471fd7", + "age": 21, + "name": "Dawson Medina", + "gender": "male", + "company": "AQUASSEUR", + "email": "dawsonmedina@aquasseur.com", + "phone": "+1 (877) 580-2295" + }, + { + "_id": "55d2fc86a3633b3a799a7811", + "age": 34, + "name": "Terrie Hobbs", + "gender": "female", + "company": "VORATAK", + "email": "terriehobbs@voratak.com", + "phone": "+1 (938) 511-2077" + }, + { + "_id": "55d2fc8665a05f05f07bb790", + "age": 37, + "name": "Iris Bishop", + "gender": "female", + "company": "INSURESYS", + "email": "irisbishop@insuresys.com", + "phone": "+1 (819) 415-3840" + }, + { + "_id": "55d2fc8799e7556a033b93f6", + "age": 29, + "name": "Estelle Grant", + "gender": "female", + "company": "ZOGAK", + "email": "estellegrant@zogak.com", + "phone": "+1 (854) 437-2898" + }, + { + "_id": "55d2fc87745c675e697af04c", + "age": 30, + "name": "Dianna Gonzalez", + "gender": "female", + "company": "PIVITOL", + "email": "diannagonzalez@pivitol.com", + "phone": "+1 (816) 545-3520" + } +] diff --git a/vendor/github.com/PuerkitoBio/purell/.gitignore b/vendor/github.com/PuerkitoBio/purell/.gitignore new file mode 100644 index 000000000..748e4c807 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/.gitignore @@ -0,0 +1,5 @@ +*.sublime-* +.DS_Store +*.swp +*.swo +tags diff --git a/vendor/github.com/PuerkitoBio/purell/.travis.yml b/vendor/github.com/PuerkitoBio/purell/.travis.yml new file mode 100644 index 000000000..facfc91c6 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - tip diff --git a/vendor/github.com/PuerkitoBio/purell/LICENSE b/vendor/github.com/PuerkitoBio/purell/LICENSE new file mode 100644 index 000000000..4b9986dea --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2012, Martin Angers +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/PuerkitoBio/purell/README.md b/vendor/github.com/PuerkitoBio/purell/README.md new file mode 100644 index 000000000..a78a3df65 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/README.md @@ -0,0 +1,185 @@ +# Purell + +Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know... + +Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc]. + +[![build status](https://secure.travis-ci.org/PuerkitoBio/purell.png)](http://travis-ci.org/PuerkitoBio/purell) + +## Install + +`go get github.com/PuerkitoBio/purell` + +## Changelog + +* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich). +* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]). +* **v0.2.0** : Add benchmarks, Attempt IDN support. +* **v0.1.0** : Initial release. + +## Examples + +From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."): + +```go +package purell + +import ( + "fmt" + "net/url" +) + +func ExampleNormalizeURLString() { + if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/", + FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil { + panic(err) + } else { + fmt.Print(normalized) + } + // Output: http://somewebsite.com:80/Amazing%3F/url/ +} + +func ExampleMustNormalizeURLString() { + normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/", + FlagsUnsafeGreedy) + fmt.Print(normalized) + + // Output: http://somewebsite.com/Amazing%FA/url +} + +func ExampleNormalizeURL() { + if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil { + panic(err) + } else { + normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment) + fmt.Print(normalized) + } + + // Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0 +} +``` + +## API + +As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags: + +```go +const ( + // Safe normalizations + FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1 + FlagLowercaseHost // http://HOST -> http://host + FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF + FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA + FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$ + FlagRemoveDefaultPort // http://host:80 -> http://host + FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path + + // Usually safe normalizations + FlagRemoveTrailingSlash // http://host/path/ -> http://host/path + FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags) + FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c + + // Unsafe normalizations + FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/ + FlagRemoveFragment // http://host/path#fragment -> http://host/path + FlagForceHTTP // https://host -> http://host + FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b + FlagRemoveWWW // http://www.host/ -> http://host/ + FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags) + FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3 + + // Normalizations not in the wikipedia article, required to cover tests cases + // submitted by jehiah + FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147 + FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147 + FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147 + FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path + FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path + + // Convenience set of safe normalizations + FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator + + // For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags, + // while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix". + + // Convenience set of usually safe normalizations (includes FlagsSafe) + FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments + FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments + + // Convenience set of unsafe normalizations (includes FlagsUsuallySafe) + FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery + FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery + + // Convenience set of all available flags + FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator + FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator +) +``` + +For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set. + +The [full godoc reference is available on gopkgdoc][godoc]. + +Some things to note: + +* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it. + +* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*): + - %24 -> $ + - %26 -> & + - %2B-%3B -> +,-./0123456789:; + - %3D -> = + - %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ + - %5F -> _ + - %61-%7A -> abcdefghijklmnopqrstuvwxyz + - %7E -> ~ + + +* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization). + +* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell. + +* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object. + +### Safe vs Usually Safe vs Unsafe + +Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between. + +Consider the following URL: + +`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid` + +Normalizing with the `FlagsSafe` gives: + +`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid` + +With the `FlagsUsuallySafeGreedy`: + +`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid` + +And with `FlagsUnsafeGreedy`: + +`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3` + +## TODOs + +* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`. + +## Thanks / Contributions + +@rogpeppe +@jehiah +@opennota +@pchristopher1275 +@zenovich + +## License + +The [BSD 3-Clause license][bsd]. + +[bsd]: http://opensource.org/licenses/BSD-3-Clause +[wiki]: http://en.wikipedia.org/wiki/URL_normalization +[rfc]: http://tools.ietf.org/html/rfc3986#section-6 +[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell +[pr5]: https://github.com/PuerkitoBio/purell/pull/5 +[iss7]: https://github.com/PuerkitoBio/purell/issues/7 diff --git a/vendor/github.com/PuerkitoBio/purell/bench_test.go b/vendor/github.com/PuerkitoBio/purell/bench_test.go new file mode 100644 index 000000000..7549731fc --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/bench_test.go @@ -0,0 +1,57 @@ +package purell + +import ( + "testing" +) + +var ( + safeUrl = "HttPS://..iaMHost..Test:443/paTh^A%ef//./%41PaTH/..//?" + usuallySafeUrl = "HttPS://..iaMHost..Test:443/paTh^A%ef//./%41PaTH/../final/" + unsafeUrl = "HttPS://..www.iaMHost..Test:443/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment" + allDWORDUrl = "HttPS://1113982867:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment" + allOctalUrl = "HttPS://0102.0146.07.0223:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment" + allHexUrl = "HttPS://0x42660793:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment" + allCombinedUrl = "HttPS://..0x42660793.:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment" +) + +func BenchmarkSafe(b *testing.B) { + for i := 0; i < b.N; i++ { + NormalizeURLString(safeUrl, FlagsSafe) + } +} + +func BenchmarkUsuallySafe(b *testing.B) { + for i := 0; i < b.N; i++ { + NormalizeURLString(usuallySafeUrl, FlagsUsuallySafeGreedy) + } +} + +func BenchmarkUnsafe(b *testing.B) { + for i := 0; i < b.N; i++ { + NormalizeURLString(unsafeUrl, FlagsUnsafeGreedy) + } +} + +func BenchmarkAllDWORD(b *testing.B) { + for i := 0; i < b.N; i++ { + NormalizeURLString(allDWORDUrl, FlagsAllGreedy) + } +} + +func BenchmarkAllOctal(b *testing.B) { + for i := 0; i < b.N; i++ { + NormalizeURLString(allOctalUrl, FlagsAllGreedy) + } +} + +func BenchmarkAllHex(b *testing.B) { + for i := 0; i < b.N; i++ { + NormalizeURLString(allHexUrl, FlagsAllGreedy) + } +} + +func BenchmarkAllCombined(b *testing.B) { + for i := 0; i < b.N; i++ { + NormalizeURLString(allCombinedUrl, FlagsAllGreedy) + } +} diff --git a/vendor/github.com/PuerkitoBio/purell/benchmarks/v0.1.0 b/vendor/github.com/PuerkitoBio/purell/benchmarks/v0.1.0 new file mode 100644 index 000000000..3bbe7113c --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/benchmarks/v0.1.0 @@ -0,0 +1,9 @@ +PASS +BenchmarkSafe 500000 6131 ns/op +BenchmarkUsuallySafe 200000 7864 ns/op +BenchmarkUnsafe 100000 28560 ns/op +BenchmarkAllDWORD 50000 38722 ns/op +BenchmarkAllOctal 50000 40941 ns/op +BenchmarkAllHex 50000 44063 ns/op +BenchmarkAllCombined 50000 33613 ns/op +ok github.com/PuerkitoBio/purell 17.404s diff --git a/vendor/github.com/PuerkitoBio/purell/example_test.go b/vendor/github.com/PuerkitoBio/purell/example_test.go new file mode 100644 index 000000000..997b95369 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/example_test.go @@ -0,0 +1,35 @@ +package purell + +import ( + "fmt" + "net/url" +) + +func ExampleNormalizeURLString() { + if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/", + FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil { + panic(err) + } else { + fmt.Print(normalized) + } + // Output: http://somewebsite.com:80/Amazing%3F/url/ +} + +func ExampleMustNormalizeURLString() { + normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/", + FlagsUnsafeGreedy) + fmt.Print(normalized) + + // Output: http://somewebsite.com/Amazing%FA/url +} + +func ExampleNormalizeURL() { + if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil { + panic(err) + } else { + normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment) + fmt.Print(normalized) + } + + // Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0 +} diff --git a/vendor/github.com/PuerkitoBio/purell/purell.go b/vendor/github.com/PuerkitoBio/purell/purell.go new file mode 100644 index 000000000..b79da64b3 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/purell.go @@ -0,0 +1,375 @@ +/* +Package purell offers URL normalization as described on the wikipedia page: +http://en.wikipedia.org/wiki/URL_normalization +*/ +package purell + +import ( + "bytes" + "fmt" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/PuerkitoBio/urlesc" + "golang.org/x/net/idna" + "golang.org/x/text/secure/precis" + "golang.org/x/text/unicode/norm" +) + +// A set of normalization flags determines how a URL will +// be normalized. +type NormalizationFlags uint + +const ( + // Safe normalizations + FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1 + FlagLowercaseHost // http://HOST -> http://host + FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF + FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA + FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$ + FlagRemoveDefaultPort // http://host:80 -> http://host + FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path + + // Usually safe normalizations + FlagRemoveTrailingSlash // http://host/path/ -> http://host/path + FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags) + FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c + + // Unsafe normalizations + FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/ + FlagRemoveFragment // http://host/path#fragment -> http://host/path + FlagForceHTTP // https://host -> http://host + FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b + FlagRemoveWWW // http://www.host/ -> http://host/ + FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags) + FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3 + + // Normalizations not in the wikipedia article, required to cover tests cases + // submitted by jehiah + FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147 + FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147 + FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147 + FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path + FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path + + // Convenience set of safe normalizations + FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator + + // For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags, + // while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix". + + // Convenience set of usually safe normalizations (includes FlagsSafe) + FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments + FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments + + // Convenience set of unsafe normalizations (includes FlagsUsuallySafe) + FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery + FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery + + // Convenience set of all available flags + FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator + FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator +) + +const ( + defaultHttpPort = ":80" + defaultHttpsPort = ":443" +) + +// Regular expressions used by the normalizations +var rxPort = regexp.MustCompile(`(:\d+)/?$`) +var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`) +var rxDupSlashes = regexp.MustCompile(`/{2,}`) +var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`) +var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`) +var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`) +var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`) +var rxEmptyPort = regexp.MustCompile(`:+$`) + +// Map of flags to implementation function. +// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically +// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator. + +// Since maps have undefined traversing order, make a slice of ordered keys +var flagsOrder = []NormalizationFlags{ + FlagLowercaseScheme, + FlagLowercaseHost, + FlagRemoveDefaultPort, + FlagRemoveDirectoryIndex, + FlagRemoveDotSegments, + FlagRemoveFragment, + FlagForceHTTP, // Must be after remove default port (because https=443/http=80) + FlagRemoveDuplicateSlashes, + FlagRemoveWWW, + FlagAddWWW, + FlagSortQuery, + FlagDecodeDWORDHost, + FlagDecodeOctalHost, + FlagDecodeHexHost, + FlagRemoveUnnecessaryHostDots, + FlagRemoveEmptyPortSeparator, + FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last + FlagAddTrailingSlash, +} + +// ... and then the map, where order is unimportant +var flags = map[NormalizationFlags]func(*url.URL){ + FlagLowercaseScheme: lowercaseScheme, + FlagLowercaseHost: lowercaseHost, + FlagRemoveDefaultPort: removeDefaultPort, + FlagRemoveDirectoryIndex: removeDirectoryIndex, + FlagRemoveDotSegments: removeDotSegments, + FlagRemoveFragment: removeFragment, + FlagForceHTTP: forceHTTP, + FlagRemoveDuplicateSlashes: removeDuplicateSlashes, + FlagRemoveWWW: removeWWW, + FlagAddWWW: addWWW, + FlagSortQuery: sortQuery, + FlagDecodeDWORDHost: decodeDWORDHost, + FlagDecodeOctalHost: decodeOctalHost, + FlagDecodeHexHost: decodeHexHost, + FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots, + FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator, + FlagRemoveTrailingSlash: removeTrailingSlash, + FlagAddTrailingSlash: addTrailingSlash, +} + +// MustNormalizeURLString returns the normalized string, and panics if an error occurs. +// It takes an URL string as input, as well as the normalization flags. +func MustNormalizeURLString(u string, f NormalizationFlags) string { + result, e := NormalizeURLString(u, f) + if e != nil { + panic(e) + } + return result +} + +// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object. +// It takes an URL string as input, as well as the normalization flags. +func NormalizeURLString(u string, f NormalizationFlags) (string, error) { + if parsed, e := url.Parse(u); e != nil { + return "", e + } else { + options := make([]precis.Option, 1, 3) + options[0] = precis.IgnoreCase + if f&FlagLowercaseHost == FlagLowercaseHost { + options = append(options, precis.FoldCase()) + } + options = append(options, precis.Norm(norm.NFC)) + profile := precis.NewFreeform(options...) + if parsed.Host, e = idna.ToASCII(profile.NewTransformer().String(parsed.Host)); e != nil { + return "", e + } + return NormalizeURL(parsed, f), nil + } + panic("Unreachable code.") +} + +// NormalizeURL returns the normalized string. +// It takes a parsed URL object as input, as well as the normalization flags. +func NormalizeURL(u *url.URL, f NormalizationFlags) string { + for _, k := range flagsOrder { + if f&k == k { + flags[k](u) + } + } + return urlesc.Escape(u) +} + +func lowercaseScheme(u *url.URL) { + if len(u.Scheme) > 0 { + u.Scheme = strings.ToLower(u.Scheme) + } +} + +func lowercaseHost(u *url.URL) { + if len(u.Host) > 0 { + u.Host = strings.ToLower(u.Host) + } +} + +func removeDefaultPort(u *url.URL) { + if len(u.Host) > 0 { + scheme := strings.ToLower(u.Scheme) + u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string { + if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) { + return "" + } + return val + }) + } +} + +func removeTrailingSlash(u *url.URL) { + if l := len(u.Path); l > 0 { + if strings.HasSuffix(u.Path, "/") { + u.Path = u.Path[:l-1] + } + } else if l = len(u.Host); l > 0 { + if strings.HasSuffix(u.Host, "/") { + u.Host = u.Host[:l-1] + } + } +} + +func addTrailingSlash(u *url.URL) { + if l := len(u.Path); l > 0 { + if !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + } else if l = len(u.Host); l > 0 { + if !strings.HasSuffix(u.Host, "/") { + u.Host += "/" + } + } +} + +func removeDotSegments(u *url.URL) { + if len(u.Path) > 0 { + var dotFree []string + var lastIsDot bool + + sections := strings.Split(u.Path, "/") + for _, s := range sections { + if s == ".." { + if len(dotFree) > 0 { + dotFree = dotFree[:len(dotFree)-1] + } + } else if s != "." { + dotFree = append(dotFree, s) + } + lastIsDot = (s == "." || s == "..") + } + // Special case if host does not end with / and new path does not begin with / + u.Path = strings.Join(dotFree, "/") + if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") { + u.Path = "/" + u.Path + } + // Special case if the last segment was a dot, make sure the path ends with a slash + if lastIsDot && !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + } +} + +func removeDirectoryIndex(u *url.URL) { + if len(u.Path) > 0 { + u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1") + } +} + +func removeFragment(u *url.URL) { + u.Fragment = "" +} + +func forceHTTP(u *url.URL) { + if strings.ToLower(u.Scheme) == "https" { + u.Scheme = "http" + } +} + +func removeDuplicateSlashes(u *url.URL) { + if len(u.Path) > 0 { + u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/") + } +} + +func removeWWW(u *url.URL) { + if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") { + u.Host = u.Host[4:] + } +} + +func addWWW(u *url.URL) { + if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") { + u.Host = "www." + u.Host + } +} + +func sortQuery(u *url.URL) { + q := u.Query() + + if len(q) > 0 { + arKeys := make([]string, len(q)) + i := 0 + for k, _ := range q { + arKeys[i] = k + i++ + } + sort.Strings(arKeys) + buf := new(bytes.Buffer) + for _, k := range arKeys { + sort.Strings(q[k]) + for _, v := range q[k] { + if buf.Len() > 0 { + buf.WriteRune('&') + } + buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v))) + } + } + + // Rebuild the raw query string + u.RawQuery = buf.String() + } +} + +func decodeDWORDHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 { + var parts [4]int64 + + dword, _ := strconv.ParseInt(matches[1], 10, 0) + for i, shift := range []uint{24, 16, 8, 0} { + parts[i] = dword >> shift & 0xFF + } + u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2]) + } + } +} + +func decodeOctalHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 { + var parts [4]int64 + + for i := 1; i <= 4; i++ { + parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0) + } + u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5]) + } + } +} + +func decodeHexHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 { + // Conversion is safe because of regex validation + parsed, _ := strconv.ParseInt(matches[1], 16, 0) + // Set host as DWORD (base 10) encoded host + u.Host = fmt.Sprintf("%d%s", parsed, matches[2]) + // The rest is the same as decoding a DWORD host + decodeDWORDHost(u) + } + } +} + +func removeUnncessaryHostDots(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 { + // Trim the leading and trailing dots + u.Host = strings.Trim(matches[1], ".") + if len(matches) > 2 { + u.Host += matches[2] + } + } + } +} + +func removeEmptyPortSeparator(u *url.URL) { + if len(u.Host) > 0 { + u.Host = rxEmptyPort.ReplaceAllString(u.Host, "") + } +} diff --git a/vendor/github.com/PuerkitoBio/purell/purell_test.go b/vendor/github.com/PuerkitoBio/purell/purell_test.go new file mode 100644 index 000000000..a3732e5a3 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/purell_test.go @@ -0,0 +1,768 @@ +package purell + +import ( + "fmt" + "net/url" + "testing" +) + +type testCase struct { + nm string + src string + flgs NormalizationFlags + res string + parsed bool +} + +var ( + cases = [...]*testCase{ + &testCase{ + "LowerScheme", + "HTTP://www.SRC.ca", + FlagLowercaseScheme, + "http://www.SRC.ca", + false, + }, + &testCase{ + "LowerScheme2", + "http://www.SRC.ca", + FlagLowercaseScheme, + "http://www.SRC.ca", + false, + }, + &testCase{ + "LowerHost", + "HTTP://www.SRC.ca/", + FlagLowercaseHost, + "http://www.src.ca/", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "UpperEscapes", + `http://www.whatever.com/Some%aa%20Special%8Ecases/`, + FlagUppercaseEscapes, + "http://www.whatever.com/Some%AA%20Special%8Ecases/", + false, + }, + &testCase{ + "UnnecessaryEscapes", + `http://www.toto.com/%41%42%2E%44/%32%33%52%2D/%5f%7E`, + FlagDecodeUnnecessaryEscapes, + "http://www.toto.com/AB.D/23R-/_~", + false, + }, + &testCase{ + "RemoveDefaultPort", + "HTTP://www.SRC.ca:80/", + FlagRemoveDefaultPort, + "http://www.SRC.ca/", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveDefaultPort2", + "HTTP://www.SRC.ca:80", + FlagRemoveDefaultPort, + "http://www.SRC.ca", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveDefaultPort3", + "HTTP://www.SRC.ca:8080", + FlagRemoveDefaultPort, + "http://www.SRC.ca:8080", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "Safe", + "HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e", + FlagsSafe, + "http://www.src.ca/to%1Ato%8B%EE/OKnowABC~", + false, + }, + &testCase{ + "BothLower", + "HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e", + FlagLowercaseHost | FlagLowercaseScheme, + "http://www.src.ca:80/to%1Ato%8B%EE/OKnowABC~", + false, + }, + &testCase{ + "RemoveTrailingSlash", + "HTTP://www.SRC.ca:80/", + FlagRemoveTrailingSlash, + "http://www.SRC.ca:80", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveTrailingSlash2", + "HTTP://www.SRC.ca:80/toto/titi/", + FlagRemoveTrailingSlash, + "http://www.SRC.ca:80/toto/titi", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveTrailingSlash3", + "HTTP://www.SRC.ca:80/toto/titi/fin/?a=1", + FlagRemoveTrailingSlash, + "http://www.SRC.ca:80/toto/titi/fin?a=1", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "AddTrailingSlash", + "HTTP://www.SRC.ca:80", + FlagAddTrailingSlash, + "http://www.SRC.ca:80/", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "AddTrailingSlash2", + "HTTP://www.SRC.ca:80/toto/titi.html", + FlagAddTrailingSlash, + "http://www.SRC.ca:80/toto/titi.html/", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "AddTrailingSlash3", + "HTTP://www.SRC.ca:80/toto/titi/fin?a=1", + FlagAddTrailingSlash, + "http://www.SRC.ca:80/toto/titi/fin/?a=1", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveDotSegments", + "HTTP://root/a/b/./../../c/", + FlagRemoveDotSegments, + "http://root/c/", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveDotSegments2", + "HTTP://root/../a/b/./../c/../d", + FlagRemoveDotSegments, + "http://root/a/d", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "UsuallySafe", + "HTTP://www.SRC.ca:80/to%1ato%8b%ee/./c/d/../OKnow%41%42%43%7e/?a=b#test", + FlagsUsuallySafeGreedy, + "http://www.src.ca/to%1Ato%8B%EE/c/OKnowABC~?a=b#test", + false, + }, + &testCase{ + "RemoveDirectoryIndex", + "HTTP://root/a/b/c/default.aspx", + FlagRemoveDirectoryIndex, + "http://root/a/b/c/", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveDirectoryIndex2", + "HTTP://root/a/b/c/default#a=b", + FlagRemoveDirectoryIndex, + "http://root/a/b/c/default#a=b", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "RemoveFragment", + "HTTP://root/a/b/c/default#toto=tata", + FlagRemoveFragment, + "http://root/a/b/c/default", // Since Go1.1, scheme is automatically lowercased + false, + }, + &testCase{ + "ForceHTTP", + "https://root/a/b/c/default#toto=tata", + FlagForceHTTP, + "http://root/a/b/c/default#toto=tata", + false, + }, + &testCase{ + "RemoveDuplicateSlashes", + "https://root/a//b///c////default#toto=tata", + FlagRemoveDuplicateSlashes, + "https://root/a/b/c/default#toto=tata", + false, + }, + &testCase{ + "RemoveDuplicateSlashes2", + "https://root//a//b///c////default#toto=tata", + FlagRemoveDuplicateSlashes, + "https://root/a/b/c/default#toto=tata", + false, + }, + &testCase{ + "RemoveWWW", + "https://www.root/a/b/c/", + FlagRemoveWWW, + "https://root/a/b/c/", + false, + }, + &testCase{ + "RemoveWWW2", + "https://WwW.Root/a/b/c/", + FlagRemoveWWW, + "https://Root/a/b/c/", + false, + }, + &testCase{ + "AddWWW", + "https://Root/a/b/c/", + FlagAddWWW, + "https://www.Root/a/b/c/", + false, + }, + &testCase{ + "SortQuery", + "http://root/toto/?b=4&a=1&c=3&b=2&a=5", + FlagSortQuery, + "http://root/toto/?a=1&a=5&b=2&b=4&c=3", + false, + }, + &testCase{ + "RemoveEmptyQuerySeparator", + "http://root/toto/?", + FlagRemoveEmptyQuerySeparator, + "http://root/toto/", + false, + }, + &testCase{ + "Unsafe", + "HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid", + FlagsUnsafeGreedy, + "http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3", + false, + }, + &testCase{ + "Safe2", + "HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid", + FlagsSafe, + "https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid", + false, + }, + &testCase{ + "UsuallySafe2", + "HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid", + FlagsUsuallySafeGreedy, + "https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid", + false, + }, + &testCase{ + "AddTrailingSlashBug", + "http://src.ca/", + FlagsAllNonGreedy, + "http://www.src.ca/", + false, + }, + &testCase{ + "SourceModified", + "HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid", + FlagsUnsafeGreedy, + "http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3", + true, + }, + &testCase{ + "IPv6-1", + "http://[2001:db8:1f70::999:de8:7648:6e8]/test", + FlagsSafe | FlagRemoveDotSegments, + "http://[2001:db8:1f70::999:de8:7648:6e8]/test", + false, + }, + &testCase{ + "IPv6-2", + "http://[::ffff:192.168.1.1]/test", + FlagsSafe | FlagRemoveDotSegments, + "http://[::ffff:192.168.1.1]/test", + false, + }, + &testCase{ + "IPv6-3", + "http://[::ffff:192.168.1.1]:80/test", + FlagsSafe | FlagRemoveDotSegments, + "http://[::ffff:192.168.1.1]/test", + false, + }, + &testCase{ + "IPv6-4", + "htTps://[::fFff:192.168.1.1]:443/test", + FlagsSafe | FlagRemoveDotSegments, + "https://[::ffff:192.168.1.1]/test", + false, + }, + &testCase{ + "FTP", + "ftp://user:pass@ftp.foo.net/foo/bar", + FlagsSafe | FlagRemoveDotSegments, + "ftp://user:pass@ftp.foo.net/foo/bar", + false, + }, + &testCase{ + "Standard-1", + "http://www.foo.com:80/foo", + FlagsSafe | FlagRemoveDotSegments, + "http://www.foo.com/foo", + false, + }, + &testCase{ + "Standard-2", + "http://www.foo.com:8000/foo", + FlagsSafe | FlagRemoveDotSegments, + "http://www.foo.com:8000/foo", + false, + }, + &testCase{ + "Standard-3", + "http://www.foo.com/%7ebar", + FlagsSafe | FlagRemoveDotSegments, + "http://www.foo.com/~bar", + false, + }, + &testCase{ + "Standard-4", + "http://www.foo.com/%7Ebar", + FlagsSafe | FlagRemoveDotSegments, + "http://www.foo.com/~bar", + false, + }, + &testCase{ + "Standard-5", + "http://USER:pass@www.Example.COM/foo/bar", + FlagsSafe | FlagRemoveDotSegments, + "http://USER:pass@www.example.com/foo/bar", + false, + }, + &testCase{ + "Standard-6", + "http://test.example/?a=%26&b=1", + FlagsSafe | FlagRemoveDotSegments, + "http://test.example/?a=%26&b=1", + false, + }, + &testCase{ + "Standard-7", + "http://test.example/%25/?p=%20val%20%25", + FlagsSafe | FlagRemoveDotSegments, + "http://test.example/%25/?p=%20val%20%25", + false, + }, + &testCase{ + "Standard-8", + "http://test.example/path/with a%20space+/", + FlagsSafe | FlagRemoveDotSegments, + "http://test.example/path/with%20a%20space+/", + false, + }, + &testCase{ + "Standard-9", + "http://test.example/?", + FlagsSafe | FlagRemoveDotSegments, + "http://test.example/", + false, + }, + &testCase{ + "Standard-10", + "http://a.COM/path/?b&a", + FlagsSafe | FlagRemoveDotSegments, + "http://a.com/path/?b&a", + false, + }, + &testCase{ + "StandardCasesAddTrailingSlash", + "http://test.example?", + FlagsSafe | FlagAddTrailingSlash, + "http://test.example/", + false, + }, + &testCase{ + "OctalIP-1", + "http://0123.011.0.4/", + FlagsSafe | FlagDecodeOctalHost, + "http://0123.011.0.4/", + false, + }, + &testCase{ + "OctalIP-2", + "http://0102.0146.07.0223/", + FlagsSafe | FlagDecodeOctalHost, + "http://66.102.7.147/", + false, + }, + &testCase{ + "OctalIP-3", + "http://0102.0146.07.0223.:23/", + FlagsSafe | FlagDecodeOctalHost, + "http://66.102.7.147.:23/", + false, + }, + &testCase{ + "OctalIP-4", + "http://USER:pass@0102.0146.07.0223../", + FlagsSafe | FlagDecodeOctalHost, + "http://USER:pass@66.102.7.147../", + false, + }, + &testCase{ + "DWORDIP-1", + "http://123.1113982867/", + FlagsSafe | FlagDecodeDWORDHost, + "http://123.1113982867/", + false, + }, + &testCase{ + "DWORDIP-2", + "http://1113982867/", + FlagsSafe | FlagDecodeDWORDHost, + "http://66.102.7.147/", + false, + }, + &testCase{ + "DWORDIP-3", + "http://1113982867.:23/", + FlagsSafe | FlagDecodeDWORDHost, + "http://66.102.7.147.:23/", + false, + }, + &testCase{ + "DWORDIP-4", + "http://USER:pass@1113982867../", + FlagsSafe | FlagDecodeDWORDHost, + "http://USER:pass@66.102.7.147../", + false, + }, + &testCase{ + "HexIP-1", + "http://0x123.1113982867/", + FlagsSafe | FlagDecodeHexHost, + "http://0x123.1113982867/", + false, + }, + &testCase{ + "HexIP-2", + "http://0x42660793/", + FlagsSafe | FlagDecodeHexHost, + "http://66.102.7.147/", + false, + }, + &testCase{ + "HexIP-3", + "http://0x42660793.:23/", + FlagsSafe | FlagDecodeHexHost, + "http://66.102.7.147.:23/", + false, + }, + &testCase{ + "HexIP-4", + "http://USER:pass@0x42660793../", + FlagsSafe | FlagDecodeHexHost, + "http://USER:pass@66.102.7.147../", + false, + }, + &testCase{ + "UnnecessaryHostDots-1", + "http://.www.foo.com../foo/bar.html", + FlagsSafe | FlagRemoveUnnecessaryHostDots, + "http://www.foo.com/foo/bar.html", + false, + }, + &testCase{ + "UnnecessaryHostDots-2", + "http://www.foo.com./foo/bar.html", + FlagsSafe | FlagRemoveUnnecessaryHostDots, + "http://www.foo.com/foo/bar.html", + false, + }, + &testCase{ + "UnnecessaryHostDots-3", + "http://www.foo.com.:81/foo", + FlagsSafe | FlagRemoveUnnecessaryHostDots, + "http://www.foo.com:81/foo", + false, + }, + &testCase{ + "UnnecessaryHostDots-4", + "http://www.example.com./", + FlagsSafe | FlagRemoveUnnecessaryHostDots, + "http://www.example.com/", + false, + }, + &testCase{ + "EmptyPort-1", + "http://www.thedraymin.co.uk:/main/?p=308", + FlagsSafe | FlagRemoveEmptyPortSeparator, + "http://www.thedraymin.co.uk/main/?p=308", + false, + }, + &testCase{ + "EmptyPort-2", + "http://www.src.ca:", + FlagsSafe | FlagRemoveEmptyPortSeparator, + "http://www.src.ca", + false, + }, + &testCase{ + "Slashes-1", + "http://test.example/foo/bar/.", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/bar/", + false, + }, + &testCase{ + "Slashes-2", + "http://test.example/foo/bar/./", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/bar/", + false, + }, + &testCase{ + "Slashes-3", + "http://test.example/foo/bar/..", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/", + false, + }, + &testCase{ + "Slashes-4", + "http://test.example/foo/bar/../", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/", + false, + }, + &testCase{ + "Slashes-5", + "http://test.example/foo/bar/../baz", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/baz", + false, + }, + &testCase{ + "Slashes-6", + "http://test.example/foo/bar/../..", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/", + false, + }, + &testCase{ + "Slashes-7", + "http://test.example/foo/bar/../../", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/", + false, + }, + &testCase{ + "Slashes-8", + "http://test.example/foo/bar/../../baz", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/baz", + false, + }, + &testCase{ + "Slashes-9", + "http://test.example/foo/bar/../../../baz", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/baz", + false, + }, + &testCase{ + "Slashes-10", + "http://test.example/foo/bar/../../../../baz", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/baz", + false, + }, + &testCase{ + "Slashes-11", + "http://test.example/./foo", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo", + false, + }, + &testCase{ + "Slashes-12", + "http://test.example/../foo", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo", + false, + }, + &testCase{ + "Slashes-13", + "http://test.example/foo.", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo.", + false, + }, + &testCase{ + "Slashes-14", + "http://test.example/.foo", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/.foo", + false, + }, + &testCase{ + "Slashes-15", + "http://test.example/foo..", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo..", + false, + }, + &testCase{ + "Slashes-16", + "http://test.example/..foo", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/..foo", + false, + }, + &testCase{ + "Slashes-17", + "http://test.example/./../foo", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo", + false, + }, + &testCase{ + "Slashes-18", + "http://test.example/./foo/.", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/", + false, + }, + &testCase{ + "Slashes-19", + "http://test.example/foo/./bar", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/bar", + false, + }, + &testCase{ + "Slashes-20", + "http://test.example/foo/../bar", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/bar", + false, + }, + &testCase{ + "Slashes-21", + "http://test.example/foo//", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/", + false, + }, + &testCase{ + "Slashes-22", + "http://test.example/foo///bar//", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "http://test.example/foo/bar/", + false, + }, + &testCase{ + "Relative", + "foo/bar", + FlagsAllGreedy, + "foo/bar", + false, + }, + &testCase{ + "Relative-1", + "./../foo", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "foo", + false, + }, + &testCase{ + "Relative-2", + "./foo/bar/../baz/../bang/..", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "foo/", + false, + }, + &testCase{ + "Relative-3", + "foo///bar//", + FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes, + "foo/bar/", + false, + }, + &testCase{ + "Relative-4", + "www.youtube.com", + FlagsUsuallySafeGreedy, + "www.youtube.com", + false, + }, + /*&testCase{ + "UrlNorm-5", + "http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3", + FlagsSafe | FlagRemoveDotSegments, + "http://ja.wikipedia.org/wiki/\xe3\x82\xad\xe3\x83\xa3\xe3\x82\xbf\xe3\x83\x94\xe3\x83\xa9\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\x91\xe3\x83\xb3", + false, + }, + &testCase{ + "UrlNorm-1", + "http://test.example/?a=%e3%82%82%26", + FlagsAllGreedy, + "http://test.example/?a=\xe3\x82\x82%26", + false, + },*/ + } +) + +func TestRunner(t *testing.T) { + for _, tc := range cases { + runCase(tc, t) + } +} + +func runCase(tc *testCase, t *testing.T) { + t.Logf("running %s...", tc.nm) + if tc.parsed { + u, e := url.Parse(tc.src) + if e != nil { + t.Errorf("%s - FAIL : %s", tc.nm, e) + return + } else { + NormalizeURL(u, tc.flgs) + if s := u.String(); s != tc.res { + t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s) + } + } + } else { + if s, e := NormalizeURLString(tc.src, tc.flgs); e != nil { + t.Errorf("%s - FAIL : %s", tc.nm, e) + } else if s != tc.res { + t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s) + } + } +} + +func TestDecodeUnnecessaryEscapesAll(t *testing.T) { + var url = "http://host/" + + for i := 0; i < 256; i++ { + url += fmt.Sprintf("%%%02x", i) + } + if s, e := NormalizeURLString(url, FlagDecodeUnnecessaryEscapes); e != nil { + t.Fatalf("Got error %s", e.Error()) + } else { + const want = "http://host/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22%23$%25&'()*+,-./0123456789:;%3C=%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF" + if s != want { + t.Errorf("DecodeUnnecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s) + } + } +} + +func TestEncodeNecessaryEscapesAll(t *testing.T) { + var url = "http://host/" + + for i := 0; i < 256; i++ { + if i != 0x25 { + url += string(i) + } + } + if s, e := NormalizeURLString(url, FlagEncodeNecessaryEscapes); e != nil { + t.Fatalf("Got error %s", e.Error()) + } else { + const want = "http://host/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22#$&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%C2%80%C2%81%C2%82%C2%83%C2%84%C2%85%C2%86%C2%87%C2%88%C2%89%C2%8A%C2%8B%C2%8C%C2%8D%C2%8E%C2%8F%C2%90%C2%91%C2%92%C2%93%C2%94%C2%95%C2%96%C2%97%C2%98%C2%99%C2%9A%C2%9B%C2%9C%C2%9D%C2%9E%C2%9F%C2%A0%C2%A1%C2%A2%C2%A3%C2%A4%C2%A5%C2%A6%C2%A7%C2%A8%C2%A9%C2%AA%C2%AB%C2%AC%C2%AD%C2%AE%C2%AF%C2%B0%C2%B1%C2%B2%C2%B3%C2%B4%C2%B5%C2%B6%C2%B7%C2%B8%C2%B9%C2%BA%C2%BB%C2%BC%C2%BD%C2%BE%C2%BF%C3%80%C3%81%C3%82%C3%83%C3%84%C3%85%C3%86%C3%87%C3%88%C3%89%C3%8A%C3%8B%C3%8C%C3%8D%C3%8E%C3%8F%C3%90%C3%91%C3%92%C3%93%C3%94%C3%95%C3%96%C3%97%C3%98%C3%99%C3%9A%C3%9B%C3%9C%C3%9D%C3%9E%C3%9F%C3%A0%C3%A1%C3%A2%C3%A3%C3%A4%C3%A5%C3%A6%C3%A7%C3%A8%C3%A9%C3%AA%C3%AB%C3%AC%C3%AD%C3%AE%C3%AF%C3%B0%C3%B1%C3%B2%C3%B3%C3%B4%C3%B5%C3%B6%C3%B7%C3%B8%C3%B9%C3%BA%C3%BB%C3%BC%C3%BD%C3%BE%C3%BF" + if s != want { + t.Errorf("EncodeNecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s) + } + } +} diff --git a/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go b/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go new file mode 100644 index 000000000..a598fe928 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go @@ -0,0 +1,52 @@ +package purell + +import ( + "testing" +) + +// Test cases merged from PR #1 +// Originally from https://github.com/jehiah/urlnorm/blob/master/test_urlnorm.py + +func assertMap(t *testing.T, cases map[string]string, f NormalizationFlags) { + for bad, good := range cases { + s, e := NormalizeURLString(bad, f) + if e != nil { + t.Errorf("%s normalizing %v to %v", e.Error(), bad, good) + } else { + if s != good { + t.Errorf("source: %v expected: %v got: %v", bad, good, s) + } + } + } +} + +// This tests normalization to a unicode representation +// precent escapes for unreserved values are unescaped to their unicode value +// tests normalization to idna domains +// test ip word handling, ipv6 address handling, and trailing domain periods +// in general, this matches google chromes unescaping for things in the address bar. +// spaces are converted to '+' (perhaphs controversial) +// http://code.google.com/p/google-url/ probably is another good reference for this approach +func TestUrlnorm(t *testing.T) { + testcases := map[string]string{ + "http://test.example/?a=%e3%82%82%26": "http://test.example/?a=%e3%82%82%26", + //"http://test.example/?a=%e3%82%82%26": "http://test.example/?a=\xe3\x82\x82%26", //should return a unicode character + "http://s.xn--q-bga.DE/": "http://s.xn--q-bga.de/", //should be in idna format + "http://XBLA\u306eXbox.com": "http://xn--xblaxbox-jf4g.com", //test utf8 and unicode + "http://президент.рф": "http://xn--d1abbgf6aiiy.xn--p1ai", + "http://ПРЕЗИДЕНТ.РФ": "http://xn--d1abbgf6aiiy.xn--p1ai", + "http://\u00e9.com": "http://xn--9ca.com", + "http://e\u0301.com": "http://xn--9ca.com", + "http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3": "http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3", + //"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3": "http://ja.wikipedia.org/wiki/\xe3\x82\xad\xe3\x83\xa3\xe3\x82\xbf\xe3\x83\x94\xe3\x83\xa9\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\x91\xe3\x83\xb3", + + "http://test.example/\xe3\x82\xad": "http://test.example/%E3%82%AD", + //"http://test.example/\xe3\x82\xad": "http://test.example/\xe3\x82\xad", + "http://test.example/?p=%23val#test-%23-val%25": "http://test.example/?p=%23val#test-%23-val%25", //check that %23 (#) is not escaped where it shouldn't be + + "http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n": "http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n", + //"http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n": "http://test.domain/I\xc3\xb1t\xc3\xabrn\xc3\xa2ti\xc3\xb4n\xef\xbf\xbdliz\xc3\xa6ti\xc3\xb8n", + } + + assertMap(t, testcases, FlagsSafe|FlagRemoveDotSegments) +} diff --git a/vendor/github.com/PuerkitoBio/urlesc/.travis.yml b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml new file mode 100644 index 000000000..478630e50 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.4 + - tip + +install: + - go build . + +script: + - go test -v diff --git a/vendor/github.com/PuerkitoBio/urlesc/LICENSE b/vendor/github.com/PuerkitoBio/urlesc/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/PuerkitoBio/urlesc/README.md b/vendor/github.com/PuerkitoBio/urlesc/README.md new file mode 100644 index 000000000..bebe305e0 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/README.md @@ -0,0 +1,16 @@ +urlesc [![Build Status](https://travis-ci.org/PuerkitoBio/urlesc.png?branch=master)](https://travis-ci.org/PuerkitoBio/urlesc) [![GoDoc](http://godoc.org/github.com/PuerkitoBio/urlesc?status.svg)](http://godoc.org/github.com/PuerkitoBio/urlesc) +====== + +Package urlesc implements query escaping as per RFC 3986. + +It contains some parts of the net/url package, modified so as to allow +some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)). + +## Install + + go get github.com/PuerkitoBio/urlesc + +## License + +Go license (BSD-3-Clause) + diff --git a/vendor/github.com/PuerkitoBio/urlesc/urlesc.go b/vendor/github.com/PuerkitoBio/urlesc/urlesc.go new file mode 100644 index 000000000..1b8462459 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/urlesc.go @@ -0,0 +1,180 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package urlesc implements query escaping as per RFC 3986. +// It contains some parts of the net/url package, modified so as to allow +// some reserved characters incorrectly escaped by net/url. +// See https://github.com/golang/go/issues/5684 +package urlesc + +import ( + "bytes" + "net/url" + "strings" +) + +type encoding int + +const ( + encodePath encoding = 1 + iota + encodeUserPassword + encodeQueryComponent + encodeFragment +) + +// Return true if the specified character should be escaped when +// appearing in a URL string, according to RFC 3986. +func shouldEscape(c byte, mode encoding) bool { + // §2.3 Unreserved characters (alphanum) + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false + } + + switch c { + case '-', '.', '_', '~': // §2.3 Unreserved characters (mark) + return false + + // §2.2 Reserved characters (reserved) + case ':', '/', '?', '#', '[', ']', '@', // gen-delims + '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims + // Different sections of the URL allow a few of + // the reserved characters to appear unescaped. + switch mode { + case encodePath: // §3.3 + // The RFC allows sub-delims and : @. + // '/', '[' and ']' can be used to assign meaning to individual path + // segments. This package only manipulates the path as a whole, + // so we allow those as well. That leaves only ? and # to escape. + return c == '?' || c == '#' + + case encodeUserPassword: // §3.2.1 + // The RFC allows : and sub-delims in + // userinfo. The parsing of userinfo treats ':' as special so we must escape + // all the gen-delims. + return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@' + + case encodeQueryComponent: // §3.4 + // The RFC allows / and ?. + return c != '/' && c != '?' + + case encodeFragment: // §4.1 + // The RFC text is silent but the grammar allows + // everything, so escape nothing but # + return c == '#' + } + } + + // Everything else must be escaped. + return true +} + +// QueryEscape escapes the string so it can be safely placed +// inside a URL query. +func QueryEscape(s string) string { + return escape(s, encodeQueryComponent) +} + +func escape(s string, mode encoding) string { + spaceCount, hexCount := 0, 0 + for i := 0; i < len(s); i++ { + c := s[i] + if shouldEscape(c, mode) { + if c == ' ' && mode == encodeQueryComponent { + spaceCount++ + } else { + hexCount++ + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s + } + + t := make([]byte, len(s)+2*hexCount) + j := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == ' ' && mode == encodeQueryComponent: + t[j] = '+' + j++ + case shouldEscape(c, mode): + t[j] = '%' + t[j+1] = "0123456789ABCDEF"[c>>4] + t[j+2] = "0123456789ABCDEF"[c&15] + j += 3 + default: + t[j] = s[i] + j++ + } + } + return string(t) +} + +var uiReplacer = strings.NewReplacer( + "%21", "!", + "%27", "'", + "%28", "(", + "%29", ")", + "%2A", "*", +) + +// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986. +func unescapeUserinfo(s string) string { + return uiReplacer.Replace(s) +} + +// Escape reassembles the URL into a valid URL string. +// The general form of the result is one of: +// +// scheme:opaque +// scheme://userinfo@host/path?query#fragment +// +// If u.Opaque is non-empty, String uses the first form; +// otherwise it uses the second form. +// +// In the second form, the following rules apply: +// - if u.Scheme is empty, scheme: is omitted. +// - if u.User is nil, userinfo@ is omitted. +// - if u.Host is empty, host/ is omitted. +// - if u.Scheme and u.Host are empty and u.User is nil, +// the entire scheme://userinfo@host/ is omitted. +// - if u.Host is non-empty and u.Path begins with a /, +// the form host/path does not add its own /. +// - if u.RawQuery is empty, ?query is omitted. +// - if u.Fragment is empty, #fragment is omitted. +func Escape(u *url.URL) string { + var buf bytes.Buffer + if u.Scheme != "" { + buf.WriteString(u.Scheme) + buf.WriteByte(':') + } + if u.Opaque != "" { + buf.WriteString(u.Opaque) + } else { + if u.Scheme != "" || u.Host != "" || u.User != nil { + buf.WriteString("//") + if ui := u.User; ui != nil { + buf.WriteString(unescapeUserinfo(ui.String())) + buf.WriteByte('@') + } + if h := u.Host; h != "" { + buf.WriteString(h) + } + } + if u.Path != "" && u.Path[0] != '/' && u.Host != "" { + buf.WriteByte('/') + } + buf.WriteString(escape(u.Path, encodePath)) + } + if u.RawQuery != "" { + buf.WriteByte('?') + buf.WriteString(u.RawQuery) + } + if u.Fragment != "" { + buf.WriteByte('#') + buf.WriteString(escape(u.Fragment, encodeFragment)) + } + return buf.String() +} diff --git a/vendor/github.com/PuerkitoBio/urlesc/urlesc_test.go b/vendor/github.com/PuerkitoBio/urlesc/urlesc_test.go new file mode 100644 index 000000000..45202e1dd --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/urlesc_test.go @@ -0,0 +1,641 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package urlesc + +import ( + "net/url" + "testing" +) + +type URLTest struct { + in string + out *url.URL + roundtrip string // expected result of reserializing the URL; empty means same as "in". +} + +var urltests = []URLTest{ + // no path + { + "http://www.google.com", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + }, + "", + }, + // path + { + "http://www.google.com/", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/", + }, + "", + }, + // path with hex escaping + { + "http://www.google.com/file%20one%26two", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/file one&two", + }, + "http://www.google.com/file%20one&two", + }, + // user + { + "ftp://webmaster@www.google.com/", + &url.URL{ + Scheme: "ftp", + User: url.User("webmaster"), + Host: "www.google.com", + Path: "/", + }, + "", + }, + // escape sequence in username + { + "ftp://john%20doe@www.google.com/", + &url.URL{ + Scheme: "ftp", + User: url.User("john doe"), + Host: "www.google.com", + Path: "/", + }, + "ftp://john%20doe@www.google.com/", + }, + // query + { + "http://www.google.com/?q=go+language", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go+language", + }, + "", + }, + // query with hex escaping: NOT parsed + { + "http://www.google.com/?q=go%20language", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go%20language", + }, + "", + }, + // %20 outside query + { + "http://www.google.com/a%20b?q=c+d", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/a b", + RawQuery: "q=c+d", + }, + "", + }, + // path without leading /, so no parsing + { + "http:www.google.com/?q=go+language", + &url.URL{ + Scheme: "http", + Opaque: "www.google.com/", + RawQuery: "q=go+language", + }, + "http:www.google.com/?q=go+language", + }, + // path without leading /, so no parsing + { + "http:%2f%2fwww.google.com/?q=go+language", + &url.URL{ + Scheme: "http", + Opaque: "%2f%2fwww.google.com/", + RawQuery: "q=go+language", + }, + "http:%2f%2fwww.google.com/?q=go+language", + }, + // non-authority with path + { + "mailto:/webmaster@golang.org", + &url.URL{ + Scheme: "mailto", + Path: "/webmaster@golang.org", + }, + "mailto:///webmaster@golang.org", // unfortunate compromise + }, + // non-authority + { + "mailto:webmaster@golang.org", + &url.URL{ + Scheme: "mailto", + Opaque: "webmaster@golang.org", + }, + "", + }, + // unescaped :// in query should not create a scheme + { + "/foo?query=http://bad", + &url.URL{ + Path: "/foo", + RawQuery: "query=http://bad", + }, + "", + }, + // leading // without scheme should create an authority + { + "//foo", + &url.URL{ + Host: "foo", + }, + "", + }, + // leading // without scheme, with userinfo, path, and query + { + "//user@foo/path?a=b", + &url.URL{ + User: url.User("user"), + Host: "foo", + Path: "/path", + RawQuery: "a=b", + }, + "", + }, + // Three leading slashes isn't an authority, but doesn't return an error. + // (We can't return an error, as this code is also used via + // ServeHTTP -> ReadRequest -> Parse, which is arguably a + // different URL parsing context, but currently shares the + // same codepath) + { + "///threeslashes", + &url.URL{ + Path: "///threeslashes", + }, + "", + }, + { + "http://user:password@google.com", + &url.URL{ + Scheme: "http", + User: url.UserPassword("user", "password"), + Host: "google.com", + }, + "http://user:password@google.com", + }, + // unescaped @ in username should not confuse host + { + "http://j@ne:password@google.com", + &url.URL{ + Scheme: "http", + User: url.UserPassword("j@ne", "password"), + Host: "google.com", + }, + "http://j%40ne:password@google.com", + }, + // unescaped @ in password should not confuse host + { + "http://jane:p@ssword@google.com", + &url.URL{ + Scheme: "http", + User: url.UserPassword("jane", "p@ssword"), + Host: "google.com", + }, + "http://jane:p%40ssword@google.com", + }, + { + "http://j@ne:password@google.com/p@th?q=@go", + &url.URL{ + Scheme: "http", + User: url.UserPassword("j@ne", "password"), + Host: "google.com", + Path: "/p@th", + RawQuery: "q=@go", + }, + "http://j%40ne:password@google.com/p@th?q=@go", + }, + { + "http://www.google.com/?q=go+language#foo", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo", + }, + "", + }, + { + "http://www.google.com/?q=go+language#foo%26bar", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo&bar", + }, + "http://www.google.com/?q=go+language#foo&bar", + }, + { + "file:///home/adg/rabbits", + &url.URL{ + Scheme: "file", + Host: "", + Path: "/home/adg/rabbits", + }, + "file:///home/adg/rabbits", + }, + // "Windows" paths are no exception to the rule. + // See golang.org/issue/6027, especially comment #9. + { + "file:///C:/FooBar/Baz.txt", + &url.URL{ + Scheme: "file", + Host: "", + Path: "/C:/FooBar/Baz.txt", + }, + "file:///C:/FooBar/Baz.txt", + }, + // case-insensitive scheme + { + "MaIlTo:webmaster@golang.org", + &url.URL{ + Scheme: "mailto", + Opaque: "webmaster@golang.org", + }, + "mailto:webmaster@golang.org", + }, + // Relative path + { + "a/b/c", + &url.URL{ + Path: "a/b/c", + }, + "a/b/c", + }, + // escaped '?' in username and password + { + "http://%3Fam:pa%3Fsword@google.com", + &url.URL{ + Scheme: "http", + User: url.UserPassword("?am", "pa?sword"), + Host: "google.com", + }, + "", + }, + // escaped '?' and '#' in path + { + "http://example.com/%3F%23", + &url.URL{ + Scheme: "http", + Host: "example.com", + Path: "?#", + }, + "", + }, + // unescaped [ ] ! ' ( ) * in path + { + "http://example.com/[]!'()*", + &url.URL{ + Scheme: "http", + Host: "example.com", + Path: "[]!'()*", + }, + "http://example.com/[]!'()*", + }, + // escaped : / ? # [ ] @ in username and password + { + "http://%3A%2F%3F:%23%5B%5D%40@example.com", + &url.URL{ + Scheme: "http", + User: url.UserPassword(":/?", "#[]@"), + Host: "example.com", + }, + "", + }, + // unescaped ! $ & ' ( ) * + , ; = in username and password + { + "http://!$&'():*+,;=@example.com", + &url.URL{ + Scheme: "http", + User: url.UserPassword("!$&'()", "*+,;="), + Host: "example.com", + }, + "", + }, + // unescaped = : / . ? = in query component + { + "http://example.com/?q=http://google.com/?q=", + &url.URL{ + Scheme: "http", + Host: "example.com", + Path: "/", + RawQuery: "q=http://google.com/?q=", + }, + "", + }, + // unescaped : / ? [ ] @ ! $ & ' ( ) * + , ; = in fragment + { + "http://example.com/#:/?%23[]@!$&'()*+,;=", + &url.URL{ + Scheme: "http", + Host: "example.com", + Path: "/", + Fragment: ":/?#[]@!$&'()*+,;=", + }, + "", + }, +} + +func DoTestString(t *testing.T, parse func(string) (*url.URL, error), name string, tests []URLTest) { + for _, tt := range tests { + u, err := parse(tt.in) + if err != nil { + t.Errorf("%s(%q) returned error %s", name, tt.in, err) + continue + } + expected := tt.in + if len(tt.roundtrip) > 0 { + expected = tt.roundtrip + } + s := Escape(u) + if s != expected { + t.Errorf("Escape(%s(%q)) == %q (expected %q)", name, tt.in, s, expected) + } + } +} + +func TestURLString(t *testing.T) { + DoTestString(t, url.Parse, "Parse", urltests) + + // no leading slash on path should prepend + // slash on String() call + noslash := URLTest{ + "http://www.google.com/search", + &url.URL{ + Scheme: "http", + Host: "www.google.com", + Path: "search", + }, + "", + } + s := Escape(noslash.out) + if s != noslash.in { + t.Errorf("Expected %s; go %s", noslash.in, s) + } +} + +type EscapeTest struct { + in string + out string + err error +} + +var escapeTests = []EscapeTest{ + { + "", + "", + nil, + }, + { + "abc", + "abc", + nil, + }, + { + "one two", + "one+two", + nil, + }, + { + "10%", + "10%25", + nil, + }, + { + " ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;", + "+?%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A/%40%24%27%28%29%2A%2C%3B", + nil, + }, +} + +func TestEscape(t *testing.T) { + for _, tt := range escapeTests { + actual := QueryEscape(tt.in) + if tt.out != actual { + t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out) + } + + // for bonus points, verify that escape:unescape is an identity. + roundtrip, err := url.QueryUnescape(actual) + if roundtrip != tt.in || err != nil { + t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]") + } + } +} + +var resolveReferenceTests = []struct { + base, rel, expected string +}{ + // Absolute URL references + {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"}, + {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"}, + {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"}, + + // Path-absolute references + {"http://foo.com/bar", "/baz", "http://foo.com/baz"}, + {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"}, + {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"}, + + // Scheme-relative + {"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"}, + + // Path-relative references: + + // ... current directory + {"http://foo.com", ".", "http://foo.com/"}, + {"http://foo.com/bar", ".", "http://foo.com/"}, + {"http://foo.com/bar/", ".", "http://foo.com/bar/"}, + + // ... going down + {"http://foo.com", "bar", "http://foo.com/bar"}, + {"http://foo.com/", "bar", "http://foo.com/bar"}, + {"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"}, + + // ... going up + {"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"}, + {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"}, + {"http://foo.com/bar", "..", "http://foo.com/"}, + {"http://foo.com/bar/baz", "./..", "http://foo.com/"}, + // ".." in the middle (issue 3560) + {"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot/"}, + + // Remove any dot-segments prior to forming the target URI. + // http://tools.ietf.org/html/rfc3986#section-5.2.4 + {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/baz"}, + + // Triple dot isn't special + {"http://foo.com/bar", "...", "http://foo.com/..."}, + + // Fragment + {"http://foo.com/bar", ".#frag", "http://foo.com/#frag"}, + + // RFC 3986: Normal Examples + // http://tools.ietf.org/html/rfc3986#section-5.4.1 + {"http://a/b/c/d;p?q", "g:h", "g:h"}, + {"http://a/b/c/d;p?q", "g", "http://a/b/c/g"}, + {"http://a/b/c/d;p?q", "./g", "http://a/b/c/g"}, + {"http://a/b/c/d;p?q", "g/", "http://a/b/c/g/"}, + {"http://a/b/c/d;p?q", "/g", "http://a/g"}, + {"http://a/b/c/d;p?q", "//g", "http://g"}, + {"http://a/b/c/d;p?q", "?y", "http://a/b/c/d;p?y"}, + {"http://a/b/c/d;p?q", "g?y", "http://a/b/c/g?y"}, + {"http://a/b/c/d;p?q", "#s", "http://a/b/c/d;p?q#s"}, + {"http://a/b/c/d;p?q", "g#s", "http://a/b/c/g#s"}, + {"http://a/b/c/d;p?q", "g?y#s", "http://a/b/c/g?y#s"}, + {"http://a/b/c/d;p?q", ";x", "http://a/b/c/;x"}, + {"http://a/b/c/d;p?q", "g;x", "http://a/b/c/g;x"}, + {"http://a/b/c/d;p?q", "g;x?y#s", "http://a/b/c/g;x?y#s"}, + {"http://a/b/c/d;p?q", "", "http://a/b/c/d;p?q"}, + {"http://a/b/c/d;p?q", ".", "http://a/b/c/"}, + {"http://a/b/c/d;p?q", "./", "http://a/b/c/"}, + {"http://a/b/c/d;p?q", "..", "http://a/b/"}, + {"http://a/b/c/d;p?q", "../", "http://a/b/"}, + {"http://a/b/c/d;p?q", "../g", "http://a/b/g"}, + {"http://a/b/c/d;p?q", "../..", "http://a/"}, + {"http://a/b/c/d;p?q", "../../", "http://a/"}, + {"http://a/b/c/d;p?q", "../../g", "http://a/g"}, + + // RFC 3986: Abnormal Examples + // http://tools.ietf.org/html/rfc3986#section-5.4.2 + {"http://a/b/c/d;p?q", "../../../g", "http://a/g"}, + {"http://a/b/c/d;p?q", "../../../../g", "http://a/g"}, + {"http://a/b/c/d;p?q", "/./g", "http://a/g"}, + {"http://a/b/c/d;p?q", "/../g", "http://a/g"}, + {"http://a/b/c/d;p?q", "g.", "http://a/b/c/g."}, + {"http://a/b/c/d;p?q", ".g", "http://a/b/c/.g"}, + {"http://a/b/c/d;p?q", "g..", "http://a/b/c/g.."}, + {"http://a/b/c/d;p?q", "..g", "http://a/b/c/..g"}, + {"http://a/b/c/d;p?q", "./../g", "http://a/b/g"}, + {"http://a/b/c/d;p?q", "./g/.", "http://a/b/c/g/"}, + {"http://a/b/c/d;p?q", "g/./h", "http://a/b/c/g/h"}, + {"http://a/b/c/d;p?q", "g/../h", "http://a/b/c/h"}, + {"http://a/b/c/d;p?q", "g;x=1/./y", "http://a/b/c/g;x=1/y"}, + {"http://a/b/c/d;p?q", "g;x=1/../y", "http://a/b/c/y"}, + {"http://a/b/c/d;p?q", "g?y/./x", "http://a/b/c/g?y/./x"}, + {"http://a/b/c/d;p?q", "g?y/../x", "http://a/b/c/g?y/../x"}, + {"http://a/b/c/d;p?q", "g#s/./x", "http://a/b/c/g#s/./x"}, + {"http://a/b/c/d;p?q", "g#s/../x", "http://a/b/c/g#s/../x"}, + + // Extras. + {"https://a/b/c/d;p?q", "//g?q", "https://g?q"}, + {"https://a/b/c/d;p?q", "//g#s", "https://g#s"}, + {"https://a/b/c/d;p?q", "//g/d/e/f?y#s", "https://g/d/e/f?y#s"}, + {"https://a/b/c/d;p#s", "?y", "https://a/b/c/d;p?y"}, + {"https://a/b/c/d;p?q#s", "?y", "https://a/b/c/d;p?y"}, +} + +func TestResolveReference(t *testing.T) { + mustParse := func(url_ string) *url.URL { + u, err := url.Parse(url_) + if err != nil { + t.Fatalf("Expected URL to parse: %q, got error: %v", url_, err) + } + return u + } + opaque := &url.URL{Scheme: "scheme", Opaque: "opaque"} + for _, test := range resolveReferenceTests { + base := mustParse(test.base) + rel := mustParse(test.rel) + url := base.ResolveReference(rel) + if Escape(url) != test.expected { + t.Errorf("URL(%q).ResolveReference(%q) == %q, got %q", test.base, test.rel, test.expected, Escape(url)) + } + // Ensure that new instances are returned. + if base == url { + t.Errorf("Expected URL.ResolveReference to return new URL instance.") + } + // Test the convenience wrapper too. + url, err := base.Parse(test.rel) + if err != nil { + t.Errorf("URL(%q).Parse(%q) failed: %v", test.base, test.rel, err) + } else if Escape(url) != test.expected { + t.Errorf("URL(%q).Parse(%q) == %q, got %q", test.base, test.rel, test.expected, Escape(url)) + } else if base == url { + // Ensure that new instances are returned for the wrapper too. + t.Errorf("Expected URL.Parse to return new URL instance.") + } + // Ensure Opaque resets the URL. + url = base.ResolveReference(opaque) + if *url != *opaque { + t.Errorf("ResolveReference failed to resolve opaque URL: want %#v, got %#v", url, opaque) + } + // Test the convenience wrapper with an opaque URL too. + url, err = base.Parse("scheme:opaque") + if err != nil { + t.Errorf(`URL(%q).Parse("scheme:opaque") failed: %v`, test.base, err) + } else if *url != *opaque { + t.Errorf("Parse failed to resolve opaque URL: want %#v, got %#v", url, opaque) + } else if base == url { + // Ensure that new instances are returned, again. + t.Errorf("Expected URL.Parse to return new URL instance.") + } + } +} + +type shouldEscapeTest struct { + in byte + mode encoding + escape bool +} + +var shouldEscapeTests = []shouldEscapeTest{ + // Unreserved characters (§2.3) + {'a', encodePath, false}, + {'a', encodeUserPassword, false}, + {'a', encodeQueryComponent, false}, + {'a', encodeFragment, false}, + {'z', encodePath, false}, + {'A', encodePath, false}, + {'Z', encodePath, false}, + {'0', encodePath, false}, + {'9', encodePath, false}, + {'-', encodePath, false}, + {'-', encodeUserPassword, false}, + {'-', encodeQueryComponent, false}, + {'-', encodeFragment, false}, + {'.', encodePath, false}, + {'_', encodePath, false}, + {'~', encodePath, false}, + + // User information (§3.2.1) + {':', encodeUserPassword, true}, + {'/', encodeUserPassword, true}, + {'?', encodeUserPassword, true}, + {'@', encodeUserPassword, true}, + {'$', encodeUserPassword, false}, + {'&', encodeUserPassword, false}, + {'+', encodeUserPassword, false}, + {',', encodeUserPassword, false}, + {';', encodeUserPassword, false}, + {'=', encodeUserPassword, false}, +} + +func TestShouldEscape(t *testing.T) { + for _, tt := range shouldEscapeTests { + if shouldEscape(tt.in, tt.mode) != tt.escape { + t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape) + } + } +} diff --git a/vendor/github.com/coreos/etcd/.dockerignore b/vendor/github.com/coreos/etcd/.dockerignore new file mode 100644 index 000000000..6b8710a71 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.dockerignore @@ -0,0 +1 @@ +.git diff --git a/vendor/github.com/coreos/etcd/.github/ISSUE_TEMPLATE.md b/vendor/github.com/coreos/etcd/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..c8c894c26 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,7 @@ +# Bug reporting + +A good bug report has some very specific qualities, so please read over our short document on [reporting bugs][report_bugs] before submitting a bug report. + +To ask a question, go ahead and ignore this. + +[report_bugs]: https://github.com/coreos/etcd/blob/master/Documentation/reporting_bugs.md diff --git a/vendor/github.com/coreos/etcd/.github/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/coreos/etcd/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..4fa34e9b3 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +# Contributing guidelines + +Please read our [contribution workflow][contributing] before submitting a pull request. + +[contributing]: https://github.com/coreos/etcd/blob/master/CONTRIBUTING.md#contribution-flow diff --git a/vendor/github.com/coreos/etcd/.gitignore b/vendor/github.com/coreos/etcd/.gitignore new file mode 100644 index 000000000..1a68387a7 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.gitignore @@ -0,0 +1,22 @@ +/agent-* +/coverage +/covdir +/gopath +/gopath.proto +/go-bindata +/release +/machine* +/bin +.Dockerfile-test +.vagrant +*.etcd +*.log +/etcd +*.swp +/hack/insta-discovery/.env +*.test +tools/functional-tester/docker/bin +hack/scripts-dev/docker-dns/.Dockerfile +hack/scripts-dev/docker-dns-srv/.Dockerfile +hack/tls-setup/certs +.idea diff --git a/vendor/github.com/coreos/etcd/.godir b/vendor/github.com/coreos/etcd/.godir new file mode 100644 index 000000000..00ff6aa80 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.godir @@ -0,0 +1 @@ +github.com/coreos/etcd diff --git a/vendor/github.com/coreos/etcd/.header b/vendor/github.com/coreos/etcd/.header new file mode 100644 index 000000000..0446af6d8 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.header @@ -0,0 +1,13 @@ +// Copyright 2016 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. diff --git a/vendor/github.com/coreos/etcd/.semaphore.sh b/vendor/github.com/coreos/etcd/.semaphore.sh new file mode 100755 index 000000000..1a6c85a62 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.semaphore.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +TEST_SUFFIX=$(date +%s | base64 | head -c 15) + +TEST_OPTS="RELEASE_TEST=y INTEGRATION=y PASSES='build unit release integration_e2e functional' MANUAL_VER=v3.2.11" +if [ "$TEST_ARCH" == "386" ]; then + TEST_OPTS="GOARCH=386 PASSES='build unit integration_e2e'" +fi + +docker run \ + --rm \ + --volume=`pwd`:/go/src/github.com/coreos/etcd \ + gcr.io/etcd-development/etcd-test:go1.8.5 \ + /bin/bash -c "${TEST_OPTS} ./test 2>&1 | tee test-${TEST_SUFFIX}.log" + +! egrep "(--- FAIL:|panic: test timed out|appears to have leaked)" -B50 -A10 test-${TEST_SUFFIX}.log diff --git a/vendor/github.com/coreos/etcd/.travis.yml b/vendor/github.com/coreos/etcd/.travis.yml new file mode 100644 index 000000000..00f2b8102 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.travis.yml @@ -0,0 +1,88 @@ +language: go +go_import_path: github.com/coreos/etcd + +sudo: required + +services: docker + +go: +- 1.8.5 +- tip + +notifications: + on_success: never + on_failure: never + +env: + matrix: + - TARGET=amd64 + - TARGET=amd64-go-tip + - TARGET=darwin-amd64 + - TARGET=windows-amd64 + - TARGET=arm64 + - TARGET=arm + - TARGET=386 + - TARGET=ppc64le + +matrix: + fast_finish: true + allow_failures: + - go: tip + env: TARGET=amd64-go-tip + exclude: + - go: 1.8.5 + env: TARGET=amd64-go-tip + - go: tip + env: TARGET=amd64 + - go: tip + env: TARGET=darwin-amd64 + - go: tip + env: TARGET=windows-amd64 + - go: tip + env: TARGET=arm + - go: tip + env: TARGET=arm64 + - go: tip + env: TARGET=386 + - go: tip + env: TARGET=ppc64le + +before_install: +- docker pull gcr.io/etcd-development/etcd-test:go1.8.5 + +install: +- pushd cmd/etcd && go get -t -v ./... && popd + +script: + - > + case "${TARGET}" in + amd64) + docker run --rm \ + --volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.8.5 \ + /bin/bash -c "GOARCH=amd64 ./test" + ;; + amd64-go-tip) + GOARCH=amd64 ./test + ;; + darwin-amd64) + docker run --rm \ + --volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.8.5 \ + /bin/bash -c "GO_BUILD_FLAGS='-a -v' GOOS=darwin GOARCH=amd64 ./build" + ;; + windows-amd64) + docker run --rm \ + --volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.8.5 \ + /bin/bash -c "GO_BUILD_FLAGS='-a -v' GOOS=windows GOARCH=amd64 ./build" + ;; + 386) + docker run --rm \ + --volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.8.5 \ + /bin/bash -c "GOARCH=386 PASSES='build unit' ./test" + ;; + *) + # test building out of gopath + docker run --rm \ + --volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.8.5 \ + /bin/bash -c "GO_BUILD_FLAGS='-a -v' GOARCH='${TARGET}' ./build" + ;; + esac diff --git a/vendor/github.com/coreos/etcd/CONTRIBUTING.md b/vendor/github.com/coreos/etcd/CONTRIBUTING.md new file mode 100644 index 000000000..635f73a30 --- /dev/null +++ b/vendor/github.com/coreos/etcd/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# How to contribute + +etcd is Apache 2.0 licensed and accepts contributions via GitHub pull requests. This document outlines some of the conventions on commit message formatting, contact points for developers, and other resources to help get contributions into etcd. + +# Email and chat + +- Email: [etcd-dev](https://groups.google.com/forum/?hl=en#!forum/etcd-dev) +- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org + +## Getting started + +- Fork the repository on GitHub +- Read the README.md for build instructions + +## Reporting bugs and creating issues + +Reporting bugs is one of the best ways to contribute. However, a good bug report has some very specific qualities, so please read over our short document on [reporting bugs](https://github.com/coreos/etcd/blob/master/Documentation/reporting_bugs.md) before submitting a bug report. This document might contain links to known issues, another good reason to take a look there before reporting a bug. + +## Contribution flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where to base the contribution. This is usually master. +- Make commits of logical units. +- Make sure commit messages are in the proper format (see below). +- Push changes in a topic branch to a personal fork of the repository. +- Submit a pull request to coreos/etcd. +- The PR must receive a LGTM from two maintainers found in the MAINTAINERS file. + +Thanks for contributing! + +### Code style + +The coding style suggested by the Golang community is used in etcd. See the [style doc](https://github.com/golang/go/wiki/CodeReviewComments) for details. + +Please follow this style to make etcd easy to review, maintain and develop. + +### Format of the commit message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +scripts: add the test-cluster command + +this uses tmux to setup a test cluster that can easily be killed and started for debugging. + +Fixes #38 +``` + +The format can be described more formally as follows: + +``` +: + + + +