newline battles continue
This commit is contained in:
12
rss/item.go
12
rss/item.go
@@ -95,7 +95,11 @@ func (i *Item) Load(key, ns1 string, ns ...string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return config.Decode(b, i)
|
err = config.Decode(b, i)
|
||||||
|
if err == nil {
|
||||||
|
i.Content = clearBlankLines(i.Content)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (is Items) Len() int {
|
func (is Items) Len() int {
|
||||||
@@ -113,8 +117,10 @@ func (is Items) Swap(i, j int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func clearBlankLines(s string) string {
|
func clearBlankLines(s string) string {
|
||||||
r := regexp.MustCompile(`(?m)^\s*<br>\s*$`)
|
r := regexp.MustCompile(`<br/>`)
|
||||||
s = r.ReplaceAllLiteralString(s, "")
|
s = r.ReplaceAllLiteralString(s, "<br>")
|
||||||
|
r = regexp.MustCompile(`(?m)<br>\s*(<br>\s*)*`)
|
||||||
|
s = r.ReplaceAllLiteralString(s, "<br>")
|
||||||
r = regexp.MustCompile(`(?m)\s\s*`)
|
r = regexp.MustCompile(`(?m)\s\s*`)
|
||||||
return r.ReplaceAllLiteralString(s, "\n")
|
return r.ReplaceAllLiteralString(s, "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,15 +92,61 @@ func TestClearBlankLines(t *testing.T) {
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
<br/><br/>
|
||||||
|
<br/> <br/>
|
||||||
</body>
|
</body>
|
||||||
</html>`,
|
</html>`,
|
||||||
outLines: 8,
|
outLines: 1,
|
||||||
|
},
|
||||||
|
"sample content": {
|
||||||
|
in: `<html>
|
||||||
|
Linux
|
||||||
|
Blog
|
||||||
|
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
|
||||||
|
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
|
||||||
|
<img src="https://www.jeremymorgan.com/images/programming-blog.png" alt="Programming
|
||||||
|
Blog"/><h1><a href="https://www.jeremymorgan.com/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">Jeremy
|
||||||
|
Morgan</a></h1><h2>Mostly
|
||||||
|
Coherent
|
||||||
|
Ramblings
|
||||||
|
of
|
||||||
|
a
|
||||||
|
Silicon
|
||||||
|
Forest
|
||||||
|
Software
|
||||||
|
Developer</h2><br/><br/><br/>
|
||||||
|
<ul><br/>
|
||||||
|
<li>RSS</li><br/>
|
||||||
|
<br/></ul><br/>
|
||||||
|
<br/><br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/><br/>
|
||||||
|
<br/><ul><li><a href="https://www.jeremymorgan.com/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">Home</a></li><li><a href="https://www.jeremymorgan.com/tutorials/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">Tutorials</a></li><li><a href="https://www.jeremymorgan.com/blog/programming/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">Programming</a></li><li><a href="https://www.jeremymorgan.com/blog/raspberry-pi/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">Raspberry
|
||||||
|
Pi</a></li><li><a href="https://www.jeremymorgan.com/blog/linux/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">Linux</a></li><li><a href="https://www.jeremymorgan.com/blog/dotnet/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">.Net</a></li><li><a href="https://github.com/JeremyMorgan" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">My
|
||||||
|
Projects</a></li><li><a href="https://www.jeremymorgan.com/contact/" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">Contact
|
||||||
|
Me</a></li></ul><br/><br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/><br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<h1>Trying
|
||||||
|
Out
|
||||||
|
</html>`,
|
||||||
|
outLines: 6,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, c := range cases {
|
for name, c := range cases {
|
||||||
out := clearBlankLines(c.in)
|
out := clearBlankLines(c.in)
|
||||||
if v := len(strings.Split(out, "\n")); v != c.outLines {
|
cnts := []int{
|
||||||
|
strings.Count(out, "<br>"),
|
||||||
|
strings.Count(out, "<br/>"),
|
||||||
|
}
|
||||||
|
if v := cnts[0] + cnts[1]; v != c.outLines {
|
||||||
t.Errorf("%v: want %v lines, got %v from %q: %q", name, c.outLines, v, c.in, out)
|
t.Errorf("%v: want %v lines, got %v from %q: %q", name, c.outLines, v, c.in, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Executable file
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
513
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Executable file
513
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Executable file
@@ -0,0 +1,513 @@
|
|||||||
|
// Copyright 2014 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package metadata provides access to Google Compute Engine (GCE)
|
||||||
|
// metadata and API service accounts.
|
||||||
|
//
|
||||||
|
// This package is a wrapper around the GCE metadata service,
|
||||||
|
// as documented at https://developers.google.com/compute/docs/metadata.
|
||||||
|
package metadata // import "cloud.google.com/go/compute/metadata"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// metadataIP is the documented metadata server IP address.
|
||||||
|
metadataIP = "169.254.169.254"
|
||||||
|
|
||||||
|
// metadataHostEnv is the environment variable specifying the
|
||||||
|
// GCE metadata hostname. If empty, the default value of
|
||||||
|
// metadataIP ("169.254.169.254") is used instead.
|
||||||
|
// This is variable name is not defined by any spec, as far as
|
||||||
|
// I know; it was made up for the Go package.
|
||||||
|
metadataHostEnv = "GCE_METADATA_HOST"
|
||||||
|
|
||||||
|
userAgent = "gcloud-golang/0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cachedValue struct {
|
||||||
|
k string
|
||||||
|
trim bool
|
||||||
|
mu sync.Mutex
|
||||||
|
v string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
projID = &cachedValue{k: "project/project-id", trim: true}
|
||||||
|
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
|
||||||
|
instID = &cachedValue{k: "instance/id", trim: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultClient = &Client{hc: &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Dial: (&net.Dialer{
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).Dial,
|
||||||
|
ResponseHeaderTimeout: 2 * time.Second,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
subscribeClient = &Client{hc: &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Dial: (&net.Dialer{
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).Dial,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotDefinedError is returned when requested metadata is not defined.
|
||||||
|
//
|
||||||
|
// The underlying string is the suffix after "/computeMetadata/v1/".
|
||||||
|
//
|
||||||
|
// This error is not returned if the value is defined to be the empty
|
||||||
|
// string.
|
||||||
|
type NotDefinedError string
|
||||||
|
|
||||||
|
func (suffix NotDefinedError) Error() string {
|
||||||
|
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cachedValue) get(cl *Client) (v string, err error) {
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.mu.Lock()
|
||||||
|
if c.v != "" {
|
||||||
|
return c.v, nil
|
||||||
|
}
|
||||||
|
if c.trim {
|
||||||
|
v, err = cl.getTrimmed(c.k)
|
||||||
|
} else {
|
||||||
|
v, err = cl.Get(c.k)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
c.v = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
onGCEOnce sync.Once
|
||||||
|
onGCE bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// OnGCE reports whether this process is running on Google Compute Engine.
|
||||||
|
func OnGCE() bool {
|
||||||
|
onGCEOnce.Do(initOnGCE)
|
||||||
|
return onGCE
|
||||||
|
}
|
||||||
|
|
||||||
|
func initOnGCE() {
|
||||||
|
onGCE = testOnGCE()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOnGCE() bool {
|
||||||
|
// The user explicitly said they're on GCE, so trust them.
|
||||||
|
if os.Getenv(metadataHostEnv) != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
resc := make(chan bool, 2)
|
||||||
|
|
||||||
|
// Try two strategies in parallel.
|
||||||
|
// See https://github.com/googleapis/google-cloud-go/issues/194
|
||||||
|
go func() {
|
||||||
|
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
res, err := defaultClient.hc.Do(req.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
resc <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
resc <- res.Header.Get("Metadata-Flavor") == "Google"
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
addrs, err := net.LookupHost("metadata.google.internal")
|
||||||
|
if err != nil || len(addrs) == 0 {
|
||||||
|
resc <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resc <- strsContains(addrs, metadataIP)
|
||||||
|
}()
|
||||||
|
|
||||||
|
tryHarder := systemInfoSuggestsGCE()
|
||||||
|
if tryHarder {
|
||||||
|
res := <-resc
|
||||||
|
if res {
|
||||||
|
// The first strategy succeeded, so let's use it.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Wait for either the DNS or metadata server probe to
|
||||||
|
// contradict the other one and say we are running on
|
||||||
|
// GCE. Give it a lot of time to do so, since the system
|
||||||
|
// info already suggests we're running on a GCE BIOS.
|
||||||
|
timer := time.NewTimer(5 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
select {
|
||||||
|
case res = <-resc:
|
||||||
|
return res
|
||||||
|
case <-timer.C:
|
||||||
|
// Too slow. Who knows what this system is.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no hint from the system info that we're running on
|
||||||
|
// GCE, so use the first probe's result as truth, whether it's
|
||||||
|
// true or false. The goal here is to optimize for speed for
|
||||||
|
// users who are NOT running on GCE. We can't assume that
|
||||||
|
// either a DNS lookup or an HTTP request to a blackholed IP
|
||||||
|
// address is fast. Worst case this should return when the
|
||||||
|
// metaClient's Transport.ResponseHeaderTimeout or
|
||||||
|
// Transport.Dial.Timeout fires (in two seconds).
|
||||||
|
return <-resc
|
||||||
|
}
|
||||||
|
|
||||||
|
// systemInfoSuggestsGCE reports whether the local system (without
|
||||||
|
// doing network requests) suggests that we're running on GCE. If this
|
||||||
|
// returns true, testOnGCE tries a bit harder to reach its metadata
|
||||||
|
// server.
|
||||||
|
func systemInfoSuggestsGCE() bool {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
// We don't have any non-Linux clues available, at least yet.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
|
||||||
|
name := strings.TrimSpace(string(slurp))
|
||||||
|
return name == "Google" || name == "Google Compute Engine"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
|
||||||
|
// ResponseHeaderTimeout).
|
||||||
|
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
||||||
|
return subscribeClient.Subscribe(suffix, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get calls Client.Get on the default client.
|
||||||
|
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
|
||||||
|
|
||||||
|
// ProjectID returns the current instance's project ID string.
|
||||||
|
func ProjectID() (string, error) { return defaultClient.ProjectID() }
|
||||||
|
|
||||||
|
// NumericProjectID returns the current instance's numeric project ID.
|
||||||
|
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
|
||||||
|
|
||||||
|
// InternalIP returns the instance's primary internal IP address.
|
||||||
|
func InternalIP() (string, error) { return defaultClient.InternalIP() }
|
||||||
|
|
||||||
|
// ExternalIP returns the instance's primary external (public) IP address.
|
||||||
|
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
|
||||||
|
|
||||||
|
// Hostname returns the instance's hostname. This will be of the form
|
||||||
|
// "<instanceID>.c.<projID>.internal".
|
||||||
|
func Hostname() (string, error) { return defaultClient.Hostname() }
|
||||||
|
|
||||||
|
// InstanceTags returns the list of user-defined instance tags,
|
||||||
|
// assigned when initially creating a GCE instance.
|
||||||
|
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
|
||||||
|
|
||||||
|
// InstanceID returns the current VM's numeric instance ID.
|
||||||
|
func InstanceID() (string, error) { return defaultClient.InstanceID() }
|
||||||
|
|
||||||
|
// InstanceName returns the current VM's instance ID string.
|
||||||
|
func InstanceName() (string, error) { return defaultClient.InstanceName() }
|
||||||
|
|
||||||
|
// Zone returns the current VM's zone, such as "us-central1-b".
|
||||||
|
func Zone() (string, error) { return defaultClient.Zone() }
|
||||||
|
|
||||||
|
// InstanceAttributes calls Client.InstanceAttributes on the default client.
|
||||||
|
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
|
||||||
|
|
||||||
|
// ProjectAttributes calls Client.ProjectAttributes on the default client.
|
||||||
|
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
|
||||||
|
|
||||||
|
// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
|
||||||
|
func InstanceAttributeValue(attr string) (string, error) {
|
||||||
|
return defaultClient.InstanceAttributeValue(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
|
||||||
|
func ProjectAttributeValue(attr string) (string, error) {
|
||||||
|
return defaultClient.ProjectAttributeValue(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scopes calls Client.Scopes on the default client.
|
||||||
|
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
|
||||||
|
|
||||||
|
func strsContains(ss []string, s string) bool {
|
||||||
|
for _, v := range ss {
|
||||||
|
if v == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Client provides metadata.
|
||||||
|
type Client struct {
|
||||||
|
hc *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
|
||||||
|
// will use the given http.Client instead of the default client.
|
||||||
|
func NewClient(c *http.Client) *Client {
|
||||||
|
return &Client{hc: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getETag returns a value from the metadata service as well as the associated ETag.
|
||||||
|
// This func is otherwise equivalent to Get.
|
||||||
|
func (c *Client) getETag(suffix string) (value, etag string, err error) {
|
||||||
|
// Using a fixed IP makes it very difficult to spoof the metadata service in
|
||||||
|
// a container, which is an important use-case for local testing of cloud
|
||||||
|
// deployments. To enable spoofing of the metadata service, the environment
|
||||||
|
// variable GCE_METADATA_HOST is first inspected to decide where metadata
|
||||||
|
// requests shall go.
|
||||||
|
host := os.Getenv(metadataHostEnv)
|
||||||
|
if host == "" {
|
||||||
|
// Using 169.254.169.254 instead of "metadata" here because Go
|
||||||
|
// binaries built with the "netgo" tag and without cgo won't
|
||||||
|
// know the search suffix for "metadata" is
|
||||||
|
// ".google.internal", and this IP address is documented as
|
||||||
|
// being stable anyway.
|
||||||
|
host = metadataIP
|
||||||
|
}
|
||||||
|
u := "http://" + host + "/computeMetadata/v1/" + suffix
|
||||||
|
req, _ := http.NewRequest("GET", u, nil)
|
||||||
|
req.Header.Set("Metadata-Flavor", "Google")
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
res, err := c.hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode == http.StatusNotFound {
|
||||||
|
return "", "", NotDefinedError(suffix)
|
||||||
|
}
|
||||||
|
all, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
return "", "", &Error{Code: res.StatusCode, Message: string(all)}
|
||||||
|
}
|
||||||
|
return string(all), res.Header.Get("Etag"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a value from the metadata service.
|
||||||
|
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||||
|
//
|
||||||
|
// If the GCE_METADATA_HOST environment variable is not defined, a default of
|
||||||
|
// 169.254.169.254 will be used instead.
|
||||||
|
//
|
||||||
|
// If the requested metadata is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
func (c *Client) Get(suffix string) (string, error) {
|
||||||
|
val, _, err := c.getETag(suffix)
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getTrimmed(suffix string) (s string, err error) {
|
||||||
|
s, err = c.Get(suffix)
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) lines(suffix string) ([]string, error) {
|
||||||
|
j, err := c.Get(suffix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := strings.Split(strings.TrimSpace(j), "\n")
|
||||||
|
for i := range s {
|
||||||
|
s[i] = strings.TrimSpace(s[i])
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectID returns the current instance's project ID string.
|
||||||
|
func (c *Client) ProjectID() (string, error) { return projID.get(c) }
|
||||||
|
|
||||||
|
// NumericProjectID returns the current instance's numeric project ID.
|
||||||
|
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
|
||||||
|
|
||||||
|
// InstanceID returns the current VM's numeric instance ID.
|
||||||
|
func (c *Client) InstanceID() (string, error) { return instID.get(c) }
|
||||||
|
|
||||||
|
// InternalIP returns the instance's primary internal IP address.
|
||||||
|
func (c *Client) InternalIP() (string, error) {
|
||||||
|
return c.getTrimmed("instance/network-interfaces/0/ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIP returns the instance's primary external (public) IP address.
|
||||||
|
func (c *Client) ExternalIP() (string, error) {
|
||||||
|
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname returns the instance's hostname. This will be of the form
|
||||||
|
// "<instanceID>.c.<projID>.internal".
|
||||||
|
func (c *Client) Hostname() (string, error) {
|
||||||
|
return c.getTrimmed("instance/hostname")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceTags returns the list of user-defined instance tags,
|
||||||
|
// assigned when initially creating a GCE instance.
|
||||||
|
func (c *Client) InstanceTags() ([]string, error) {
|
||||||
|
var s []string
|
||||||
|
j, err := c.Get("instance/tags")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceName returns the current VM's instance ID string.
|
||||||
|
func (c *Client) InstanceName() (string, error) {
|
||||||
|
host, err := c.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.Split(host, ".")[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone returns the current VM's zone, such as "us-central1-b".
|
||||||
|
func (c *Client) Zone() (string, error) {
|
||||||
|
zone, err := c.getTrimmed("instance/zone")
|
||||||
|
// zone is of the form "projects/<projNum>/zones/<zoneName>".
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return zone[strings.LastIndex(zone, "/")+1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributes returns the list of user-defined attributes,
|
||||||
|
// assigned when initially creating a GCE VM instance. The value of an
|
||||||
|
// attribute can be obtained with InstanceAttributeValue.
|
||||||
|
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
|
||||||
|
|
||||||
|
// ProjectAttributes returns the list of user-defined attributes
|
||||||
|
// applying to the project as a whole, not just this VM. The value of
|
||||||
|
// an attribute can be obtained with ProjectAttributeValue.
|
||||||
|
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
|
||||||
|
|
||||||
|
// InstanceAttributeValue returns the value of the provided VM
|
||||||
|
// instance attribute.
|
||||||
|
//
|
||||||
|
// If the requested attribute is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// InstanceAttributeValue may return ("", nil) if the attribute was
|
||||||
|
// defined to be the empty string.
|
||||||
|
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
|
||||||
|
return c.Get("instance/attributes/" + attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributeValue returns the value of the provided
|
||||||
|
// project attribute.
|
||||||
|
//
|
||||||
|
// If the requested attribute is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// ProjectAttributeValue may return ("", nil) if the attribute was
|
||||||
|
// defined to be the empty string.
|
||||||
|
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
|
||||||
|
return c.Get("project/attributes/" + attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scopes returns the service account scopes for the given account.
|
||||||
|
// The account may be empty or the string "default" to use the instance's
|
||||||
|
// main account.
|
||||||
|
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
|
||||||
|
if serviceAccount == "" {
|
||||||
|
serviceAccount = "default"
|
||||||
|
}
|
||||||
|
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe subscribes to a value from the metadata service.
|
||||||
|
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||||
|
// The suffix may contain query parameters.
|
||||||
|
//
|
||||||
|
// Subscribe calls fn with the latest metadata value indicated by the provided
|
||||||
|
// suffix. If the metadata value is deleted, fn is called with the empty string
|
||||||
|
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
|
||||||
|
// is deleted. Subscribe returns the error value returned from the last call to
|
||||||
|
// fn, which may be nil when ok == false.
|
||||||
|
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
||||||
|
const failedSubscribeSleep = time.Second * 5
|
||||||
|
|
||||||
|
// First check to see if the metadata value exists at all.
|
||||||
|
val, lastETag, err := c.getETag(suffix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fn(val, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := true
|
||||||
|
if strings.ContainsRune(suffix, '?') {
|
||||||
|
suffix += "&wait_for_change=true&last_etag="
|
||||||
|
} else {
|
||||||
|
suffix += "?wait_for_change=true&last_etag="
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
|
||||||
|
if err != nil {
|
||||||
|
if _, deleted := err.(NotDefinedError); !deleted {
|
||||||
|
time.Sleep(failedSubscribeSleep)
|
||||||
|
continue // Retry on other errors.
|
||||||
|
}
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
lastETag = etag
|
||||||
|
|
||||||
|
if err := fn(val, ok); err != nil || !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error contains an error response from the server.
|
||||||
|
type Error struct {
|
||||||
|
// Code is the HTTP response status code.
|
||||||
|
Code int
|
||||||
|
// Message is the server response message.
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
|
||||||
|
}
|
||||||
12
vendor/github.com/PuerkitoBio/goquery/LICENSE
generated
vendored
Normal file
12
vendor/github.com/PuerkitoBio/goquery/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Copyright (c) 2012-2016, Martin Angers & Contributors
|
||||||
|
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.
|
||||||
182
vendor/github.com/PuerkitoBio/goquery/README.md
generated
vendored
Normal file
182
vendor/github.com/PuerkitoBio/goquery/README.md
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# goquery - a little like that j-thing, only in Go
|
||||||
|
[](http://travis-ci.org/PuerkitoBio/goquery) [](http://godoc.org/github.com/PuerkitoBio/goquery) [](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge)
|
||||||
|
|
||||||
|
goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off.
|
||||||
|
|
||||||
|
Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. See the [wiki][] for various options to do this.
|
||||||
|
|
||||||
|
Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...).
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Changelog](#changelog)
|
||||||
|
* [API](#api)
|
||||||
|
* [Examples](#examples)
|
||||||
|
* [Related Projects](#related-projects)
|
||||||
|
* [Support](#support)
|
||||||
|
* [License](#license)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Please note that because of the net/html dependency, goquery requires Go1.1+.
|
||||||
|
|
||||||
|
$ go get github.com/PuerkitoBio/goquery
|
||||||
|
|
||||||
|
(optional) To run unit tests:
|
||||||
|
|
||||||
|
$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
|
||||||
|
$ go test
|
||||||
|
|
||||||
|
(optional) To run benchmarks (warning: it runs for a few minutes):
|
||||||
|
|
||||||
|
$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
|
||||||
|
$ go test -bench=".*"
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
**Note that goquery's API is now stable, and will not break.**
|
||||||
|
|
||||||
|
* **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505).
|
||||||
|
* **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples.
|
||||||
|
* **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`.
|
||||||
|
* **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue).
|
||||||
|
* **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins).
|
||||||
|
* **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv).
|
||||||
|
* **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb).
|
||||||
|
* **2016-08-28 (v1.0.1)** : Optimize performance for large documents.
|
||||||
|
* **2016-07-27 (v1.0.0)** : Tag version 1.0.0.
|
||||||
|
* **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object.
|
||||||
|
* **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see godoc for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`).
|
||||||
|
* **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr].
|
||||||
|
* **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone].
|
||||||
|
* **2014-11-28** : Add more manipulation functions - ReplaceWith*, Wrap* and Unwrap - thanks again to [Andrew Stone][thatguystone].
|
||||||
|
* **2014-11-07** : Add manipulation functions (thanks to [Andrew Stone][thatguystone]) and `*Matcher` functions, that receive compiled cascadia selectors instead of selector strings, thus avoiding potential panics thrown by goquery via `cascadia.MustCompile` calls. This results in better performance (selectors can be compiled once and reused) and more idiomatic error handling (you can handle cascadia's compilation errors, instead of recovering from panics, which had been bugging me for a long time). Note that the actual type expected is a `Matcher` interface, that `cascadia.Selector` implements. Other matcher implementations could be used.
|
||||||
|
* **2014-11-06** : Change import paths of net/html to golang.org/x/net/html (see https://groups.google.com/forum/#!topic/golang-nuts/eD8dh3T9yyA). Make sure to update your code to use the new import path too when you call goquery with `html.Node`s.
|
||||||
|
* **v0.3.2** : Add `NewDocumentFromReader()` (thanks jweir) which allows creating a goquery document from an io.Reader.
|
||||||
|
* **v0.3.1** : Add `NewDocumentFromResponse()` (thanks assassingj) which allows creating a goquery document from an http response.
|
||||||
|
* **v0.3.0** : Add `EachWithBreak()` which allows to break out of an `Each()` loop by returning false. This function was added instead of changing the existing `Each()` to avoid breaking compatibility.
|
||||||
|
* **v0.2.1** : Make go-getable, now that [go.net/html is Go1.0-compatible][gonet] (thanks to @matrixik for pointing this out).
|
||||||
|
* **v0.2.0** : Add support for negative indices in Slice(). **BREAKING CHANGE** `Document.Root` is removed, `Document` is now a `Selection` itself (a selection of one, the root element, just like `Document.Root` was before). Add jQuery's Closest() method.
|
||||||
|
* **v0.1.1** : Add benchmarks to use as baseline for refactorings, refactor Next...() and Prev...() methods to use the new html package's linked list features (Next/PrevSibling, FirstChild). Good performance boost (40+% in some cases).
|
||||||
|
* **v0.1.0** : Initial release.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
goquery exposes two structs, `Document` and `Selection`, and the `Matcher` interface. Unlike jQuery, which is loaded as part of a DOM document, and thus acts on its containing document, goquery doesn't know which HTML document to act upon. So it needs to be told, and that's what the `Document` type is for. It holds the root document node as the initial Selection value to manipulate.
|
||||||
|
|
||||||
|
jQuery often has many variants for the same function (no argument, a selector string argument, a jQuery object argument, a DOM element argument, ...). Instead of exposing the same features in goquery as a single method with variadic empty interface arguments, statically-typed signatures are used following this naming convention:
|
||||||
|
|
||||||
|
* When the jQuery equivalent can be called with no argument, it has the same name as jQuery for the no argument signature (e.g.: `Prev()`), and the version with a selector string argument is called `XxxFiltered()` (e.g.: `PrevFiltered()`)
|
||||||
|
* When the jQuery equivalent **requires** one argument, the same name as jQuery is used for the selector string version (e.g.: `Is()`)
|
||||||
|
* The signatures accepting a jQuery object as argument are defined in goquery as `XxxSelection()` and take a `*Selection` object as argument (e.g.: `FilterSelection()`)
|
||||||
|
* The signatures accepting a DOM element as argument in jQuery are defined in goquery as `XxxNodes()` and take a variadic argument of type `*html.Node` (e.g.: `FilterNodes()`)
|
||||||
|
* The signatures accepting a function as argument in jQuery are defined in goquery as `XxxFunction()` and take a function as argument (e.g.: `FilterFunction()`)
|
||||||
|
* The goquery methods that can be called with a selector string have a corresponding version that take a `Matcher` interface and are defined as `XxxMatcher()` (e.g.: `IsMatcher()`)
|
||||||
|
|
||||||
|
Utility functions that are not in jQuery but are useful in Go are implemented as functions (that take a `*Selection` as parameter), to avoid a potential naming clash on the `*Selection`'s methods (reserved for jQuery-equivalent behaviour).
|
||||||
|
|
||||||
|
The complete [godoc reference documentation can be found here][doc].
|
||||||
|
|
||||||
|
Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string):
|
||||||
|
|
||||||
|
* `Find("~")` returns an empty selection because the selector string doesn't match anything.
|
||||||
|
* `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything).
|
||||||
|
* `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything.
|
||||||
|
* `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See some tips and tricks in the [wiki][].
|
||||||
|
|
||||||
|
Adapted from example_test.go:
|
||||||
|
|
||||||
|
```Go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleScrape() {
|
||||||
|
// Request the HTML page.
|
||||||
|
res, err := http.Get("http://metalsucks.net")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the HTML document
|
||||||
|
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the review items
|
||||||
|
doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
|
||||||
|
// For each item found, get the band and title
|
||||||
|
band := s.Find("a").Text()
|
||||||
|
title := s.Find("i").Text()
|
||||||
|
fmt.Printf("Review %d: %s - %s\n", i, band, title)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ExampleScrape()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Projects
|
||||||
|
|
||||||
|
- [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags.
|
||||||
|
- [andybalholm/cascadia][cascadia], the CSS selector library used by goquery.
|
||||||
|
- [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors.
|
||||||
|
- [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework
|
||||||
|
- [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets.
|
||||||
|
- [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping.
|
||||||
|
- [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins
|
||||||
|
- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers.
|
||||||
|
- [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
There are a number of ways you can support the project:
|
||||||
|
|
||||||
|
* Use it, star it, build something with it, spread the word!
|
||||||
|
- If you do build something open-source or otherwise publicly-visible, let me know so I can add it to the [Related Projects](#related-projects) section!
|
||||||
|
* Raise issues to improve the project (note: doc typos and clarifications are issues too!)
|
||||||
|
- Please search existing issues before opening a new one - it may have already been adressed.
|
||||||
|
* Pull requests: please discuss new code in an issue first, unless the fix is really trivial.
|
||||||
|
- Make sure new code is tested.
|
||||||
|
- Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue.
|
||||||
|
|
||||||
|
If you desperately want to send money my way, I have a BuyMeACoffee.com page:
|
||||||
|
|
||||||
|
<a href="https://www.buymeacoffee.com/mna" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
|
||||||
|
|
||||||
|
[jquery]: http://jquery.com/
|
||||||
|
[go]: http://golang.org/
|
||||||
|
[cascadia]: https://github.com/andybalholm/cascadia
|
||||||
|
[cascadiacli]: https://github.com/suntong/cascadia
|
||||||
|
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
||||||
|
[golic]: http://golang.org/LICENSE
|
||||||
|
[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
|
||||||
|
[doc]: http://godoc.org/github.com/PuerkitoBio/goquery
|
||||||
|
[index]: http://api.jquery.com/index/
|
||||||
|
[gonet]: https://github.com/golang/net/
|
||||||
|
[html]: http://godoc.org/golang.org/x/net/html
|
||||||
|
[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
|
||||||
|
[thatguystone]: https://github.com/thatguystone
|
||||||
|
[piotr]: https://github.com/piotrkowalczuk
|
||||||
|
[goq]: https://github.com/andrewstuart/goq
|
||||||
124
vendor/github.com/PuerkitoBio/goquery/array.go
generated
vendored
Normal file
124
vendor/github.com/PuerkitoBio/goquery/array.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxUint = ^uint(0)
|
||||||
|
maxInt = int(maxUint >> 1)
|
||||||
|
|
||||||
|
// ToEnd is a special index value that can be used as end index in a call
|
||||||
|
// to Slice so that all elements are selected until the end of the Selection.
|
||||||
|
// It is equivalent to passing (*Selection).Length().
|
||||||
|
ToEnd = maxInt
|
||||||
|
)
|
||||||
|
|
||||||
|
// First reduces the set of matched elements to the first in the set.
|
||||||
|
// It returns a new Selection object, and an empty Selection object if the
|
||||||
|
// the selection is empty.
|
||||||
|
func (s *Selection) First() *Selection {
|
||||||
|
return s.Eq(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last reduces the set of matched elements to the last in the set.
|
||||||
|
// It returns a new Selection object, and an empty Selection object if
|
||||||
|
// the selection is empty.
|
||||||
|
func (s *Selection) Last() *Selection {
|
||||||
|
return s.Eq(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eq reduces the set of matched elements to the one at the specified index.
|
||||||
|
// If a negative index is given, it counts backwards starting at the end of the
|
||||||
|
// set. It returns a new Selection object, and an empty Selection object if the
|
||||||
|
// index is invalid.
|
||||||
|
func (s *Selection) Eq(index int) *Selection {
|
||||||
|
if index < 0 {
|
||||||
|
index += len(s.Nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if index >= len(s.Nodes) || index < 0 {
|
||||||
|
return newEmptySelection(s.document)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Slice(index, index+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice reduces the set of matched elements to a subset specified by a range
|
||||||
|
// of indices. The start index is 0-based and indicates the index of the first
|
||||||
|
// element to select. The end index is 0-based and indicates the index at which
|
||||||
|
// the elements stop being selected (the end index is not selected).
|
||||||
|
//
|
||||||
|
// The indices may be negative, in which case they represent an offset from the
|
||||||
|
// end of the selection.
|
||||||
|
//
|
||||||
|
// The special value ToEnd may be specified as end index, in which case all elements
|
||||||
|
// until the end are selected. This works both for a positive and negative start
|
||||||
|
// index.
|
||||||
|
func (s *Selection) Slice(start, end int) *Selection {
|
||||||
|
if start < 0 {
|
||||||
|
start += len(s.Nodes)
|
||||||
|
}
|
||||||
|
if end == ToEnd {
|
||||||
|
end = len(s.Nodes)
|
||||||
|
} else if end < 0 {
|
||||||
|
end += len(s.Nodes)
|
||||||
|
}
|
||||||
|
return pushStack(s, s.Nodes[start:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the underlying node at the specified index.
|
||||||
|
// Get without parameter is not implemented, since the node array is available
|
||||||
|
// on the Selection object.
|
||||||
|
func (s *Selection) Get(index int) *html.Node {
|
||||||
|
if index < 0 {
|
||||||
|
index += len(s.Nodes) // Negative index gets from the end
|
||||||
|
}
|
||||||
|
return s.Nodes[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index returns the position of the first element within the Selection object
|
||||||
|
// relative to its sibling elements.
|
||||||
|
func (s *Selection) Index() int {
|
||||||
|
if len(s.Nodes) > 0 {
|
||||||
|
return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length()
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexSelector returns the position of the first element within the
|
||||||
|
// Selection object relative to the elements matched by the selector, or -1 if
|
||||||
|
// not found.
|
||||||
|
func (s *Selection) IndexSelector(selector string) int {
|
||||||
|
if len(s.Nodes) > 0 {
|
||||||
|
sel := s.document.Find(selector)
|
||||||
|
return indexInSlice(sel.Nodes, s.Nodes[0])
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexMatcher returns the position of the first element within the
|
||||||
|
// Selection object relative to the elements matched by the matcher, or -1 if
|
||||||
|
// not found.
|
||||||
|
func (s *Selection) IndexMatcher(m Matcher) int {
|
||||||
|
if len(s.Nodes) > 0 {
|
||||||
|
sel := s.document.FindMatcher(m)
|
||||||
|
return indexInSlice(sel.Nodes, s.Nodes[0])
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOfNode returns the position of the specified node within the Selection
|
||||||
|
// object, or -1 if not found.
|
||||||
|
func (s *Selection) IndexOfNode(node *html.Node) int {
|
||||||
|
return indexInSlice(s.Nodes, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOfSelection returns the position of the first node in the specified
|
||||||
|
// Selection object within this Selection object, or -1 if not found.
|
||||||
|
func (s *Selection) IndexOfSelection(sel *Selection) int {
|
||||||
|
if sel != nil && len(sel.Nodes) > 0 {
|
||||||
|
return indexInSlice(s.Nodes, sel.Nodes[0])
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
123
vendor/github.com/PuerkitoBio/goquery/doc.go
generated
vendored
Normal file
123
vendor/github.com/PuerkitoBio/goquery/doc.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (c) 2012-2016, Martin Angers & Contributors
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package goquery implements features similar to jQuery, including the chainable
|
||||||
|
syntax, to manipulate and query an HTML document.
|
||||||
|
|
||||||
|
It brings a syntax and a set of features similar to jQuery to the Go language.
|
||||||
|
It is based on Go's net/html package and the CSS Selector library cascadia.
|
||||||
|
Since the net/html parser returns nodes, and not a full-featured DOM
|
||||||
|
tree, jQuery's stateful manipulation functions (like height(), css(), detach())
|
||||||
|
have been left off.
|
||||||
|
|
||||||
|
Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is
|
||||||
|
the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML.
|
||||||
|
See the repository's wiki for various options on how to do this.
|
||||||
|
|
||||||
|
Syntax-wise, it is as close as possible to jQuery, with the same method names when
|
||||||
|
possible, and that warm and fuzzy chainable interface. jQuery being the
|
||||||
|
ultra-popular library that it is, writing a similar HTML-manipulating
|
||||||
|
library was better to follow its API than to start anew (in the same spirit as
|
||||||
|
Go's fmt package), even though some of its methods are less than intuitive (looking
|
||||||
|
at you, index()...).
|
||||||
|
|
||||||
|
It is hosted on GitHub, along with additional documentation in the README.md
|
||||||
|
file: https://github.com/puerkitobio/goquery
|
||||||
|
|
||||||
|
Please note that because of the net/html dependency, goquery requires Go1.1+.
|
||||||
|
|
||||||
|
The various methods are split into files based on the category of behavior.
|
||||||
|
The three dots (...) indicate that various "overloads" are available.
|
||||||
|
|
||||||
|
* array.go : array-like positional manipulation of the selection.
|
||||||
|
- Eq()
|
||||||
|
- First()
|
||||||
|
- Get()
|
||||||
|
- Index...()
|
||||||
|
- Last()
|
||||||
|
- Slice()
|
||||||
|
|
||||||
|
* expand.go : methods that expand or augment the selection's set.
|
||||||
|
- Add...()
|
||||||
|
- AndSelf()
|
||||||
|
- Union(), which is an alias for AddSelection()
|
||||||
|
|
||||||
|
* filter.go : filtering methods, that reduce the selection's set.
|
||||||
|
- End()
|
||||||
|
- Filter...()
|
||||||
|
- Has...()
|
||||||
|
- Intersection(), which is an alias of FilterSelection()
|
||||||
|
- Not...()
|
||||||
|
|
||||||
|
* iteration.go : methods to loop over the selection's nodes.
|
||||||
|
- Each()
|
||||||
|
- EachWithBreak()
|
||||||
|
- Map()
|
||||||
|
|
||||||
|
* manipulation.go : methods for modifying the document
|
||||||
|
- After...()
|
||||||
|
- Append...()
|
||||||
|
- Before...()
|
||||||
|
- Clone()
|
||||||
|
- Empty()
|
||||||
|
- Prepend...()
|
||||||
|
- Remove...()
|
||||||
|
- ReplaceWith...()
|
||||||
|
- Unwrap()
|
||||||
|
- Wrap...()
|
||||||
|
- WrapAll...()
|
||||||
|
- WrapInner...()
|
||||||
|
|
||||||
|
* property.go : methods that inspect and get the node's properties values.
|
||||||
|
- Attr*(), RemoveAttr(), SetAttr()
|
||||||
|
- AddClass(), HasClass(), RemoveClass(), ToggleClass()
|
||||||
|
- Html()
|
||||||
|
- Length()
|
||||||
|
- Size(), which is an alias for Length()
|
||||||
|
- Text()
|
||||||
|
|
||||||
|
* query.go : methods that query, or reflect, a node's identity.
|
||||||
|
- Contains()
|
||||||
|
- Is...()
|
||||||
|
|
||||||
|
* traversal.go : methods to traverse the HTML document tree.
|
||||||
|
- Children...()
|
||||||
|
- Contents()
|
||||||
|
- Find...()
|
||||||
|
- Next...()
|
||||||
|
- Parent[s]...()
|
||||||
|
- Prev...()
|
||||||
|
- Siblings...()
|
||||||
|
|
||||||
|
* type.go : definition of the types exposed by goquery.
|
||||||
|
- Document
|
||||||
|
- Selection
|
||||||
|
- Matcher
|
||||||
|
|
||||||
|
* utilities.go : definition of helper functions (and not methods on a *Selection)
|
||||||
|
that are not part of jQuery, but are useful to goquery.
|
||||||
|
- NodeName
|
||||||
|
- OuterHtml
|
||||||
|
*/
|
||||||
|
package goquery
|
||||||
70
vendor/github.com/PuerkitoBio/goquery/expand.go
generated
vendored
Normal file
70
vendor/github.com/PuerkitoBio/goquery/expand.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import "golang.org/x/net/html"
|
||||||
|
|
||||||
|
// Add adds the selector string's matching nodes to those in the current
|
||||||
|
// selection and returns a new Selection object.
|
||||||
|
// The selector string is run in the context of the document of the current
|
||||||
|
// Selection object.
|
||||||
|
func (s *Selection) Add(selector string) *Selection {
|
||||||
|
return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMatcher adds the matcher's matching nodes to those in the current
|
||||||
|
// selection and returns a new Selection object.
|
||||||
|
// The matcher is run in the context of the document of the current
|
||||||
|
// Selection object.
|
||||||
|
func (s *Selection) AddMatcher(m Matcher) *Selection {
|
||||||
|
return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, m)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSelection adds the specified Selection object's nodes to those in the
|
||||||
|
// current selection and returns a new Selection object.
|
||||||
|
func (s *Selection) AddSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.AddNodes()
|
||||||
|
}
|
||||||
|
return s.AddNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union is an alias for AddSelection.
|
||||||
|
func (s *Selection) Union(sel *Selection) *Selection {
|
||||||
|
return s.AddSelection(sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNodes adds the specified nodes to those in the
|
||||||
|
// current selection and returns a new Selection object.
|
||||||
|
func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AndSelf adds the previous set of elements on the stack to the current set.
|
||||||
|
// It returns a new Selection object containing the current Selection combined
|
||||||
|
// with the previous one.
|
||||||
|
// Deprecated: This function has been deprecated and is now an alias for AddBack().
|
||||||
|
func (s *Selection) AndSelf() *Selection {
|
||||||
|
return s.AddBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBack adds the previous set of elements on the stack to the current set.
|
||||||
|
// It returns a new Selection object containing the current Selection combined
|
||||||
|
// with the previous one.
|
||||||
|
func (s *Selection) AddBack() *Selection {
|
||||||
|
return s.AddSelection(s.prevSel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBackFiltered reduces the previous set of elements on the stack to those that
|
||||||
|
// match the selector string, and adds them to the current set.
|
||||||
|
// It returns a new Selection object containing the current Selection combined
|
||||||
|
// with the filtered previous one
|
||||||
|
func (s *Selection) AddBackFiltered(selector string) *Selection {
|
||||||
|
return s.AddSelection(s.prevSel.Filter(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBackMatcher reduces the previous set of elements on the stack to those that match
|
||||||
|
// the mateher, and adds them to the curernt set.
|
||||||
|
// It returns a new Selection object containing the current Selection combined
|
||||||
|
// with the filtered previous one
|
||||||
|
func (s *Selection) AddBackMatcher(m Matcher) *Selection {
|
||||||
|
return s.AddSelection(s.prevSel.FilterMatcher(m))
|
||||||
|
}
|
||||||
163
vendor/github.com/PuerkitoBio/goquery/filter.go
generated
vendored
Normal file
163
vendor/github.com/PuerkitoBio/goquery/filter.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import "golang.org/x/net/html"
|
||||||
|
|
||||||
|
// Filter reduces the set of matched elements to those that match the selector string.
|
||||||
|
// It returns a new Selection object for this subset of matching elements.
|
||||||
|
func (s *Selection) Filter(selector string) *Selection {
|
||||||
|
return s.FilterMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMatcher reduces the set of matched elements to those that match
|
||||||
|
// the given matcher. It returns a new Selection object for this subset
|
||||||
|
// of matching elements.
|
||||||
|
func (s *Selection) FilterMatcher(m Matcher) *Selection {
|
||||||
|
return pushStack(s, winnow(s, m, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not removes elements from the Selection that match the selector string.
|
||||||
|
// It returns a new Selection object with the matching elements removed.
|
||||||
|
func (s *Selection) Not(selector string) *Selection {
|
||||||
|
return s.NotMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotMatcher removes elements from the Selection that match the given matcher.
|
||||||
|
// It returns a new Selection object with the matching elements removed.
|
||||||
|
func (s *Selection) NotMatcher(m Matcher) *Selection {
|
||||||
|
return pushStack(s, winnow(s, m, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterFunction reduces the set of matched elements to those that pass the function's test.
|
||||||
|
// It returns a new Selection object for this subset of elements.
|
||||||
|
func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
|
||||||
|
return pushStack(s, winnowFunction(s, f, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFunction removes elements from the Selection that pass the function's test.
|
||||||
|
// It returns a new Selection object with the matching elements removed.
|
||||||
|
func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
|
||||||
|
return pushStack(s, winnowFunction(s, f, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterNodes reduces the set of matched elements to those that match the specified nodes.
|
||||||
|
// It returns a new Selection object for this subset of elements.
|
||||||
|
func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return pushStack(s, winnowNodes(s, nodes, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotNodes removes elements from the Selection that match the specified nodes.
|
||||||
|
// It returns a new Selection object with the matching elements removed.
|
||||||
|
func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return pushStack(s, winnowNodes(s, nodes, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterSelection reduces the set of matched elements to those that match a
|
||||||
|
// node in the specified Selection object.
|
||||||
|
// It returns a new Selection object for this subset of elements.
|
||||||
|
func (s *Selection) FilterSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return pushStack(s, winnowNodes(s, nil, true))
|
||||||
|
}
|
||||||
|
return pushStack(s, winnowNodes(s, sel.Nodes, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotSelection removes elements from the Selection that match a node in the specified
|
||||||
|
// Selection object. It returns a new Selection object with the matching elements removed.
|
||||||
|
func (s *Selection) NotSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return pushStack(s, winnowNodes(s, nil, false))
|
||||||
|
}
|
||||||
|
return pushStack(s, winnowNodes(s, sel.Nodes, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection is an alias for FilterSelection.
|
||||||
|
func (s *Selection) Intersection(sel *Selection) *Selection {
|
||||||
|
return s.FilterSelection(sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has reduces the set of matched elements to those that have a descendant
|
||||||
|
// that matches the selector.
|
||||||
|
// It returns a new Selection object with the matching elements.
|
||||||
|
func (s *Selection) Has(selector string) *Selection {
|
||||||
|
return s.HasSelection(s.document.Find(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasMatcher reduces the set of matched elements to those that have a descendant
|
||||||
|
// that matches the matcher.
|
||||||
|
// It returns a new Selection object with the matching elements.
|
||||||
|
func (s *Selection) HasMatcher(m Matcher) *Selection {
|
||||||
|
return s.HasSelection(s.document.FindMatcher(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasNodes reduces the set of matched elements to those that have a
|
||||||
|
// descendant that matches one of the nodes.
|
||||||
|
// It returns a new Selection object with the matching elements.
|
||||||
|
func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return s.FilterFunction(func(_ int, sel *Selection) bool {
|
||||||
|
// Add all nodes that contain one of the specified nodes
|
||||||
|
for _, n := range nodes {
|
||||||
|
if sel.Contains(n) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSelection reduces the set of matched elements to those that have a
|
||||||
|
// descendant that matches one of the nodes of the specified Selection object.
|
||||||
|
// It returns a new Selection object with the matching elements.
|
||||||
|
func (s *Selection) HasSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.HasNodes()
|
||||||
|
}
|
||||||
|
return s.HasNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// End ends the most recent filtering operation in the current chain and
|
||||||
|
// returns the set of matched elements to its previous state.
|
||||||
|
func (s *Selection) End() *Selection {
|
||||||
|
if s.prevSel != nil {
|
||||||
|
return s.prevSel
|
||||||
|
}
|
||||||
|
return newEmptySelection(s.document)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter based on the matcher, and the indicator to keep (Filter) or
|
||||||
|
// to get rid of (Not) the matching elements.
|
||||||
|
func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
|
||||||
|
// Optimize if keep is requested
|
||||||
|
if keep {
|
||||||
|
return m.Filter(sel.Nodes)
|
||||||
|
}
|
||||||
|
// Use grep
|
||||||
|
return grep(sel, func(i int, s *Selection) bool {
|
||||||
|
return !m.Match(s.Get(0))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter based on an array of nodes, and the indicator to keep (Filter) or
|
||||||
|
// to get rid of (Not) the matching elements.
|
||||||
|
func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
|
||||||
|
if len(nodes)+len(sel.Nodes) < minNodesForSet {
|
||||||
|
return grep(sel, func(i int, s *Selection) bool {
|
||||||
|
return isInSlice(nodes, s.Get(0)) == keep
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
set := make(map[*html.Node]bool)
|
||||||
|
for _, n := range nodes {
|
||||||
|
set[n] = true
|
||||||
|
}
|
||||||
|
return grep(sel, func(i int, s *Selection) bool {
|
||||||
|
return set[s.Get(0)] == keep
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter based on a function test, and the indicator to keep (Filter) or
|
||||||
|
// to get rid of (Not) the matching elements.
|
||||||
|
func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node {
|
||||||
|
return grep(sel, func(i int, s *Selection) bool {
|
||||||
|
return f(i, s) == keep
|
||||||
|
})
|
||||||
|
}
|
||||||
6
vendor/github.com/PuerkitoBio/goquery/go.mod
generated
vendored
Normal file
6
vendor/github.com/PuerkitoBio/goquery/go.mod
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module github.com/PuerkitoBio/goquery
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/andybalholm/cascadia v1.0.0
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
|
||||||
|
)
|
||||||
5
vendor/github.com/PuerkitoBio/goquery/go.sum
generated
vendored
Normal file
5
vendor/github.com/PuerkitoBio/goquery/go.sum
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
39
vendor/github.com/PuerkitoBio/goquery/iteration.go
generated
vendored
Normal file
39
vendor/github.com/PuerkitoBio/goquery/iteration.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
// Each iterates over a Selection object, executing a function for each
|
||||||
|
// matched element. It returns the current Selection object. The function
|
||||||
|
// f is called for each element in the selection with the index of the
|
||||||
|
// element in that selection starting at 0, and a *Selection that contains
|
||||||
|
// only that element.
|
||||||
|
func (s *Selection) Each(f func(int, *Selection)) *Selection {
|
||||||
|
for i, n := range s.Nodes {
|
||||||
|
f(i, newSingleSelection(n, s.document))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachWithBreak iterates over a Selection object, executing a function for each
|
||||||
|
// matched element. It is identical to Each except that it is possible to break
|
||||||
|
// out of the loop by returning false in the callback function. It returns the
|
||||||
|
// current Selection object.
|
||||||
|
func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
|
||||||
|
for i, n := range s.Nodes {
|
||||||
|
if !f(i, newSingleSelection(n, s.document)) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map passes each element in the current matched set through a function,
|
||||||
|
// producing a slice of string holding the returned values. The function
|
||||||
|
// f is called for each element in the selection with the index of the
|
||||||
|
// element in that selection starting at 0, and a *Selection that contains
|
||||||
|
// only that element.
|
||||||
|
func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
|
||||||
|
for i, n := range s.Nodes {
|
||||||
|
result = append(result, f(i, newSingleSelection(n, s.document)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
574
vendor/github.com/PuerkitoBio/goquery/manipulation.go
generated
vendored
Normal file
574
vendor/github.com/PuerkitoBio/goquery/manipulation.go
generated
vendored
Normal file
@@ -0,0 +1,574 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
// After applies the selector from the root document and inserts the matched elements
|
||||||
|
// after the elements in the set of matched elements.
|
||||||
|
//
|
||||||
|
// If one of the matched elements in the selection is not currently in the
|
||||||
|
// document, it's impossible to insert nodes after it, so it will be ignored.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) After(selector string) *Selection {
|
||||||
|
return s.AfterMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterMatcher applies the matcher from the root document and inserts the matched elements
|
||||||
|
// after the elements in the set of matched elements.
|
||||||
|
//
|
||||||
|
// If one of the matched elements in the selection is not currently in the
|
||||||
|
// document, it's impossible to insert nodes after it, so it will be ignored.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) AfterMatcher(m Matcher) *Selection {
|
||||||
|
return s.AfterNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterSelection inserts the elements in the selection after each element in the set of matched
|
||||||
|
// elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) AfterSelection(sel *Selection) *Selection {
|
||||||
|
return s.AfterNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterHtml parses the html and inserts it after the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) AfterHtml(html string) *Selection {
|
||||||
|
return s.AfterNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterNodes inserts the nodes after each element in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
|
||||||
|
return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
|
||||||
|
if sn.Parent != nil {
|
||||||
|
sn.Parent.InsertBefore(n, sn.NextSibling)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append appends the elements specified by the selector to the end of each element
|
||||||
|
// in the set of matched elements, following those rules:
|
||||||
|
//
|
||||||
|
// 1) The selector is applied to the root document.
|
||||||
|
//
|
||||||
|
// 2) Elements that are part of the document will be moved to the new location.
|
||||||
|
//
|
||||||
|
// 3) If there are multiple locations to append to, cloned nodes will be
|
||||||
|
// appended to all target locations except the last one, which will be moved
|
||||||
|
// as noted in (2).
|
||||||
|
func (s *Selection) Append(selector string) *Selection {
|
||||||
|
return s.AppendMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendMatcher appends the elements specified by the matcher to the end of each element
|
||||||
|
// in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) AppendMatcher(m Matcher) *Selection {
|
||||||
|
return s.AppendNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendSelection appends the elements in the selection to the end of each element
|
||||||
|
// in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) AppendSelection(sel *Selection) *Selection {
|
||||||
|
return s.AppendNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendHtml parses the html and appends it to the set of matched elements.
|
||||||
|
func (s *Selection) AppendHtml(html string) *Selection {
|
||||||
|
return s.AppendNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendNodes appends the specified nodes to each node in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
|
||||||
|
return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
|
||||||
|
sn.AppendChild(n)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before inserts the matched elements before each element in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) Before(selector string) *Selection {
|
||||||
|
return s.BeforeMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeMatcher inserts the matched elements before each element in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) BeforeMatcher(m Matcher) *Selection {
|
||||||
|
return s.BeforeNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeSelection inserts the elements in the selection before each element in the set of matched
|
||||||
|
// elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) BeforeSelection(sel *Selection) *Selection {
|
||||||
|
return s.BeforeNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeHtml parses the html and inserts it before the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) BeforeHtml(html string) *Selection {
|
||||||
|
return s.BeforeNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeNodes inserts the nodes before each element in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection {
|
||||||
|
return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
|
||||||
|
if sn.Parent != nil {
|
||||||
|
sn.Parent.InsertBefore(n, sn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a deep copy of the set of matched nodes. The new nodes will not be
|
||||||
|
// attached to the document.
|
||||||
|
func (s *Selection) Clone() *Selection {
|
||||||
|
ns := newEmptySelection(s.document)
|
||||||
|
ns.Nodes = cloneNodes(s.Nodes)
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty removes all children nodes from the set of matched elements.
|
||||||
|
// It returns the children nodes in a new Selection.
|
||||||
|
func (s *Selection) Empty() *Selection {
|
||||||
|
var nodes []*html.Node
|
||||||
|
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
for c := n.FirstChild; c != nil; c = n.FirstChild {
|
||||||
|
n.RemoveChild(c)
|
||||||
|
nodes = append(nodes, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pushStack(s, nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend prepends the elements specified by the selector to each element in
|
||||||
|
// the set of matched elements, following the same rules as Append.
|
||||||
|
func (s *Selection) Prepend(selector string) *Selection {
|
||||||
|
return s.PrependMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrependMatcher prepends the elements specified by the matcher to each
|
||||||
|
// element in the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) PrependMatcher(m Matcher) *Selection {
|
||||||
|
return s.PrependNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrependSelection prepends the elements in the selection to each element in
|
||||||
|
// the set of matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) PrependSelection(sel *Selection) *Selection {
|
||||||
|
return s.PrependNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrependHtml parses the html and prepends it to the set of matched elements.
|
||||||
|
func (s *Selection) PrependHtml(html string) *Selection {
|
||||||
|
return s.PrependNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrependNodes prepends the specified nodes to each node in the set of
|
||||||
|
// matched elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) PrependNodes(ns ...*html.Node) *Selection {
|
||||||
|
return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
|
||||||
|
// sn.FirstChild may be nil, in which case this functions like
|
||||||
|
// sn.AppendChild()
|
||||||
|
sn.InsertBefore(n, sn.FirstChild)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the set of matched elements from the document.
|
||||||
|
// It returns the same selection, now consisting of nodes not in the document.
|
||||||
|
func (s *Selection) Remove() *Selection {
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
if n.Parent != nil {
|
||||||
|
n.Parent.RemoveChild(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFiltered removes the set of matched elements by selector.
|
||||||
|
// It returns the Selection of removed nodes.
|
||||||
|
func (s *Selection) RemoveFiltered(selector string) *Selection {
|
||||||
|
return s.RemoveMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMatcher removes the set of matched elements.
|
||||||
|
// It returns the Selection of removed nodes.
|
||||||
|
func (s *Selection) RemoveMatcher(m Matcher) *Selection {
|
||||||
|
return s.FilterMatcher(m).Remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWith replaces each element in the set of matched elements with the
|
||||||
|
// nodes matched by the given selector.
|
||||||
|
// It returns the removed elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) ReplaceWith(selector string) *Selection {
|
||||||
|
return s.ReplaceWithMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWithMatcher replaces each element in the set of matched elements with
|
||||||
|
// the nodes matched by the given Matcher.
|
||||||
|
// It returns the removed elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection {
|
||||||
|
return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWithSelection replaces each element in the set of matched elements with
|
||||||
|
// the nodes from the given Selection.
|
||||||
|
// It returns the removed elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
|
||||||
|
return s.ReplaceWithNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWithHtml replaces each element in the set of matched elements with
|
||||||
|
// the parsed HTML.
|
||||||
|
// It returns the removed elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) ReplaceWithHtml(html string) *Selection {
|
||||||
|
return s.ReplaceWithNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWithNodes replaces each element in the set of matched elements with
|
||||||
|
// the given nodes.
|
||||||
|
// It returns the removed elements.
|
||||||
|
//
|
||||||
|
// This follows the same rules as Selection.Append.
|
||||||
|
func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
|
||||||
|
s.AfterNodes(ns...)
|
||||||
|
return s.Remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHtml sets the html content of each element in the selection to
|
||||||
|
// specified html string.
|
||||||
|
func (s *Selection) SetHtml(html string) *Selection {
|
||||||
|
return setHtmlNodes(s, parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetText sets the content of each element in the selection to specified content.
|
||||||
|
// The provided text string is escaped.
|
||||||
|
func (s *Selection) SetText(text string) *Selection {
|
||||||
|
return s.SetHtml(html.EscapeString(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap removes the parents of the set of matched elements, leaving the matched
|
||||||
|
// elements (and their siblings, if any) in their place.
|
||||||
|
// It returns the original selection.
|
||||||
|
func (s *Selection) Unwrap() *Selection {
|
||||||
|
s.Parent().Each(func(i int, ss *Selection) {
|
||||||
|
// For some reason, jquery allows unwrap to remove the <head> element, so
|
||||||
|
// allowing it here too. Same for <html>. Why it allows those elements to
|
||||||
|
// be unwrapped while not allowing body is a mystery to me.
|
||||||
|
if ss.Nodes[0].Data != "body" {
|
||||||
|
ss.ReplaceWithSelection(ss.Contents())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap wraps each element in the set of matched elements inside the first
|
||||||
|
// element matched by the given selector. The matched child is cloned before
|
||||||
|
// being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) Wrap(selector string) *Selection {
|
||||||
|
return s.WrapMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapMatcher wraps each element in the set of matched elements inside the
|
||||||
|
// first element matched by the given matcher. The matched child is cloned
|
||||||
|
// before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapMatcher(m Matcher) *Selection {
|
||||||
|
return s.wrapNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapSelection wraps each element in the set of matched elements inside the
|
||||||
|
// first element in the given Selection. The element is cloned before being
|
||||||
|
// inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapSelection(sel *Selection) *Selection {
|
||||||
|
return s.wrapNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapHtml wraps each element in the set of matched elements inside the inner-
|
||||||
|
// most child of the given HTML.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapHtml(html string) *Selection {
|
||||||
|
return s.wrapNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapNode wraps each element in the set of matched elements inside the inner-
|
||||||
|
// most child of the given node. The given node is copied before being inserted
|
||||||
|
// into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapNode(n *html.Node) *Selection {
|
||||||
|
return s.wrapNodes(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
|
||||||
|
s.Each(func(i int, ss *Selection) {
|
||||||
|
ss.wrapAllNodes(ns...)
|
||||||
|
})
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapAll wraps a single HTML structure, matched by the given selector, around
|
||||||
|
// all elements in the set of matched elements. The matched child is cloned
|
||||||
|
// before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapAll(selector string) *Selection {
|
||||||
|
return s.WrapAllMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
|
||||||
|
// around all elements in the set of matched elements. The matched child is
|
||||||
|
// cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapAllMatcher(m Matcher) *Selection {
|
||||||
|
return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapAllSelection wraps a single HTML structure, the first node of the given
|
||||||
|
// Selection, around all elements in the set of matched elements. The matched
|
||||||
|
// child is cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
|
||||||
|
return s.wrapAllNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapAllHtml wraps the given HTML structure around all elements in the set of
|
||||||
|
// matched elements. The matched child is cloned before being inserted into the
|
||||||
|
// document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapAllHtml(html string) *Selection {
|
||||||
|
return s.wrapAllNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
|
||||||
|
if len(ns) > 0 {
|
||||||
|
return s.WrapAllNode(ns[0])
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapAllNode wraps the given node around the first element in the Selection,
|
||||||
|
// making all other nodes in the Selection children of the given node. The node
|
||||||
|
// is cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapAllNode(n *html.Node) *Selection {
|
||||||
|
if s.Size() == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap := cloneNode(n)
|
||||||
|
|
||||||
|
first := s.Nodes[0]
|
||||||
|
if first.Parent != nil {
|
||||||
|
first.Parent.InsertBefore(wrap, first)
|
||||||
|
first.Parent.RemoveChild(first)
|
||||||
|
}
|
||||||
|
|
||||||
|
for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) {
|
||||||
|
wrap = c
|
||||||
|
}
|
||||||
|
|
||||||
|
newSingleSelection(wrap, s.document).AppendSelection(s)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapInner wraps an HTML structure, matched by the given selector, around the
|
||||||
|
// content of element in the set of matched elements. The matched child is
|
||||||
|
// cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapInner(selector string) *Selection {
|
||||||
|
return s.WrapInnerMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapInnerMatcher wraps an HTML structure, matched by the given selector,
|
||||||
|
// around the content of element in the set of matched elements. The matched
|
||||||
|
// child is cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapInnerMatcher(m Matcher) *Selection {
|
||||||
|
return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapInnerSelection wraps an HTML structure, matched by the given selector,
|
||||||
|
// around the content of element in the set of matched elements. The matched
|
||||||
|
// child is cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
|
||||||
|
return s.wrapInnerNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapInnerHtml wraps an HTML structure, matched by the given selector, around
|
||||||
|
// the content of element in the set of matched elements. The matched child is
|
||||||
|
// cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapInnerHtml(html string) *Selection {
|
||||||
|
return s.wrapInnerNodes(parseHtml(html)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapInnerNode wraps an HTML structure, matched by the given selector, around
|
||||||
|
// the content of element in the set of matched elements. The matched child is
|
||||||
|
// cloned before being inserted into the document.
|
||||||
|
//
|
||||||
|
// It returns the original set of elements.
|
||||||
|
func (s *Selection) WrapInnerNode(n *html.Node) *Selection {
|
||||||
|
return s.wrapInnerNodes(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection {
|
||||||
|
if len(ns) == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Each(func(i int, s *Selection) {
|
||||||
|
contents := s.Contents()
|
||||||
|
|
||||||
|
if contents.Size() > 0 {
|
||||||
|
contents.wrapAllNodes(ns...)
|
||||||
|
} else {
|
||||||
|
s.AppendNodes(cloneNode(ns[0]))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHtml(h string) []*html.Node {
|
||||||
|
// Errors are only returned when the io.Reader returns any error besides
|
||||||
|
// EOF, but strings.Reader never will
|
||||||
|
nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode})
|
||||||
|
if err != nil {
|
||||||
|
panic("goquery: failed to parse HTML: " + err.Error())
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection {
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
for c := n.FirstChild; c != nil; c = n.FirstChild {
|
||||||
|
n.RemoveChild(c)
|
||||||
|
}
|
||||||
|
for _, c := range ns {
|
||||||
|
n.AppendChild(cloneNode(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first child that is an ElementNode
|
||||||
|
func getFirstChildEl(n *html.Node) *html.Node {
|
||||||
|
c := n.FirstChild
|
||||||
|
for c != nil && c.Type != html.ElementNode {
|
||||||
|
c = c.NextSibling
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep copy a slice of nodes.
|
||||||
|
func cloneNodes(ns []*html.Node) []*html.Node {
|
||||||
|
cns := make([]*html.Node, 0, len(ns))
|
||||||
|
|
||||||
|
for _, n := range ns {
|
||||||
|
cns = append(cns, cloneNode(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
return cns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep copy a node. The new node has clones of all the original node's
|
||||||
|
// children but none of its parents or siblings.
|
||||||
|
func cloneNode(n *html.Node) *html.Node {
|
||||||
|
nn := &html.Node{
|
||||||
|
Type: n.Type,
|
||||||
|
DataAtom: n.DataAtom,
|
||||||
|
Data: n.Data,
|
||||||
|
Attr: make([]html.Attribute, len(n.Attr)),
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(nn.Attr, n.Attr)
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
nn.AppendChild(cloneNode(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
|
||||||
|
f func(sn *html.Node, n *html.Node)) *Selection {
|
||||||
|
|
||||||
|
lasti := s.Size() - 1
|
||||||
|
|
||||||
|
// net.Html doesn't provide document fragments for insertion, so to get
|
||||||
|
// things in the correct order with After() and Prepend(), the callback
|
||||||
|
// needs to be called on the reverse of the nodes.
|
||||||
|
if reverse {
|
||||||
|
for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
ns[i], ns[j] = ns[j], ns[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, sn := range s.Nodes {
|
||||||
|
for _, n := range ns {
|
||||||
|
if i != lasti {
|
||||||
|
f(sn, cloneNode(n))
|
||||||
|
} else {
|
||||||
|
if n.Parent != nil {
|
||||||
|
n.Parent.RemoveChild(n)
|
||||||
|
}
|
||||||
|
f(sn, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
275
vendor/github.com/PuerkitoBio/goquery/property.go
generated
vendored
Normal file
275
vendor/github.com/PuerkitoBio/goquery/property.go
generated
vendored
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rxClassTrim = regexp.MustCompile("[\t\r\n]")
|
||||||
|
|
||||||
|
// Attr gets the specified attribute's value for the first element in the
|
||||||
|
// Selection. To get the value for each element individually, use a looping
|
||||||
|
// construct such as Each or Map method.
|
||||||
|
func (s *Selection) Attr(attrName string) (val string, exists bool) {
|
||||||
|
if len(s.Nodes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return getAttributeValue(attrName, s.Nodes[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttrOr works like Attr but returns default value if attribute is not present.
|
||||||
|
func (s *Selection) AttrOr(attrName, defaultValue string) string {
|
||||||
|
if len(s.Nodes) == 0 {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
val, exists := getAttributeValue(attrName, s.Nodes[0])
|
||||||
|
if !exists {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAttr removes the named attribute from each element in the set of matched elements.
|
||||||
|
func (s *Selection) RemoveAttr(attrName string) *Selection {
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
removeAttr(n, attrName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttr sets the given attribute on each element in the set of matched elements.
|
||||||
|
func (s *Selection) SetAttr(attrName, val string) *Selection {
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
attr := getAttributePtr(attrName, n)
|
||||||
|
if attr == nil {
|
||||||
|
n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val})
|
||||||
|
} else {
|
||||||
|
attr.Val = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text gets the combined text contents of each element in the set of matched
|
||||||
|
// elements, including their descendants.
|
||||||
|
func (s *Selection) Text() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Slightly optimized vs calling Each: no single selection object created
|
||||||
|
var f func(*html.Node)
|
||||||
|
f = func(n *html.Node) {
|
||||||
|
if n.Type == html.TextNode {
|
||||||
|
// Keep newlines and spaces, like jQuery
|
||||||
|
buf.WriteString(n.Data)
|
||||||
|
}
|
||||||
|
if n.FirstChild != nil {
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
f(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
f(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size is an alias for Length.
|
||||||
|
func (s *Selection) Size() int {
|
||||||
|
return s.Length()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length returns the number of elements in the Selection object.
|
||||||
|
func (s *Selection) Length() int {
|
||||||
|
return len(s.Nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Html gets the HTML contents of the first element in the set of matched
|
||||||
|
// elements. It includes text and comment nodes.
|
||||||
|
func (s *Selection) Html() (ret string, e error) {
|
||||||
|
// Since there is no .innerHtml, the HTML content must be re-created from
|
||||||
|
// the nodes using html.Render.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
if len(s.Nodes) > 0 {
|
||||||
|
for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
e = html.Render(&buf, c)
|
||||||
|
if e != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddClass adds the given class(es) to each element in the set of matched elements.
|
||||||
|
// Multiple class names can be specified, separated by a space or via multiple arguments.
|
||||||
|
func (s *Selection) AddClass(class ...string) *Selection {
|
||||||
|
classStr := strings.TrimSpace(strings.Join(class, " "))
|
||||||
|
|
||||||
|
if classStr == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
tcls := getClassesSlice(classStr)
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
curClasses, attr := getClassesAndAttr(n, true)
|
||||||
|
for _, newClass := range tcls {
|
||||||
|
if !strings.Contains(curClasses, " "+newClass+" ") {
|
||||||
|
curClasses += newClass + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setClasses(n, attr, curClasses)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasClass determines whether any of the matched elements are assigned the
|
||||||
|
// given class.
|
||||||
|
func (s *Selection) HasClass(class string) bool {
|
||||||
|
class = " " + class + " "
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
classes, _ := getClassesAndAttr(n, false)
|
||||||
|
if strings.Contains(classes, class) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveClass removes the given class(es) from each element in the set of matched elements.
|
||||||
|
// Multiple class names can be specified, separated by a space or via multiple arguments.
|
||||||
|
// If no class name is provided, all classes are removed.
|
||||||
|
func (s *Selection) RemoveClass(class ...string) *Selection {
|
||||||
|
var rclasses []string
|
||||||
|
|
||||||
|
classStr := strings.TrimSpace(strings.Join(class, " "))
|
||||||
|
remove := classStr == ""
|
||||||
|
|
||||||
|
if !remove {
|
||||||
|
rclasses = getClassesSlice(classStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
if remove {
|
||||||
|
removeAttr(n, "class")
|
||||||
|
} else {
|
||||||
|
classes, attr := getClassesAndAttr(n, true)
|
||||||
|
for _, rcl := range rclasses {
|
||||||
|
classes = strings.Replace(classes, " "+rcl+" ", " ", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
setClasses(n, attr, classes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToggleClass adds or removes the given class(es) for each element in the set of matched elements.
|
||||||
|
// Multiple class names can be specified, separated by a space or via multiple arguments.
|
||||||
|
func (s *Selection) ToggleClass(class ...string) *Selection {
|
||||||
|
classStr := strings.TrimSpace(strings.Join(class, " "))
|
||||||
|
|
||||||
|
if classStr == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
tcls := getClassesSlice(classStr)
|
||||||
|
|
||||||
|
for _, n := range s.Nodes {
|
||||||
|
classes, attr := getClassesAndAttr(n, true)
|
||||||
|
for _, tcl := range tcls {
|
||||||
|
if strings.Contains(classes, " "+tcl+" ") {
|
||||||
|
classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
|
||||||
|
} else {
|
||||||
|
classes += tcl + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setClasses(n, attr, classes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, a := range n.Attr {
|
||||||
|
if a.Key == attrName {
|
||||||
|
return &n.Attr[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private function to get the specified attribute's value from a node.
|
||||||
|
func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) {
|
||||||
|
if a := getAttributePtr(attrName, n); a != nil {
|
||||||
|
val = a.Val
|
||||||
|
exists = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get and normalize the "class" attribute from the node.
|
||||||
|
func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) {
|
||||||
|
// Applies only to element nodes
|
||||||
|
if n.Type == html.ElementNode {
|
||||||
|
attr = getAttributePtr("class", n)
|
||||||
|
if attr == nil && create {
|
||||||
|
n.Attr = append(n.Attr, html.Attribute{
|
||||||
|
Key: "class",
|
||||||
|
Val: "",
|
||||||
|
})
|
||||||
|
attr = &n.Attr[len(n.Attr)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr == nil {
|
||||||
|
classes = " "
|
||||||
|
} else {
|
||||||
|
classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClassesSlice(classes string) []string {
|
||||||
|
return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeAttr(n *html.Node, attrName string) {
|
||||||
|
for i, a := range n.Attr {
|
||||||
|
if a.Key == attrName {
|
||||||
|
n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr =
|
||||||
|
n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setClasses(n *html.Node, attr *html.Attribute, classes string) {
|
||||||
|
classes = strings.TrimSpace(classes)
|
||||||
|
if classes == "" {
|
||||||
|
removeAttr(n, "class")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attr.Val = classes
|
||||||
|
}
|
||||||
49
vendor/github.com/PuerkitoBio/goquery/query.go
generated
vendored
Normal file
49
vendor/github.com/PuerkitoBio/goquery/query.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import "golang.org/x/net/html"
|
||||||
|
|
||||||
|
// Is checks the current matched set of elements against a selector and
|
||||||
|
// returns true if at least one of these elements matches.
|
||||||
|
func (s *Selection) Is(selector string) bool {
|
||||||
|
return s.IsMatcher(compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMatcher checks the current matched set of elements against a matcher and
|
||||||
|
// returns true if at least one of these elements matches.
|
||||||
|
func (s *Selection) IsMatcher(m Matcher) bool {
|
||||||
|
if len(s.Nodes) > 0 {
|
||||||
|
if len(s.Nodes) == 1 {
|
||||||
|
return m.Match(s.Nodes[0])
|
||||||
|
}
|
||||||
|
return len(m.Filter(s.Nodes)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFunction checks the current matched set of elements against a predicate and
|
||||||
|
// returns true if at least one of these elements matches.
|
||||||
|
func (s *Selection) IsFunction(f func(int, *Selection) bool) bool {
|
||||||
|
return s.FilterFunction(f).Length() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSelection checks the current matched set of elements against a Selection object
|
||||||
|
// and returns true if at least one of these elements matches.
|
||||||
|
func (s *Selection) IsSelection(sel *Selection) bool {
|
||||||
|
return s.FilterSelection(sel).Length() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNodes checks the current matched set of elements against the specified nodes
|
||||||
|
// and returns true if at least one of these elements matches.
|
||||||
|
func (s *Selection) IsNodes(nodes ...*html.Node) bool {
|
||||||
|
return s.FilterNodes(nodes...).Length() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the specified Node is within,
|
||||||
|
// at any depth, one of the nodes in the Selection object.
|
||||||
|
// It is NOT inclusive, to behave like jQuery's implementation, and
|
||||||
|
// unlike Javascript's .contains, so if the contained
|
||||||
|
// node is itself in the selection, it returns false.
|
||||||
|
func (s *Selection) Contains(n *html.Node) bool {
|
||||||
|
return sliceContains(s.Nodes, n)
|
||||||
|
}
|
||||||
698
vendor/github.com/PuerkitoBio/goquery/traversal.go
generated
vendored
Normal file
698
vendor/github.com/PuerkitoBio/goquery/traversal.go
generated
vendored
Normal file
@@ -0,0 +1,698 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import "golang.org/x/net/html"
|
||||||
|
|
||||||
|
type siblingType int
|
||||||
|
|
||||||
|
// Sibling type, used internally when iterating over children at the same
|
||||||
|
// level (siblings) to specify which nodes are requested.
|
||||||
|
const (
|
||||||
|
siblingPrevUntil siblingType = iota - 3
|
||||||
|
siblingPrevAll
|
||||||
|
siblingPrev
|
||||||
|
siblingAll
|
||||||
|
siblingNext
|
||||||
|
siblingNextAll
|
||||||
|
siblingNextUntil
|
||||||
|
siblingAllIncludingNonElements
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find gets the descendants of each element in the current set of matched
|
||||||
|
// elements, filtered by a selector. It returns a new Selection object
|
||||||
|
// containing these matched elements.
|
||||||
|
func (s *Selection) Find(selector string) *Selection {
|
||||||
|
return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMatcher gets the descendants of each element in the current set of matched
|
||||||
|
// elements, filtered by the matcher. It returns a new Selection object
|
||||||
|
// containing these matched elements.
|
||||||
|
func (s *Selection) FindMatcher(m Matcher) *Selection {
|
||||||
|
return pushStack(s, findWithMatcher(s.Nodes, m))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindSelection gets the descendants of each element in the current
|
||||||
|
// Selection, filtered by a Selection. It returns a new Selection object
|
||||||
|
// containing these matched elements.
|
||||||
|
func (s *Selection) FindSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return pushStack(s, nil)
|
||||||
|
}
|
||||||
|
return s.FindNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindNodes gets the descendants of each element in the current
|
||||||
|
// Selection, filtered by some nodes. It returns a new Selection object
|
||||||
|
// containing these matched elements.
|
||||||
|
func (s *Selection) FindNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
||||||
|
if sliceContains(s.Nodes, n) {
|
||||||
|
return []*html.Node{n}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contents gets the children of each element in the Selection,
|
||||||
|
// including text and comment nodes. It returns a new Selection object
|
||||||
|
// containing these elements.
|
||||||
|
func (s *Selection) Contents() *Selection {
|
||||||
|
return pushStack(s, getChildrenNodes(s.Nodes, siblingAllIncludingNonElements))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentsFiltered gets the children of each element in the Selection,
|
||||||
|
// filtered by the specified selector. It returns a new Selection
|
||||||
|
// object containing these elements. Since selectors only act on Element nodes,
|
||||||
|
// this function is an alias to ChildrenFiltered unless the selector is empty,
|
||||||
|
// in which case it is an alias to Contents.
|
||||||
|
func (s *Selection) ContentsFiltered(selector string) *Selection {
|
||||||
|
if selector != "" {
|
||||||
|
return s.ChildrenFiltered(selector)
|
||||||
|
}
|
||||||
|
return s.Contents()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentsMatcher gets the children of each element in the Selection,
|
||||||
|
// filtered by the specified matcher. It returns a new Selection
|
||||||
|
// object containing these elements. Since matchers only act on Element nodes,
|
||||||
|
// this function is an alias to ChildrenMatcher.
|
||||||
|
func (s *Selection) ContentsMatcher(m Matcher) *Selection {
|
||||||
|
return s.ChildrenMatcher(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Children gets the child elements of each element in the Selection.
|
||||||
|
// It returns a new Selection object containing these elements.
|
||||||
|
func (s *Selection) Children() *Selection {
|
||||||
|
return pushStack(s, getChildrenNodes(s.Nodes, siblingAll))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChildrenFiltered gets the child elements of each element in the Selection,
|
||||||
|
// filtered by the specified selector. It returns a new
|
||||||
|
// Selection object containing these elements.
|
||||||
|
func (s *Selection) ChildrenFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChildrenMatcher gets the child elements of each element in the Selection,
|
||||||
|
// filtered by the specified matcher. It returns a new
|
||||||
|
// Selection object containing these elements.
|
||||||
|
func (s *Selection) ChildrenMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent gets the parent of each element in the Selection. It returns a
|
||||||
|
// new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) Parent() *Selection {
|
||||||
|
return pushStack(s, getParentNodes(s.Nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentFiltered gets the parent of each element in the Selection filtered by a
|
||||||
|
// selector. It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getParentNodes(s.Nodes), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentMatcher gets the parent of each element in the Selection filtered by a
|
||||||
|
// matcher. It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getParentNodes(s.Nodes), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closest gets the first element that matches the selector by testing the
|
||||||
|
// element itself and traversing up through its ancestors in the DOM tree.
|
||||||
|
func (s *Selection) Closest(selector string) *Selection {
|
||||||
|
cs := compileMatcher(selector)
|
||||||
|
return s.ClosestMatcher(cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClosestMatcher gets the first element that matches the matcher by testing the
|
||||||
|
// element itself and traversing up through its ancestors in the DOM tree.
|
||||||
|
func (s *Selection) ClosestMatcher(m Matcher) *Selection {
|
||||||
|
return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
|
||||||
|
// For each node in the selection, test the node itself, then each parent
|
||||||
|
// until a match is found.
|
||||||
|
for ; n != nil; n = n.Parent {
|
||||||
|
if m.Match(n) {
|
||||||
|
return []*html.Node{n}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClosestNodes gets the first element that matches one of the nodes by testing the
|
||||||
|
// element itself and traversing up through its ancestors in the DOM tree.
|
||||||
|
func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
|
||||||
|
set := make(map[*html.Node]bool)
|
||||||
|
for _, n := range nodes {
|
||||||
|
set[n] = true
|
||||||
|
}
|
||||||
|
return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
|
||||||
|
// For each node in the selection, test the node itself, then each parent
|
||||||
|
// until a match is found.
|
||||||
|
for ; n != nil; n = n.Parent {
|
||||||
|
if set[n] {
|
||||||
|
return []*html.Node{n}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClosestSelection gets the first element that matches one of the nodes in the
|
||||||
|
// Selection by testing the element itself and traversing up through its ancestors
|
||||||
|
// in the DOM tree.
|
||||||
|
func (s *Selection) ClosestSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return pushStack(s, nil)
|
||||||
|
}
|
||||||
|
return s.ClosestNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parents gets the ancestors of each element in the current Selection. It
|
||||||
|
// returns a new Selection object with the matched elements.
|
||||||
|
func (s *Selection) Parents() *Selection {
|
||||||
|
return pushStack(s, getParentsNodes(s.Nodes, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsFiltered gets the ancestors of each element in the current
|
||||||
|
// Selection. It returns a new Selection object with the matched elements.
|
||||||
|
func (s *Selection) ParentsFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsMatcher gets the ancestors of each element in the current
|
||||||
|
// Selection. It returns a new Selection object with the matched elements.
|
||||||
|
func (s *Selection) ParentsMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsUntil gets the ancestors of each element in the Selection, up to but
|
||||||
|
// not including the element matched by the selector. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsUntil(selector string) *Selection {
|
||||||
|
return pushStack(s, getParentsNodes(s.Nodes, compileMatcher(selector), nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but
|
||||||
|
// not including the element matched by the matcher. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsUntilMatcher(m Matcher) *Selection {
|
||||||
|
return pushStack(s, getParentsNodes(s.Nodes, m, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsUntilSelection gets the ancestors of each element in the Selection,
|
||||||
|
// up to but not including the elements in the specified Selection. It returns a
|
||||||
|
// new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.Parents()
|
||||||
|
}
|
||||||
|
return s.ParentsUntilNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsUntilNodes gets the ancestors of each element in the Selection,
|
||||||
|
// up to but not including the specified nodes. It returns a
|
||||||
|
// new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return pushStack(s, getParentsNodes(s.Nodes, nil, nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsFilteredUntil is like ParentsUntil, with the option to filter the
|
||||||
|
// results based on a selector string. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection {
|
||||||
|
return filterAndPush(s, getParentsNodes(s.Nodes, compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the
|
||||||
|
// results based on a matcher. It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getParentsNodes(s.Nodes, until, nil), filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsFilteredUntilSelection is like ParentsUntilSelection, with the
|
||||||
|
// option to filter the results based on a selector string. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
|
||||||
|
return s.ParentsMatcherUntilSelection(compileMatcher(filterSelector), sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsMatcherUntilSelection is like ParentsUntilSelection, with the
|
||||||
|
// option to filter the results based on a matcher. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.ParentsMatcher(filter)
|
||||||
|
}
|
||||||
|
return s.ParentsMatcherUntilNodes(filter, sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsFilteredUntilNodes is like ParentsUntilNodes, with the
|
||||||
|
// option to filter the results based on a selector string. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
||||||
|
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), compileMatcher(filterSelector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsMatcherUntilNodes is like ParentsUntilNodes, with the
|
||||||
|
// option to filter the results based on a matcher. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) ParentsMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
|
||||||
|
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Siblings gets the siblings of each element in the Selection. It returns
|
||||||
|
// a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) Siblings() *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiblingsFiltered gets the siblings of each element in the Selection
|
||||||
|
// filtered by a selector. It returns a new Selection object containing the
|
||||||
|
// matched elements.
|
||||||
|
func (s *Selection) SiblingsFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiblingsMatcher gets the siblings of each element in the Selection
|
||||||
|
// filtered by a matcher. It returns a new Selection object containing the
|
||||||
|
// matched elements.
|
||||||
|
func (s *Selection) SiblingsMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next gets the immediately following sibling of each element in the
|
||||||
|
// Selection. It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) Next() *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextFiltered gets the immediately following sibling of each element in the
|
||||||
|
// Selection filtered by a selector. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) NextFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextMatcher gets the immediately following sibling of each element in the
|
||||||
|
// Selection filtered by a matcher. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) NextMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextAll gets all the following siblings of each element in the
|
||||||
|
// Selection. It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) NextAll() *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextAllFiltered gets all the following siblings of each element in the
|
||||||
|
// Selection filtered by a selector. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) NextAllFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextAllMatcher gets all the following siblings of each element in the
|
||||||
|
// Selection filtered by a matcher. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) NextAllMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev gets the immediately preceding sibling of each element in the
|
||||||
|
// Selection. It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) Prev() *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevFiltered gets the immediately preceding sibling of each element in the
|
||||||
|
// Selection filtered by a selector. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) PrevFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevMatcher gets the immediately preceding sibling of each element in the
|
||||||
|
// Selection filtered by a matcher. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) PrevMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevAll gets all the preceding siblings of each element in the
|
||||||
|
// Selection. It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) PrevAll() *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevAllFiltered gets all the preceding siblings of each element in the
|
||||||
|
// Selection filtered by a selector. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) PrevAllFiltered(selector string) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), compileMatcher(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevAllMatcher gets all the preceding siblings of each element in the
|
||||||
|
// Selection filtered by a matcher. It returns a new Selection object
|
||||||
|
// containing the matched elements.
|
||||||
|
func (s *Selection) PrevAllMatcher(m Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextUntil gets all following siblings of each element up to but not
|
||||||
|
// including the element matched by the selector. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) NextUntil(selector string) *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
||||||
|
compileMatcher(selector), nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextUntilMatcher gets all following siblings of each element up to but not
|
||||||
|
// including the element matched by the matcher. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) NextUntilMatcher(m Matcher) *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
||||||
|
m, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextUntilSelection gets all following siblings of each element up to but not
|
||||||
|
// including the element matched by the Selection. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) NextUntilSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.NextAll()
|
||||||
|
}
|
||||||
|
return s.NextUntilNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextUntilNodes gets all following siblings of each element up to but not
|
||||||
|
// including the element matched by the nodes. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
||||||
|
nil, nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevUntil gets all preceding siblings of each element up to but not
|
||||||
|
// including the element matched by the selector. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) PrevUntil(selector string) *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
||||||
|
compileMatcher(selector), nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevUntilMatcher gets all preceding siblings of each element up to but not
|
||||||
|
// including the element matched by the matcher. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) PrevUntilMatcher(m Matcher) *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
||||||
|
m, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevUntilSelection gets all preceding siblings of each element up to but not
|
||||||
|
// including the element matched by the Selection. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) PrevUntilSelection(sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.PrevAll()
|
||||||
|
}
|
||||||
|
return s.PrevUntilNodes(sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevUntilNodes gets all preceding siblings of each element up to but not
|
||||||
|
// including the element matched by the nodes. It returns a new Selection
|
||||||
|
// object containing the matched elements.
|
||||||
|
func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
|
||||||
|
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
||||||
|
nil, nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextFilteredUntil is like NextUntil, with the option to filter
|
||||||
|
// the results based on a selector string.
|
||||||
|
// It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
||||||
|
compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter
|
||||||
|
// the results based on a matcher.
|
||||||
|
// It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
||||||
|
until, nil), filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextFilteredUntilSelection is like NextUntilSelection, with the
|
||||||
|
// option to filter the results based on a selector string. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
|
||||||
|
return s.NextMatcherUntilSelection(compileMatcher(filterSelector), sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextMatcherUntilSelection is like NextUntilSelection, with the
|
||||||
|
// option to filter the results based on a matcher. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.NextMatcher(filter)
|
||||||
|
}
|
||||||
|
return s.NextMatcherUntilNodes(filter, sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextFilteredUntilNodes is like NextUntilNodes, with the
|
||||||
|
// option to filter the results based on a selector string. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
||||||
|
nil, nodes), compileMatcher(filterSelector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextMatcherUntilNodes is like NextUntilNodes, with the
|
||||||
|
// option to filter the results based on a matcher. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
||||||
|
nil, nodes), filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevFilteredUntil is like PrevUntil, with the option to filter
|
||||||
|
// the results based on a selector string.
|
||||||
|
// It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
||||||
|
compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter
|
||||||
|
// the results based on a matcher.
|
||||||
|
// It returns a new Selection object containing the matched elements.
|
||||||
|
func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
||||||
|
until, nil), filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevFilteredUntilSelection is like PrevUntilSelection, with the
|
||||||
|
// option to filter the results based on a selector string. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
|
||||||
|
return s.PrevMatcherUntilSelection(compileMatcher(filterSelector), sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevMatcherUntilSelection is like PrevUntilSelection, with the
|
||||||
|
// option to filter the results based on a matcher. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
|
||||||
|
if sel == nil {
|
||||||
|
return s.PrevMatcher(filter)
|
||||||
|
}
|
||||||
|
return s.PrevMatcherUntilNodes(filter, sel.Nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevFilteredUntilNodes is like PrevUntilNodes, with the
|
||||||
|
// option to filter the results based on a selector string. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
||||||
|
nil, nodes), compileMatcher(filterSelector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevMatcherUntilNodes is like PrevUntilNodes, with the
|
||||||
|
// option to filter the results based on a matcher. It returns a new
|
||||||
|
// Selection object containing the matched elements.
|
||||||
|
func (s *Selection) PrevMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
|
||||||
|
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
||||||
|
nil, nodes), filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter and push filters the nodes based on a matcher, and pushes the results
|
||||||
|
// on the stack, with the srcSel as previous selection.
|
||||||
|
func filterAndPush(srcSel *Selection, nodes []*html.Node, m Matcher) *Selection {
|
||||||
|
// Create a temporary Selection with the specified nodes to filter using winnow
|
||||||
|
sel := &Selection{nodes, srcSel.document, nil}
|
||||||
|
// Filter based on matcher and push on stack
|
||||||
|
return pushStack(srcSel, winnow(sel, m, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal implementation of Find that return raw nodes.
|
||||||
|
func findWithMatcher(nodes []*html.Node, m Matcher) []*html.Node {
|
||||||
|
// Map nodes to find the matches within the children of each node
|
||||||
|
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
|
||||||
|
// Go down one level, becausejQuery's Find selects only within descendants
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if c.Type == html.ElementNode {
|
||||||
|
result = append(result, m.MatchAll(c)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal implementation to get all parent nodes, stopping at the specified
|
||||||
|
// node (or nil if no stop).
|
||||||
|
func getParentsNodes(nodes []*html.Node, stopm Matcher, stopNodes []*html.Node) []*html.Node {
|
||||||
|
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
|
||||||
|
for p := n.Parent; p != nil; p = p.Parent {
|
||||||
|
sel := newSingleSelection(p, nil)
|
||||||
|
if stopm != nil {
|
||||||
|
if sel.IsMatcher(stopm) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if len(stopNodes) > 0 {
|
||||||
|
if sel.IsNodes(stopNodes...) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Type == html.ElementNode {
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal implementation of sibling nodes that return a raw slice of matches.
|
||||||
|
func getSiblingNodes(nodes []*html.Node, st siblingType, untilm Matcher, untilNodes []*html.Node) []*html.Node {
|
||||||
|
var f func(*html.Node) bool
|
||||||
|
|
||||||
|
// If the requested siblings are ...Until, create the test function to
|
||||||
|
// determine if the until condition is reached (returns true if it is)
|
||||||
|
if st == siblingNextUntil || st == siblingPrevUntil {
|
||||||
|
f = func(n *html.Node) bool {
|
||||||
|
if untilm != nil {
|
||||||
|
// Matcher-based condition
|
||||||
|
sel := newSingleSelection(n, nil)
|
||||||
|
return sel.IsMatcher(untilm)
|
||||||
|
} else if len(untilNodes) > 0 {
|
||||||
|
// Nodes-based condition
|
||||||
|
sel := newSingleSelection(n, nil)
|
||||||
|
return sel.IsNodes(untilNodes...)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
||||||
|
return getChildrenWithSiblingType(n.Parent, st, n, f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the children nodes of each node in the specified slice of nodes,
|
||||||
|
// based on the sibling type request.
|
||||||
|
func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node {
|
||||||
|
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
||||||
|
return getChildrenWithSiblingType(n, st, nil, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the children of the specified parent, based on the requested sibling
|
||||||
|
// type, skipping a specified node if required.
|
||||||
|
func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node,
|
||||||
|
untilFunc func(*html.Node) bool) (result []*html.Node) {
|
||||||
|
|
||||||
|
// Create the iterator function
|
||||||
|
var iter = func(cur *html.Node) (ret *html.Node) {
|
||||||
|
// Based on the sibling type requested, iterate the right way
|
||||||
|
for {
|
||||||
|
switch st {
|
||||||
|
case siblingAll, siblingAllIncludingNonElements:
|
||||||
|
if cur == nil {
|
||||||
|
// First iteration, start with first child of parent
|
||||||
|
// Skip node if required
|
||||||
|
if ret = parent.FirstChild; ret == skipNode && skipNode != nil {
|
||||||
|
ret = skipNode.NextSibling
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Skip node if required
|
||||||
|
if ret = cur.NextSibling; ret == skipNode && skipNode != nil {
|
||||||
|
ret = skipNode.NextSibling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case siblingPrev, siblingPrevAll, siblingPrevUntil:
|
||||||
|
if cur == nil {
|
||||||
|
// Start with previous sibling of the skip node
|
||||||
|
ret = skipNode.PrevSibling
|
||||||
|
} else {
|
||||||
|
ret = cur.PrevSibling
|
||||||
|
}
|
||||||
|
case siblingNext, siblingNextAll, siblingNextUntil:
|
||||||
|
if cur == nil {
|
||||||
|
// Start with next sibling of the skip node
|
||||||
|
ret = skipNode.NextSibling
|
||||||
|
} else {
|
||||||
|
ret = cur.NextSibling
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("Invalid sibling type.")
|
||||||
|
}
|
||||||
|
if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Not a valid node, try again from this one
|
||||||
|
cur = ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for c := iter(nil); c != nil; c = iter(c) {
|
||||||
|
// If this is an ...Until case, test before append (returns true
|
||||||
|
// if the until condition is reached)
|
||||||
|
if st == siblingNextUntil || st == siblingPrevUntil {
|
||||||
|
if untilFunc(c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, c)
|
||||||
|
if st == siblingNext || st == siblingPrev {
|
||||||
|
// Only one node was requested (immediate next or previous), so exit
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal implementation of parent nodes that return a raw slice of Nodes.
|
||||||
|
func getParentNodes(nodes []*html.Node) []*html.Node {
|
||||||
|
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
||||||
|
if n.Parent != nil && n.Parent.Type == html.ElementNode {
|
||||||
|
return []*html.Node{n.Parent}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal map function used by many traversing methods. Takes the source nodes
|
||||||
|
// to iterate on and the mapping function that returns an array of nodes.
|
||||||
|
// Returns an array of nodes mapped by calling the callback function once for
|
||||||
|
// each node in the source nodes.
|
||||||
|
func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
|
||||||
|
set := make(map[*html.Node]bool)
|
||||||
|
for i, n := range nodes {
|
||||||
|
if vals := f(i, n); len(vals) > 0 {
|
||||||
|
result = appendWithoutDuplicates(result, vals, set)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
141
vendor/github.com/PuerkitoBio/goquery/type.go
generated
vendored
Normal file
141
vendor/github.com/PuerkitoBio/goquery/type.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/andybalholm/cascadia"
|
||||||
|
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Document represents an HTML document to be manipulated. Unlike jQuery, which
|
||||||
|
// is loaded as part of a DOM document, and thus acts upon its containing
|
||||||
|
// document, GoQuery doesn't know which HTML document to act upon. So it needs
|
||||||
|
// to be told, and that's what the Document class is for. It holds the root
|
||||||
|
// document node to manipulate, and can make selections on this document.
|
||||||
|
type Document struct {
|
||||||
|
*Selection
|
||||||
|
Url *url.URL
|
||||||
|
rootNode *html.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDocumentFromNode is a Document constructor that takes a root html Node
|
||||||
|
// as argument.
|
||||||
|
func NewDocumentFromNode(root *html.Node) *Document {
|
||||||
|
return newDocument(root, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDocument is a Document constructor that takes a string URL as argument.
|
||||||
|
// It loads the specified document, parses it, and stores the root Document
|
||||||
|
// node, ready to be manipulated.
|
||||||
|
//
|
||||||
|
// Deprecated: Use the net/http standard library package to make the request
|
||||||
|
// and validate the response before calling goquery.NewDocumentFromReader
|
||||||
|
// with the response's body.
|
||||||
|
func NewDocument(url string) (*Document, error) {
|
||||||
|
// Load the URL
|
||||||
|
res, e := http.Get(url)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return NewDocumentFromResponse(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDocumentFromReader returns a Document from an io.Reader.
|
||||||
|
// It returns an error as second value if the reader's data cannot be parsed
|
||||||
|
// as html. It does not check if the reader is also an io.Closer, the
|
||||||
|
// provided reader is never closed by this call. It is the responsibility
|
||||||
|
// of the caller to close it if required.
|
||||||
|
func NewDocumentFromReader(r io.Reader) (*Document, error) {
|
||||||
|
root, e := html.Parse(r)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return newDocument(root, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDocumentFromResponse is another Document constructor that takes an http response as argument.
|
||||||
|
// It loads the specified response's document, parses it, and stores the root Document
|
||||||
|
// node, ready to be manipulated. The response's body is closed on return.
|
||||||
|
//
|
||||||
|
// Deprecated: Use goquery.NewDocumentFromReader with the response's body.
|
||||||
|
func NewDocumentFromResponse(res *http.Response) (*Document, error) {
|
||||||
|
if res == nil {
|
||||||
|
return nil, errors.New("Response is nil")
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.Request == nil {
|
||||||
|
return nil, errors.New("Response.Request is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the HTML into nodes
|
||||||
|
root, e := html.Parse(res.Body)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and fill the document
|
||||||
|
return newDocument(root, res.Request.URL), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneDocument creates a deep-clone of a document.
|
||||||
|
func CloneDocument(doc *Document) *Document {
|
||||||
|
return newDocument(cloneNode(doc.rootNode), doc.Url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private constructor, make sure all fields are correctly filled.
|
||||||
|
func newDocument(root *html.Node, url *url.URL) *Document {
|
||||||
|
// Create and fill the document
|
||||||
|
d := &Document{nil, url, root}
|
||||||
|
d.Selection = newSingleSelection(root, d)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selection represents a collection of nodes matching some criteria. The
|
||||||
|
// initial Selection can be created by using Document.Find, and then
|
||||||
|
// manipulated using the jQuery-like chainable syntax and methods.
|
||||||
|
type Selection struct {
|
||||||
|
Nodes []*html.Node
|
||||||
|
document *Document
|
||||||
|
prevSel *Selection
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper constructor to create an empty selection
|
||||||
|
func newEmptySelection(doc *Document) *Selection {
|
||||||
|
return &Selection{nil, doc, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper constructor to create a selection of only one node
|
||||||
|
func newSingleSelection(node *html.Node, doc *Document) *Selection {
|
||||||
|
return &Selection{[]*html.Node{node}, doc, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matcher is an interface that defines the methods to match
|
||||||
|
// HTML nodes against a compiled selector string. Cascadia's
|
||||||
|
// Selector implements this interface.
|
||||||
|
type Matcher interface {
|
||||||
|
Match(*html.Node) bool
|
||||||
|
MatchAll(*html.Node) []*html.Node
|
||||||
|
Filter([]*html.Node) []*html.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
// compileMatcher compiles the selector string s and returns
|
||||||
|
// the corresponding Matcher. If s is an invalid selector string,
|
||||||
|
// it returns a Matcher that fails all matches.
|
||||||
|
func compileMatcher(s string) Matcher {
|
||||||
|
cs, err := cascadia.Compile(s)
|
||||||
|
if err != nil {
|
||||||
|
return invalidMatcher{}
|
||||||
|
}
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalidMatcher is a Matcher that always fails to match.
|
||||||
|
type invalidMatcher struct{}
|
||||||
|
|
||||||
|
func (invalidMatcher) Match(n *html.Node) bool { return false }
|
||||||
|
func (invalidMatcher) MatchAll(n *html.Node) []*html.Node { return nil }
|
||||||
|
func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil }
|
||||||
161
vendor/github.com/PuerkitoBio/goquery/utilities.go
generated
vendored
Normal file
161
vendor/github.com/PuerkitoBio/goquery/utilities.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package goquery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
// used to determine if a set (map[*html.Node]bool) should be used
|
||||||
|
// instead of iterating over a slice. The set uses more memory and
|
||||||
|
// is slower than slice iteration for small N.
|
||||||
|
const minNodesForSet = 1000
|
||||||
|
|
||||||
|
var nodeNames = []string{
|
||||||
|
html.ErrorNode: "#error",
|
||||||
|
html.TextNode: "#text",
|
||||||
|
html.DocumentNode: "#document",
|
||||||
|
html.CommentNode: "#comment",
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeName returns the node name of the first element in the selection.
|
||||||
|
// It tries to behave in a similar way as the DOM's nodeName property
|
||||||
|
// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
|
||||||
|
//
|
||||||
|
// Go's net/html package defines the following node types, listed with
|
||||||
|
// the corresponding returned value from this function:
|
||||||
|
//
|
||||||
|
// ErrorNode : #error
|
||||||
|
// TextNode : #text
|
||||||
|
// DocumentNode : #document
|
||||||
|
// ElementNode : the element's tag name
|
||||||
|
// CommentNode : #comment
|
||||||
|
// DoctypeNode : the name of the document type
|
||||||
|
//
|
||||||
|
func NodeName(s *Selection) string {
|
||||||
|
if s.Length() == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch n := s.Get(0); n.Type {
|
||||||
|
case html.ElementNode, html.DoctypeNode:
|
||||||
|
return n.Data
|
||||||
|
default:
|
||||||
|
if n.Type >= 0 && int(n.Type) < len(nodeNames) {
|
||||||
|
return nodeNames[n.Type]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OuterHtml returns the outer HTML rendering of the first item in
|
||||||
|
// the selection - that is, the HTML including the first element's
|
||||||
|
// tag and attributes.
|
||||||
|
//
|
||||||
|
// Unlike InnerHtml, this is a function and not a method on the Selection,
|
||||||
|
// because this is not a jQuery method (in javascript-land, this is
|
||||||
|
// a property provided by the DOM).
|
||||||
|
func OuterHtml(s *Selection) (string, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
if s.Length() == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
n := s.Get(0)
|
||||||
|
if err := html.Render(&buf, n); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all container nodes to search for the target node.
|
||||||
|
func sliceContains(container []*html.Node, contained *html.Node) bool {
|
||||||
|
for _, n := range container {
|
||||||
|
if nodeContains(n, contained) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the contained node is within the container node.
|
||||||
|
func nodeContains(container *html.Node, contained *html.Node) bool {
|
||||||
|
// Check if the parent of the contained node is the container node, traversing
|
||||||
|
// upward until the top is reached, or the container is found.
|
||||||
|
for contained = contained.Parent; contained != nil; contained = contained.Parent {
|
||||||
|
if container == contained {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the target node is in the slice of nodes.
|
||||||
|
func isInSlice(slice []*html.Node, node *html.Node) bool {
|
||||||
|
return indexInSlice(slice, node) > -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the index of the target node in the slice, or -1.
|
||||||
|
func indexInSlice(slice []*html.Node, node *html.Node) int {
|
||||||
|
if node != nil {
|
||||||
|
for i, n := range slice {
|
||||||
|
if n == node {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appends the new nodes to the target slice, making sure no duplicate is added.
|
||||||
|
// There is no check to the original state of the target slice, so it may still
|
||||||
|
// contain duplicates. The target slice is returned because append() may create
|
||||||
|
// a new underlying array. If targetSet is nil, a local set is created with the
|
||||||
|
// target if len(target) + len(nodes) is greater than minNodesForSet.
|
||||||
|
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node {
|
||||||
|
// if there are not that many nodes, don't use the map, faster to just use nested loops
|
||||||
|
// (unless a non-nil targetSet is passed, in which case the caller knows better).
|
||||||
|
if targetSet == nil && len(target)+len(nodes) < minNodesForSet {
|
||||||
|
for _, n := range nodes {
|
||||||
|
if !isInSlice(target, n) {
|
||||||
|
target = append(target, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a targetSet is passed, then assume it is reliable, otherwise create one
|
||||||
|
// and initialize it with the current target contents.
|
||||||
|
if targetSet == nil {
|
||||||
|
targetSet = make(map[*html.Node]bool, len(target))
|
||||||
|
for _, n := range target {
|
||||||
|
targetSet[n] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, n := range nodes {
|
||||||
|
if !targetSet[n] {
|
||||||
|
target = append(target, n)
|
||||||
|
targetSet[n] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through a selection, returning only those nodes that pass the predicate
|
||||||
|
// function.
|
||||||
|
func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
|
||||||
|
for i, n := range sel.Nodes {
|
||||||
|
if predicate(i, newSingleSelection(n, sel.document)) {
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new Selection object based on the specified nodes, and keeps the
|
||||||
|
// source Selection object on the stack (linked list).
|
||||||
|
func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
|
||||||
|
result := &Selection{nodes, fromSel.document, fromSel}
|
||||||
|
return result
|
||||||
|
}
|
||||||
191
vendor/github.com/Unknwon/goconfig/LICENSE
generated
vendored
Executable file
191
vendor/github.com/Unknwon/goconfig/LICENSE
generated
vendored
Executable file
@@ -0,0 +1,191 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, and
|
||||||
|
distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, "control" means (i) the power, direct or
|
||||||
|
indirect, to cause the direction or management of such entity, whether by
|
||||||
|
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||||
|
permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical transformation or
|
||||||
|
translation of a Source form, including but not limited to compiled object code,
|
||||||
|
generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||||
|
available under the License, as indicated by a copyright notice that is included
|
||||||
|
in or attached to the work (an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||||
|
is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative Works
|
||||||
|
shall not include works that remain separable from, or merely link (or bind by
|
||||||
|
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative Works
|
||||||
|
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||||
|
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||||
|
on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
"submitted" means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||||
|
the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently
|
||||||
|
incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License.
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||||
|
Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License.
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable (except as stated in this section) patent license to make, have
|
||||||
|
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||||
|
such license applies only to those patent claims licensable by such Contributor
|
||||||
|
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||||
|
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||||
|
submitted. If You institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||||
|
Contribution incorporated within the Work constitutes direct or contributory
|
||||||
|
patent infringement, then any patent licenses granted to You under this License
|
||||||
|
for that Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution.
|
||||||
|
|
||||||
|
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||||
|
in any medium, with or without modifications, and in Source or Object form,
|
||||||
|
provided that You meet the following conditions:
|
||||||
|
|
||||||
|
You must give any other recipients of the Work or Derivative Works a copy of
|
||||||
|
this License; and
|
||||||
|
You must cause any modified files to carry prominent notices stating that You
|
||||||
|
changed the files; and
|
||||||
|
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source form
|
||||||
|
of the Work, excluding those notices that do not pertain to any part of the
|
||||||
|
Derivative Works; and
|
||||||
|
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||||
|
Derivative Works that You distribute must include a readable copy of the
|
||||||
|
attribution notices contained within such NOTICE file, excluding those notices
|
||||||
|
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||||
|
following places: within a NOTICE text file distributed as part of the
|
||||||
|
Derivative Works; within the Source form or documentation, if provided along
|
||||||
|
with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents of
|
||||||
|
the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works that
|
||||||
|
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||||
|
provided that such additional attribution notices cannot be construed as
|
||||||
|
modifying the License.
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction, or
|
||||||
|
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||||
|
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||||
|
with the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions.
|
||||||
|
|
||||||
|
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||||
|
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||||
|
conditions of this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||||
|
any separate license agreement you may have executed with Licensor regarding
|
||||||
|
such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks.
|
||||||
|
|
||||||
|
This License does not grant permission to use the trade names, trademarks,
|
||||||
|
service marks, or product names of the Licensor, except as required for
|
||||||
|
reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||||
|
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||||
|
including, without limitation, any warranties or conditions of TITLE,
|
||||||
|
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||||
|
solely responsible for determining the appropriateness of using or
|
||||||
|
redistributing the Work and assume any risks associated with Your exercise of
|
||||||
|
permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability.
|
||||||
|
|
||||||
|
In no event and under no legal theory, whether in tort (including negligence),
|
||||||
|
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||||
|
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special, incidental,
|
||||||
|
or consequential damages of any character arising as a result of this License or
|
||||||
|
out of the use or inability to use the Work (including but not limited to
|
||||||
|
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||||
|
any and all other commercial damages or losses), even if such Contributor has
|
||||||
|
been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability.
|
||||||
|
|
||||||
|
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||||
|
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||||
|
other liability obligations and/or rights consistent with this License. However,
|
||||||
|
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||||
|
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||||
|
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason of your
|
||||||
|
accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||||
|
identifying information. (Don't include the brackets!) The text should be
|
||||||
|
enclosed in the appropriate comment syntax for the file format. We also
|
||||||
|
recommend that a file or class name and description of purpose be included on
|
||||||
|
the same "printed page" as the copyright notice for easier identification within
|
||||||
|
third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
72
vendor/github.com/Unknwon/goconfig/README.md
generated
vendored
Executable file
72
vendor/github.com/Unknwon/goconfig/README.md
generated
vendored
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
goconfig [](https://drone.io/github.com/Unknwon/goconfig/latest) [](http://gowalker.org/github.com/Unknwon/goconfig)
|
||||||
|
========
|
||||||
|
|
||||||
|
[中文文档](README_ZH.md)
|
||||||
|
|
||||||
|
**IMPORTANT**
|
||||||
|
|
||||||
|
- This library is under bug fix only mode, which means no more features will be added.
|
||||||
|
- I'm continuing working on better Go code with a different library: [ini](https://github.com/go-ini/ini).
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Package goconfig is a easy-use, comments-support configuration file parser for the Go Programming Language, which provides a structure similar to what you would find on Microsoft Windows INI files.
|
||||||
|
|
||||||
|
The configuration file consists of sections, led by a `[section]` header and followed by `name:value` or `name=value` entries. Note that leading whitespace is removed from values. The optional values can contain format strings which refer to other values in the same section, or values in a special DEFAULT section. Comments are indicated by ";" or "#"; comments may begin anywhere on a single line.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- It simplified operation processes, easy to use and undersatnd; therefore, there are less chances to have errors.
|
||||||
|
- It uses exactly the same way to access a configuration file as you use Windows APIs, so you don't need to change your code style.
|
||||||
|
- It supports read recursion sections.
|
||||||
|
- It supports auto increment of key.
|
||||||
|
- It supports **READ** and **WRITE** configuration file with comments each section or key which all the other parsers don't support!!!!!!!
|
||||||
|
- It supports get value through type bool, float64, int, int64 and string, methods that start with "Must" means ignore errors and get zero-value if error occurs, or you can specify a default value.
|
||||||
|
- It's able to load multiple files to overwrite key values.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get github.com/Unknwon/goconfig
|
||||||
|
|
||||||
|
Or
|
||||||
|
|
||||||
|
gopm get github.com/Unknwon/goconfig
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
[Go Walker](http://gowalker.org/github.com/Unknwon/goconfig).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Please see [conf.ini](testdata/conf.ini) as an example.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
- Function `LoadConfigFile` load file(s) depends on your situation, and return a variable with type `ConfigFile`.
|
||||||
|
- `GetValue` gives basic functionality of getting a value of given section and key.
|
||||||
|
- Methods like `Bool`, `Int`, `Int64` return corresponding type of values.
|
||||||
|
- Methods start with `Must` return corresponding type of values and returns zero-value of given type if something goes wrong.
|
||||||
|
- `SetValue` sets value to given section and key, and inserts somewhere if it does not exist.
|
||||||
|
- `DeleteKey` deletes by given section and key.
|
||||||
|
- Finally, `SaveConfigFile` saves your configuration to local file system.
|
||||||
|
- Use method `Reload` in case someone else modified your file(s).
|
||||||
|
- Methods contains `Comment` help you manipulate comments.
|
||||||
|
- `LoadFromReader` allows loading data without an intermediate file.
|
||||||
|
- `SaveConfigData` added, which writes configuration to an arbitrary writer.
|
||||||
|
- `ReloadData` allows to reload data from memory.
|
||||||
|
|
||||||
|
Note that you cannot mix in-memory configuration with on-disk configuration.
|
||||||
|
|
||||||
|
## More Information
|
||||||
|
|
||||||
|
- All characters are CASE SENSITIVE, BE CAREFUL!
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- [goconf](http://code.google.com/p/goconf/)
|
||||||
|
- [robfig/config](https://github.com/robfig/config)
|
||||||
|
- [Delete an item from a slice](https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/lYz8ftASMQ0)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.
|
||||||
64
vendor/github.com/Unknwon/goconfig/README_ZH.md
generated
vendored
Executable file
64
vendor/github.com/Unknwon/goconfig/README_ZH.md
generated
vendored
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
goconfig [](https://drone.io/github.com/Unknwon/goconfig/latest) [](http://gowalker.org/github.com/Unknwon/goconfig)
|
||||||
|
========
|
||||||
|
|
||||||
|
本库已被 [《Go名库讲解》](https://github.com/Unknwon/go-rock-libraries-showcases/tree/master/lectures/01-goconfig) 收录讲解,欢迎前往学习如何使用!
|
||||||
|
|
||||||
|
编码规范:基于 [Go 编码规范](https://github.com/Unknwon/go-code-convention)
|
||||||
|
|
||||||
|
## 关于
|
||||||
|
|
||||||
|
包 goconfig 是一个易于使用,支持注释的 Go 语言配置文件解析器,该文件的书写格式和 Windows 下的 INI 文件一样。
|
||||||
|
|
||||||
|
配置文件由形为 `[section]` 的节构成,内部使用 `name:value` 或 `name=value` 这样的键值对;每行开头和尾部的空白符号都将被忽略;如果未指定任何节,则会默认放入名为 `DEFAULT` 的节当中;可以使用 “;” 或 “#” 来作为注释的开头,并可以放置于任意的单独一行中。
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
- 简化流程,易于理解,更少出错。
|
||||||
|
- 提供与 Windows API 一模一样的操作方式。
|
||||||
|
- 支持读取递归节。
|
||||||
|
- 支持自增键名。
|
||||||
|
- 支持对注释的 **读** 和 **写** 操作,其它所有解析器都不支持!!!!
|
||||||
|
- 可以直接返回 bool, float64, int, int64 和 string 类型的值,如果使用 “Must” 开头的方法,则一定会返回这个类型的一个值而不返回错误,如果错误发生则会返回零值。
|
||||||
|
- 支持加载多个文件来重写值。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
go get github.com/Unknwon/goconfig
|
||||||
|
|
||||||
|
或
|
||||||
|
|
||||||
|
gopm get github.com/Unknwon/goconfig
|
||||||
|
|
||||||
|
|
||||||
|
## API 文档
|
||||||
|
|
||||||
|
[Go Walker](http://gowalker.org/github.com/Unknwon/goconfig).
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
请查看 [conf.ini](testdata/conf.ini) 文件作为使用示例。
|
||||||
|
|
||||||
|
### 用例
|
||||||
|
|
||||||
|
- 函数 `LoadConfigFile` 加载一个或多个文件,然后返回一个类型为 `ConfigFile` 的变量。
|
||||||
|
- `GetValue` 可以简单的获取某个值。
|
||||||
|
- 像 `Bool`、`Int`、`Int64` 这样的方法会直接返回指定类型的值。
|
||||||
|
- 以 `Must` 开头的方法不会返回错误,但当错误发生时会返回零值。
|
||||||
|
- `SetValue` 可以设置某个值。
|
||||||
|
- `DeleteKey` 可以删除某个键。
|
||||||
|
- 最后,`SaveConfigFile` 可以保持您的配置到本地文件系统。
|
||||||
|
- 使用方法 `Reload` 可以重载您的配置文件。
|
||||||
|
|
||||||
|
## 更多信息
|
||||||
|
|
||||||
|
- 所有字符都是大小写敏感的!
|
||||||
|
|
||||||
|
## 参考信息
|
||||||
|
|
||||||
|
- [goconf](http://code.google.com/p/goconf/)
|
||||||
|
- [robfig/config](https://github.com/robfig/config)
|
||||||
|
- [Delete an item from a slice](https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/lYz8ftASMQ0)
|
||||||
|
|
||||||
|
## 授权许可
|
||||||
|
|
||||||
|
本项目采用 Apache v2 开源授权许可证,完整的授权说明已放置在 [LICENSE](LICENSE) 文件中。
|
||||||
555
vendor/github.com/Unknwon/goconfig/conf.go
generated
vendored
Executable file
555
vendor/github.com/Unknwon/goconfig/conf.go
generated
vendored
Executable file
@@ -0,0 +1,555 @@
|
|||||||
|
// Copyright 2013 Unknwon
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
// Package goconfig is a fully functional and comments-support configuration file(.ini) parser.
|
||||||
|
package goconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default section name.
|
||||||
|
DEFAULT_SECTION = "DEFAULT"
|
||||||
|
// Maximum allowed depth when recursively substituing variable names.
|
||||||
|
_DEPTH_VALUES = 200
|
||||||
|
)
|
||||||
|
|
||||||
|
type ParseError int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ERR_SECTION_NOT_FOUND ParseError = iota + 1
|
||||||
|
ERR_KEY_NOT_FOUND
|
||||||
|
ERR_BLANK_SECTION_NAME
|
||||||
|
ERR_COULD_NOT_PARSE
|
||||||
|
)
|
||||||
|
|
||||||
|
var LineBreak = "\n"
|
||||||
|
|
||||||
|
// Variable regexp pattern: %(variable)s
|
||||||
|
var varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
LineBreak = "\r\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ConfigFile represents a INI formar configuration file.
|
||||||
|
type ConfigFile struct {
|
||||||
|
lock sync.RWMutex // Go map is not safe.
|
||||||
|
fileNames []string // Support mutil-files.
|
||||||
|
data map[string]map[string]string // Section -> key : value
|
||||||
|
|
||||||
|
// Lists can keep sections and keys in order.
|
||||||
|
sectionList []string // Section name list.
|
||||||
|
keyList map[string][]string // Section -> Key name list
|
||||||
|
|
||||||
|
sectionComments map[string]string // Sections comments.
|
||||||
|
keyComments map[string]map[string]string // Keys comments.
|
||||||
|
BlockMode bool // Indicates whether use lock or not.
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConfigFile creates an empty configuration representation.
|
||||||
|
func newConfigFile(fileNames []string) *ConfigFile {
|
||||||
|
c := new(ConfigFile)
|
||||||
|
c.fileNames = fileNames
|
||||||
|
c.data = make(map[string]map[string]string)
|
||||||
|
c.keyList = make(map[string][]string)
|
||||||
|
c.sectionComments = make(map[string]string)
|
||||||
|
c.keyComments = make(map[string]map[string]string)
|
||||||
|
c.BlockMode = true
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValue adds a new section-key-value to the configuration.
|
||||||
|
// It returns true if the key and value were inserted,
|
||||||
|
// or returns false if the value was overwritten.
|
||||||
|
// If the section does not exist in advance, it will be created.
|
||||||
|
func (c *ConfigFile) SetValue(section, key, value string) bool {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
if len(key) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BlockMode {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if section exists.
|
||||||
|
if _, ok := c.data[section]; !ok {
|
||||||
|
// Execute add operation.
|
||||||
|
c.data[section] = make(map[string]string)
|
||||||
|
// Append section to list.
|
||||||
|
c.sectionList = append(c.sectionList, section)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if key exists.
|
||||||
|
_, ok := c.data[section][key]
|
||||||
|
c.data[section][key] = value
|
||||||
|
if !ok {
|
||||||
|
// If not exists, append to key list.
|
||||||
|
c.keyList[section] = append(c.keyList[section], key)
|
||||||
|
}
|
||||||
|
return !ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteKey deletes the key in given section.
|
||||||
|
// It returns true if the key was deleted,
|
||||||
|
// or returns false if the section or key didn't exist.
|
||||||
|
func (c *ConfigFile) DeleteKey(section, key string) bool {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BlockMode {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if section exists.
|
||||||
|
if _, ok := c.data[section]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if key exists.
|
||||||
|
if _, ok := c.data[section][key]; ok {
|
||||||
|
delete(c.data[section], key)
|
||||||
|
// Remove comments of key.
|
||||||
|
c.SetKeyComments(section, key, "")
|
||||||
|
// Get index of key.
|
||||||
|
i := 0
|
||||||
|
for _, keyName := range c.keyList[section] {
|
||||||
|
if keyName == key {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Remove from key list.
|
||||||
|
c.keyList[section] = append(c.keyList[section][:i], c.keyList[section][i+1:]...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue returns the value of key available in the given section.
|
||||||
|
// If the value needs to be unfolded
|
||||||
|
// (see e.g. %(google)s example in the GoConfig_test.go),
|
||||||
|
// then String does this unfolding automatically, up to
|
||||||
|
// _DEPTH_VALUES number of iterations.
|
||||||
|
// It returns an error and empty string value if the section does not exist,
|
||||||
|
// or key does not exist in DEFAULT and current sections.
|
||||||
|
func (c *ConfigFile) GetValue(section, key string) (string, error) {
|
||||||
|
if c.BlockMode {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if section exists
|
||||||
|
if _, ok := c.data[section]; !ok {
|
||||||
|
// Section does not exist.
|
||||||
|
return "", getError{ERR_SECTION_NOT_FOUND, section}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section exists.
|
||||||
|
// Check if key exists or empty value.
|
||||||
|
value, ok := c.data[section][key]
|
||||||
|
if !ok {
|
||||||
|
// Check if it is a sub-section.
|
||||||
|
if i := strings.LastIndex(section, "."); i > -1 {
|
||||||
|
return c.GetValue(section[:i], key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return empty value.
|
||||||
|
return "", getError{ERR_KEY_NOT_FOUND, key}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key exists.
|
||||||
|
var i int
|
||||||
|
for i = 0; i < _DEPTH_VALUES; i++ {
|
||||||
|
vr := varPattern.FindString(value)
|
||||||
|
if len(vr) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take off leading '%(' and trailing ')s'.
|
||||||
|
noption := strings.TrimLeft(vr, "%(")
|
||||||
|
noption = strings.TrimRight(noption, ")s")
|
||||||
|
|
||||||
|
// Search variable in default section.
|
||||||
|
nvalue, err := c.GetValue(DEFAULT_SECTION, noption)
|
||||||
|
if err != nil && section != DEFAULT_SECTION {
|
||||||
|
// Search in the same section.
|
||||||
|
if _, ok := c.data[section][noption]; ok {
|
||||||
|
nvalue = c.data[section][noption]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substitute by new value and take off leading '%(' and trailing ')s'.
|
||||||
|
value = strings.Replace(value, vr, nvalue, -1)
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool returns bool type value.
|
||||||
|
func (c *ConfigFile) Bool(section, key string) (bool, error) {
|
||||||
|
value, err := c.GetValue(section, key)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return strconv.ParseBool(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 returns float64 type value.
|
||||||
|
func (c *ConfigFile) Float64(section, key string) (float64, error) {
|
||||||
|
value, err := c.GetValue(section, key)
|
||||||
|
if err != nil {
|
||||||
|
return 0.0, err
|
||||||
|
}
|
||||||
|
return strconv.ParseFloat(value, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns int type value.
|
||||||
|
func (c *ConfigFile) Int(section, key string) (int, error) {
|
||||||
|
value, err := c.GetValue(section, key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.Atoi(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 returns int64 type value.
|
||||||
|
func (c *ConfigFile) Int64(section, key string) (int64, error) {
|
||||||
|
value, err := c.GetValue(section, key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.ParseInt(value, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustValue always returns value without error.
|
||||||
|
// It returns empty string if error occurs, or the default value if given.
|
||||||
|
func (c *ConfigFile) MustValue(section, key string, defaultVal ...string) string {
|
||||||
|
val, err := c.GetValue(section, key)
|
||||||
|
if len(defaultVal) > 0 && (err != nil || len(val) == 0) {
|
||||||
|
return defaultVal[0]
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustValue always returns value without error,
|
||||||
|
// It returns empty string if error occurs, or the default value if given,
|
||||||
|
// and a bool value indicates whether default value is returned.
|
||||||
|
func (c *ConfigFile) MustValueSet(section, key string, defaultVal ...string) (string, bool) {
|
||||||
|
val, err := c.GetValue(section, key)
|
||||||
|
if len(defaultVal) > 0 && (err != nil || len(val) == 0) {
|
||||||
|
c.SetValue(section, key, defaultVal[0])
|
||||||
|
return defaultVal[0], true
|
||||||
|
}
|
||||||
|
return val, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustValueRange always returns value without error,
|
||||||
|
// it returns default value if error occurs or doesn't fit into range.
|
||||||
|
func (c *ConfigFile) MustValueRange(section, key, defaultVal string, candidates []string) string {
|
||||||
|
val, err := c.GetValue(section, key)
|
||||||
|
if err != nil || len(val) == 0 {
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cand := range candidates {
|
||||||
|
if val == cand {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustValueArray always returns value array without error,
|
||||||
|
// it returns empty array if error occurs, split by delimiter otherwise.
|
||||||
|
func (c *ConfigFile) MustValueArray(section, key, delim string) []string {
|
||||||
|
val, err := c.GetValue(section, key)
|
||||||
|
if err != nil || len(val) == 0 {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := strings.Split(val, delim)
|
||||||
|
for i := range vals {
|
||||||
|
vals[i] = strings.TrimSpace(vals[i])
|
||||||
|
}
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustBool always returns value without error,
|
||||||
|
// it returns false if error occurs.
|
||||||
|
func (c *ConfigFile) MustBool(section, key string, defaultVal ...bool) bool {
|
||||||
|
val, err := c.Bool(section, key)
|
||||||
|
if len(defaultVal) > 0 && err != nil {
|
||||||
|
return defaultVal[0]
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustFloat64 always returns value without error,
|
||||||
|
// it returns 0.0 if error occurs.
|
||||||
|
func (c *ConfigFile) MustFloat64(section, key string, defaultVal ...float64) float64 {
|
||||||
|
value, err := c.Float64(section, key)
|
||||||
|
if len(defaultVal) > 0 && err != nil {
|
||||||
|
return defaultVal[0]
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt always returns value without error,
|
||||||
|
// it returns 0 if error occurs.
|
||||||
|
func (c *ConfigFile) MustInt(section, key string, defaultVal ...int) int {
|
||||||
|
value, err := c.Int(section, key)
|
||||||
|
if len(defaultVal) > 0 && err != nil {
|
||||||
|
return defaultVal[0]
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt64 always returns value without error,
|
||||||
|
// it returns 0 if error occurs.
|
||||||
|
func (c *ConfigFile) MustInt64(section, key string, defaultVal ...int64) int64 {
|
||||||
|
value, err := c.Int64(section, key)
|
||||||
|
if len(defaultVal) > 0 && err != nil {
|
||||||
|
return defaultVal[0]
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSectionList returns the list of all sections
|
||||||
|
// in the same order in the file.
|
||||||
|
func (c *ConfigFile) GetSectionList() []string {
|
||||||
|
list := make([]string, len(c.sectionList))
|
||||||
|
copy(list, c.sectionList)
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKeyList returns the list of all keys in give section
|
||||||
|
// in the same order in the file.
|
||||||
|
// It returns nil if given section does not exist.
|
||||||
|
func (c *ConfigFile) GetKeyList(section string) []string {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BlockMode {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if section exists.
|
||||||
|
if _, ok := c.data[section]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-default section has a blank key as section keeper.
|
||||||
|
list := make([]string, 0, len(c.keyList[section]))
|
||||||
|
for _, key := range c.keyList[section] {
|
||||||
|
if key != " " {
|
||||||
|
list = append(list, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSection deletes the entire section by given name.
|
||||||
|
// It returns true if the section was deleted, and false if the section didn't exist.
|
||||||
|
func (c *ConfigFile) DeleteSection(section string) bool {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BlockMode {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if section exists.
|
||||||
|
if _, ok := c.data[section]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(c.data, section)
|
||||||
|
// Remove comments of section.
|
||||||
|
c.SetSectionComments(section, "")
|
||||||
|
// Get index of section.
|
||||||
|
i := 0
|
||||||
|
for _, secName := range c.sectionList {
|
||||||
|
if secName == section {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Remove from section and key list.
|
||||||
|
c.sectionList = append(c.sectionList[:i], c.sectionList[i+1:]...)
|
||||||
|
delete(c.keyList, section)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSection returns key-value pairs in given section.
|
||||||
|
// If section does not exist, returns nil and error.
|
||||||
|
func (c *ConfigFile) GetSection(section string) (map[string]string, error) {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BlockMode {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if section exists.
|
||||||
|
if _, ok := c.data[section]; !ok {
|
||||||
|
// Section does not exist.
|
||||||
|
return nil, getError{ERR_SECTION_NOT_FOUND, section}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove pre-defined key.
|
||||||
|
secMap := c.data[section]
|
||||||
|
delete(c.data[section], " ")
|
||||||
|
|
||||||
|
// Section exists.
|
||||||
|
return secMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSectionComments adds new section comments to the configuration.
|
||||||
|
// If comments are empty(0 length), it will remove its section comments!
|
||||||
|
// It returns true if the comments were inserted or removed,
|
||||||
|
// or returns false if the comments were overwritten.
|
||||||
|
func (c *ConfigFile) SetSectionComments(section, comments string) bool {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(comments) == 0 {
|
||||||
|
if _, ok := c.sectionComments[section]; ok {
|
||||||
|
delete(c.sectionComments, section)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not exists can be seen as remove.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if comments exists.
|
||||||
|
_, ok := c.sectionComments[section]
|
||||||
|
if comments[0] != '#' && comments[0] != ';' {
|
||||||
|
comments = "; " + comments
|
||||||
|
}
|
||||||
|
c.sectionComments[section] = comments
|
||||||
|
return !ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeyComments adds new section-key comments to the configuration.
|
||||||
|
// If comments are empty(0 length), it will remove its section-key comments!
|
||||||
|
// It returns true if the comments were inserted or removed,
|
||||||
|
// or returns false if the comments were overwritten.
|
||||||
|
// If the section does not exist in advance, it is created.
|
||||||
|
func (c *ConfigFile) SetKeyComments(section, key, comments string) bool {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if section exists.
|
||||||
|
if _, ok := c.keyComments[section]; ok {
|
||||||
|
if len(comments) == 0 {
|
||||||
|
if _, ok := c.keyComments[section][key]; ok {
|
||||||
|
delete(c.keyComments[section], key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not exists can be seen as remove.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(comments) == 0 {
|
||||||
|
// Not exists can be seen as remove.
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
// Execute add operation.
|
||||||
|
c.keyComments[section] = make(map[string]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if key exists.
|
||||||
|
_, ok := c.keyComments[section][key]
|
||||||
|
if comments[0] != '#' && comments[0] != ';' {
|
||||||
|
comments = "; " + comments
|
||||||
|
}
|
||||||
|
c.keyComments[section][key] = comments
|
||||||
|
return !ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSectionComments returns the comments in the given section.
|
||||||
|
// It returns an empty string(0 length) if the comments do not exist.
|
||||||
|
func (c *ConfigFile) GetSectionComments(section string) (comments string) {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
return c.sectionComments[section]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKeyComments returns the comments of key in the given section.
|
||||||
|
// It returns an empty string(0 length) if the comments do not exist.
|
||||||
|
func (c *ConfigFile) GetKeyComments(section, key string) (comments string) {
|
||||||
|
// Blank section name represents DEFAULT section.
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = DEFAULT_SECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := c.keyComments[section]; ok {
|
||||||
|
return c.keyComments[section][key]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// getError occurs when get value in configuration file with invalid parameter.
|
||||||
|
type getError struct {
|
||||||
|
Reason ParseError
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements Error interface.
|
||||||
|
func (err getError) Error() string {
|
||||||
|
switch err.Reason {
|
||||||
|
case ERR_SECTION_NOT_FOUND:
|
||||||
|
return fmt.Sprintf("section '%s' not found", err.Name)
|
||||||
|
case ERR_KEY_NOT_FOUND:
|
||||||
|
return fmt.Sprintf("key '%s' not found", err.Name)
|
||||||
|
}
|
||||||
|
return "invalid get error"
|
||||||
|
}
|
||||||
294
vendor/github.com/Unknwon/goconfig/read.go
generated
vendored
Executable file
294
vendor/github.com/Unknwon/goconfig/read.go
generated
vendored
Executable file
@@ -0,0 +1,294 @@
|
|||||||
|
// Copyright 2013 Unknwon
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package goconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read reads an io.Reader and returns a configuration representation.
|
||||||
|
// This representation can be queried with GetValue.
|
||||||
|
func (c *ConfigFile) read(reader io.Reader) (err error) {
|
||||||
|
buf := bufio.NewReader(reader)
|
||||||
|
|
||||||
|
// Handle BOM-UTF8.
|
||||||
|
// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
|
||||||
|
mask, err := buf.Peek(3)
|
||||||
|
if err == nil && len(mask) >= 3 &&
|
||||||
|
mask[0] == 239 && mask[1] == 187 && mask[2] == 191 {
|
||||||
|
buf.Read(mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 1 // Counter for auto increment.
|
||||||
|
// Current section name.
|
||||||
|
section := DEFAULT_SECTION
|
||||||
|
var comments string
|
||||||
|
// Parse line-by-line
|
||||||
|
for {
|
||||||
|
line, err := buf.ReadString('\n')
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
lineLengh := len(line) //[SWH|+]
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached end of file, if nothing to read then break,
|
||||||
|
// otherwise handle the last line.
|
||||||
|
if lineLengh == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch written for readability (not performance)
|
||||||
|
switch {
|
||||||
|
case lineLengh == 0: // Empty line
|
||||||
|
continue
|
||||||
|
case line[0] == '#' || line[0] == ';': // Comment
|
||||||
|
// Append comments
|
||||||
|
if len(comments) == 0 {
|
||||||
|
comments = line
|
||||||
|
} else {
|
||||||
|
comments += LineBreak + line
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case line[0] == '[' && line[lineLengh-1] == ']': // New sction.
|
||||||
|
// Get section name.
|
||||||
|
section = strings.TrimSpace(line[1 : lineLengh-1])
|
||||||
|
// Set section comments and empty if it has comments.
|
||||||
|
if len(comments) > 0 {
|
||||||
|
c.SetSectionComments(section, comments)
|
||||||
|
comments = ""
|
||||||
|
}
|
||||||
|
// Make section exist even though it does not have any key.
|
||||||
|
c.SetValue(section, " ", " ")
|
||||||
|
// Reset counter.
|
||||||
|
count = 1
|
||||||
|
continue
|
||||||
|
case section == "": // No section defined so far
|
||||||
|
return readError{ERR_BLANK_SECTION_NAME, line}
|
||||||
|
default: // Other alternatives
|
||||||
|
var (
|
||||||
|
i int
|
||||||
|
keyQuote string
|
||||||
|
key string
|
||||||
|
valQuote string
|
||||||
|
value string
|
||||||
|
)
|
||||||
|
//[SWH|+]:支持引号包围起来的字串
|
||||||
|
if line[0] == '"' {
|
||||||
|
if lineLengh >= 6 && line[0:3] == `"""` {
|
||||||
|
keyQuote = `"""`
|
||||||
|
} else {
|
||||||
|
keyQuote = `"`
|
||||||
|
}
|
||||||
|
} else if line[0] == '`' {
|
||||||
|
keyQuote = "`"
|
||||||
|
}
|
||||||
|
if keyQuote != "" {
|
||||||
|
qLen := len(keyQuote)
|
||||||
|
pos := strings.Index(line[qLen:], keyQuote)
|
||||||
|
if pos == -1 {
|
||||||
|
return readError{ERR_COULD_NOT_PARSE, line}
|
||||||
|
}
|
||||||
|
pos = pos + qLen
|
||||||
|
i = strings.IndexAny(line[pos:], "=:")
|
||||||
|
if i <= 0 {
|
||||||
|
return readError{ERR_COULD_NOT_PARSE, line}
|
||||||
|
}
|
||||||
|
i = i + pos
|
||||||
|
key = line[qLen:pos] //保留引号内的两端的空格
|
||||||
|
} else {
|
||||||
|
i = strings.IndexAny(line, "=:")
|
||||||
|
if i <= 0 {
|
||||||
|
return readError{ERR_COULD_NOT_PARSE, line}
|
||||||
|
}
|
||||||
|
key = strings.TrimSpace(line[0:i])
|
||||||
|
}
|
||||||
|
//[SWH|+];
|
||||||
|
|
||||||
|
// Check if it needs auto increment.
|
||||||
|
if key == "-" {
|
||||||
|
key = "#" + fmt.Sprint(count)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
//[SWH|+]:支持引号包围起来的字串
|
||||||
|
lineRight := strings.TrimSpace(line[i+1:])
|
||||||
|
lineRightLength := len(lineRight)
|
||||||
|
firstChar := ""
|
||||||
|
if lineRightLength >= 2 {
|
||||||
|
firstChar = lineRight[0:1]
|
||||||
|
}
|
||||||
|
if firstChar == "`" {
|
||||||
|
valQuote = "`"
|
||||||
|
} else if lineRightLength >= 6 && lineRight[0:3] == `"""` {
|
||||||
|
valQuote = `"""`
|
||||||
|
}
|
||||||
|
if valQuote != "" {
|
||||||
|
qLen := len(valQuote)
|
||||||
|
pos := strings.LastIndex(lineRight[qLen:], valQuote)
|
||||||
|
if pos == -1 {
|
||||||
|
return readError{ERR_COULD_NOT_PARSE, line}
|
||||||
|
}
|
||||||
|
pos = pos + qLen
|
||||||
|
value = lineRight[qLen:pos]
|
||||||
|
} else {
|
||||||
|
value = strings.TrimSpace(lineRight[0:])
|
||||||
|
}
|
||||||
|
//[SWH|+];
|
||||||
|
|
||||||
|
c.SetValue(section, key, value)
|
||||||
|
// Set key comments and empty if it has comments.
|
||||||
|
if len(comments) > 0 {
|
||||||
|
c.SetKeyComments(section, key, comments)
|
||||||
|
comments = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached end of file.
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFromData accepts raw data directly from memory
|
||||||
|
// and returns a new configuration representation.
|
||||||
|
// Note that the configuration is written to the system
|
||||||
|
// temporary folder, so your file should not contain
|
||||||
|
// sensitive information.
|
||||||
|
func LoadFromData(data []byte) (c *ConfigFile, err error) {
|
||||||
|
// Save memory data to temporary file to support further operations.
|
||||||
|
tmpName := path.Join(os.TempDir(), "goconfig", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||||
|
if err = os.MkdirAll(path.Dir(tmpName), os.ModePerm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c = newConfigFile([]string{tmpName})
|
||||||
|
err = c.read(bytes.NewBuffer(data))
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFromReader accepts raw data directly from a reader
|
||||||
|
// and returns a new configuration representation.
|
||||||
|
// You must use ReloadData to reload.
|
||||||
|
// You cannot append files a configfile read this way.
|
||||||
|
func LoadFromReader(in io.Reader) (c *ConfigFile, err error) {
|
||||||
|
c = newConfigFile([]string{""})
|
||||||
|
err = c.read(in)
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConfigFile) loadFile(fileName string) (err error) {
|
||||||
|
f, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return c.read(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfigFile reads a file and returns a new configuration representation.
|
||||||
|
// This representation can be queried with GetValue.
|
||||||
|
func LoadConfigFile(fileName string, moreFiles ...string) (c *ConfigFile, err error) {
|
||||||
|
// Append files' name together.
|
||||||
|
fileNames := make([]string, 1, len(moreFiles)+1)
|
||||||
|
fileNames[0] = fileName
|
||||||
|
if len(moreFiles) > 0 {
|
||||||
|
fileNames = append(fileNames, moreFiles...)
|
||||||
|
}
|
||||||
|
|
||||||
|
c = newConfigFile(fileNames)
|
||||||
|
|
||||||
|
for _, name := range fileNames {
|
||||||
|
if err = c.loadFile(name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload reloads configuration file in case it has changes.
|
||||||
|
func (c *ConfigFile) Reload() (err error) {
|
||||||
|
var cfg *ConfigFile
|
||||||
|
if len(c.fileNames) == 1 {
|
||||||
|
if c.fileNames[0] == "" {
|
||||||
|
return fmt.Errorf("file opened from in-memory data, use ReloadData to reload")
|
||||||
|
}
|
||||||
|
cfg, err = LoadConfigFile(c.fileNames[0])
|
||||||
|
} else {
|
||||||
|
cfg, err = LoadConfigFile(c.fileNames[0], c.fileNames[1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
*c = *cfg
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadData reloads configuration file from memory
|
||||||
|
func (c *ConfigFile) ReloadData(in io.Reader) (err error) {
|
||||||
|
var cfg *ConfigFile
|
||||||
|
if len(c.fileNames) != 1 {
|
||||||
|
return fmt.Errorf("Multiple files loaded, unable to mix in-memory and file data")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err = LoadFromReader(in)
|
||||||
|
if err == nil {
|
||||||
|
*c = *cfg
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendFiles appends more files to ConfigFile and reload automatically.
|
||||||
|
func (c *ConfigFile) AppendFiles(files ...string) error {
|
||||||
|
if len(c.fileNames) == 1 && c.fileNames[0] == "" {
|
||||||
|
return fmt.Errorf("Cannot append file data to in-memory data")
|
||||||
|
}
|
||||||
|
c.fileNames = append(c.fileNames, files...)
|
||||||
|
return c.Reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
// readError occurs when read configuration file with wrong format.
|
||||||
|
type readError struct {
|
||||||
|
Reason ParseError
|
||||||
|
Content string // Line content
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implement Error interface.
|
||||||
|
func (err readError) Error() string {
|
||||||
|
switch err.Reason {
|
||||||
|
case ERR_BLANK_SECTION_NAME:
|
||||||
|
return "empty section name not allowed"
|
||||||
|
case ERR_COULD_NOT_PARSE:
|
||||||
|
return fmt.Sprintf("could not parse line: %s", string(err.Content))
|
||||||
|
}
|
||||||
|
return "invalid read error"
|
||||||
|
}
|
||||||
117
vendor/github.com/Unknwon/goconfig/write.go
generated
vendored
Executable file
117
vendor/github.com/Unknwon/goconfig/write.go
generated
vendored
Executable file
@@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2013 Unknwon
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package goconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Write spaces around "=" to look better.
|
||||||
|
var PrettyFormat = true
|
||||||
|
|
||||||
|
// SaveConfigData writes configuration to a writer
|
||||||
|
func SaveConfigData(c *ConfigFile, out io.Writer) (err error) {
|
||||||
|
equalSign := "="
|
||||||
|
if PrettyFormat {
|
||||||
|
equalSign = " = "
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
for _, section := range c.sectionList {
|
||||||
|
// Write section comments.
|
||||||
|
if len(c.GetSectionComments(section)) > 0 {
|
||||||
|
if _, err = buf.WriteString(c.GetSectionComments(section) + LineBreak); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if section != DEFAULT_SECTION {
|
||||||
|
// Write section name.
|
||||||
|
if _, err = buf.WriteString("[" + section + "]" + LineBreak); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range c.keyList[section] {
|
||||||
|
if key != " " {
|
||||||
|
// Write key comments.
|
||||||
|
if len(c.GetKeyComments(section, key)) > 0 {
|
||||||
|
if _, err = buf.WriteString(c.GetKeyComments(section, key) + LineBreak); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyName := key
|
||||||
|
// Check if it's auto increment.
|
||||||
|
if keyName[0] == '#' {
|
||||||
|
keyName = "-"
|
||||||
|
}
|
||||||
|
//[SWH|+]:支持键名包含等号和冒号
|
||||||
|
if strings.Contains(keyName, `=`) || strings.Contains(keyName, `:`) {
|
||||||
|
if strings.Contains(keyName, "`") {
|
||||||
|
if strings.Contains(keyName, `"`) {
|
||||||
|
keyName = `"""` + keyName + `"""`
|
||||||
|
} else {
|
||||||
|
keyName = `"` + keyName + `"`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keyName = "`" + keyName + "`"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value := c.data[section][key]
|
||||||
|
// In case key value contains "`" or "\"".
|
||||||
|
if strings.Contains(value, "`") {
|
||||||
|
if strings.Contains(value, `"`) {
|
||||||
|
value = `"""` + value + `"""`
|
||||||
|
} else {
|
||||||
|
value = `"` + value + `"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write key and value.
|
||||||
|
if _, err = buf.WriteString(keyName + equalSign + value + LineBreak); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put a line between sections.
|
||||||
|
if _, err = buf.WriteString(LineBreak); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := buf.WriteTo(out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveConfigFile writes configuration file to local file system
|
||||||
|
func SaveConfigFile(c *ConfigFile, filename string) (err error) {
|
||||||
|
// Write configuration file by filename.
|
||||||
|
var f *os.File
|
||||||
|
if f, err = os.Create(filename); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := SaveConfigData(c, f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.Close()
|
||||||
|
}
|
||||||
178
vendor/github.com/abbot/go-http-auth/LICENSE
generated
vendored
Executable file
178
vendor/github.com/abbot/go-http-auth/LICENSE
generated
vendored
Executable file
@@ -0,0 +1,178 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
12
vendor/github.com/abbot/go-http-auth/Makefile
generated
vendored
Executable file
12
vendor/github.com/abbot/go-http-auth/Makefile
generated
vendored
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
include $(GOROOT)/src/Make.inc
|
||||||
|
|
||||||
|
TARG=auth_digest
|
||||||
|
GOFILES=\
|
||||||
|
auth.go\
|
||||||
|
digest.go\
|
||||||
|
basic.go\
|
||||||
|
misc.go\
|
||||||
|
md5crypt.go\
|
||||||
|
users.go\
|
||||||
|
|
||||||
|
include $(GOROOT)/src/Make.pkg
|
||||||
71
vendor/github.com/abbot/go-http-auth/README.md
generated
vendored
Executable file
71
vendor/github.com/abbot/go-http-auth/README.md
generated
vendored
Executable file
@@ -0,0 +1,71 @@
|
|||||||
|
HTTP Authentication implementation in Go
|
||||||
|
========================================
|
||||||
|
|
||||||
|
This is an implementation of HTTP Basic and HTTP Digest authentication
|
||||||
|
in Go language. It is designed as a simple wrapper for
|
||||||
|
http.RequestHandler functions.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Supports HTTP Basic and HTTP Digest authentication.
|
||||||
|
* Supports htpasswd and htdigest formatted files.
|
||||||
|
* Automatic reloading of password files.
|
||||||
|
* Pluggable interface for user/password storage.
|
||||||
|
* Supports MD5, SHA1 and BCrypt for Basic authentication password storage.
|
||||||
|
* Configurable Digest nonce cache size with expiration.
|
||||||
|
* Wrapper for legacy http handlers (http.HandlerFunc interface)
|
||||||
|
|
||||||
|
Example usage
|
||||||
|
-------------
|
||||||
|
|
||||||
|
This is a complete working example for Basic auth:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
auth "github.com/abbot/go-http-auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Secret(user, realm string) string {
|
||||||
|
if user == "john" {
|
||||||
|
// password is "hello"
|
||||||
|
return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
|
||||||
|
fmt.Fprintf(w, "<html><body><h1>Hello, %s!</h1></body></html>", r.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
authenticator := auth.NewBasicAuthenticator("example.com", Secret)
|
||||||
|
http.HandleFunc("/", authenticator.Wrap(handle))
|
||||||
|
http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
See more examples in the "examples" directory.
|
||||||
|
|
||||||
|
Legal
|
||||||
|
-----
|
||||||
|
|
||||||
|
This module is developed under Apache 2.0 license, and can be used for
|
||||||
|
open and proprietary projects.
|
||||||
|
|
||||||
|
Copyright 2012-2013 Lev Shamardin
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you
|
||||||
|
may not use this file or any other part of this project 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.
|
||||||
109
vendor/github.com/abbot/go-http-auth/auth.go
generated
vendored
Executable file
109
vendor/github.com/abbot/go-http-auth/auth.go
generated
vendored
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
// Package auth is an implementation of HTTP Basic and HTTP Digest authentication.
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Request handlers must take AuthenticatedRequest instead of http.Request
|
||||||
|
*/
|
||||||
|
type AuthenticatedRequest struct {
|
||||||
|
http.Request
|
||||||
|
/*
|
||||||
|
Authenticated user name. Current API implies that Username is
|
||||||
|
never empty, which means that authentication is always done
|
||||||
|
before calling the request handler.
|
||||||
|
*/
|
||||||
|
Username string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AuthenticatedHandlerFunc is like http.HandlerFunc, but takes
|
||||||
|
AuthenticatedRequest instead of http.Request
|
||||||
|
*/
|
||||||
|
type AuthenticatedHandlerFunc func(http.ResponseWriter, *AuthenticatedRequest)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Authenticator wraps an AuthenticatedHandlerFunc with
|
||||||
|
authentication-checking code.
|
||||||
|
|
||||||
|
Typical Authenticator usage is something like:
|
||||||
|
|
||||||
|
authenticator := SomeAuthenticator(...)
|
||||||
|
http.HandleFunc("/", authenticator(my_handler))
|
||||||
|
|
||||||
|
Authenticator wrapper checks the user authentication and calls the
|
||||||
|
wrapped function only after authentication has succeeded. Otherwise,
|
||||||
|
it returns a handler which initiates the authentication procedure.
|
||||||
|
*/
|
||||||
|
type Authenticator func(AuthenticatedHandlerFunc) http.HandlerFunc
|
||||||
|
|
||||||
|
// Info contains authentication information for the request.
|
||||||
|
type Info struct {
|
||||||
|
// Authenticated is set to true when request was authenticated
|
||||||
|
// successfully, i.e. username and password passed in request did
|
||||||
|
// pass the check.
|
||||||
|
Authenticated bool
|
||||||
|
|
||||||
|
// Username contains a user name passed in the request when
|
||||||
|
// Authenticated is true. It's value is undefined if Authenticated
|
||||||
|
// is false.
|
||||||
|
Username string
|
||||||
|
|
||||||
|
// ResponseHeaders contains extra headers that must be set by server
|
||||||
|
// when sending back HTTP response.
|
||||||
|
ResponseHeaders http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateHeaders updates headers with this Info's ResponseHeaders. It is
|
||||||
|
// safe to call this function on nil Info.
|
||||||
|
func (i *Info) UpdateHeaders(headers http.Header) {
|
||||||
|
if i == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, values := range i.ResponseHeaders {
|
||||||
|
for _, v := range values {
|
||||||
|
headers.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type key int // used for context keys
|
||||||
|
|
||||||
|
var infoKey key = 0
|
||||||
|
|
||||||
|
type AuthenticatorInterface interface {
|
||||||
|
// NewContext returns a new context carrying authentication
|
||||||
|
// information extracted from the request.
|
||||||
|
NewContext(ctx context.Context, r *http.Request) context.Context
|
||||||
|
|
||||||
|
// Wrap returns an http.HandlerFunc which wraps
|
||||||
|
// AuthenticatedHandlerFunc with this authenticator's
|
||||||
|
// authentication checks.
|
||||||
|
Wrap(AuthenticatedHandlerFunc) http.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContext returns authentication information from the context or
|
||||||
|
// nil if no such information present.
|
||||||
|
func FromContext(ctx context.Context) *Info {
|
||||||
|
info, ok := ctx.Value(infoKey).(*Info)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthUsernameHeader is the header set by JustCheck functions. It
|
||||||
|
// contains an authenticated username (if authentication was
|
||||||
|
// successful).
|
||||||
|
const AuthUsernameHeader = "X-Authenticated-Username"
|
||||||
|
|
||||||
|
func JustCheck(auth AuthenticatorInterface, wrapped http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return auth.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
|
||||||
|
ar.Header.Set(AuthUsernameHeader, ar.Username)
|
||||||
|
wrapped(w, &ar.Request)
|
||||||
|
})
|
||||||
|
}
|
||||||
163
vendor/github.com/abbot/go-http-auth/basic.go
generated
vendored
Executable file
163
vendor/github.com/abbot/go-http-auth/basic.go
generated
vendored
Executable file
@@ -0,0 +1,163 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type compareFunc func(hashedPassword, password []byte) error
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMismatchedHashAndPassword = errors.New("mismatched hash and password")
|
||||||
|
|
||||||
|
compareFuncs = []struct {
|
||||||
|
prefix string
|
||||||
|
compare compareFunc
|
||||||
|
}{
|
||||||
|
{"", compareMD5HashAndPassword}, // default compareFunc
|
||||||
|
{"{SHA}", compareShaHashAndPassword},
|
||||||
|
// Bcrypt is complicated. According to crypt(3) from
|
||||||
|
// crypt_blowfish version 1.3 (fetched from
|
||||||
|
// http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz), there
|
||||||
|
// are three different has prefixes: "$2a$", used by versions up
|
||||||
|
// to 1.0.4, and "$2x$" and "$2y$", used in all later
|
||||||
|
// versions. "$2a$" has a known bug, "$2x$" was added as a
|
||||||
|
// migration path for systems with "$2a$" prefix and still has a
|
||||||
|
// bug, and only "$2y$" should be used by modern systems. The bug
|
||||||
|
// has something to do with handling of 8-bit characters. Since
|
||||||
|
// both "$2a$" and "$2x$" are deprecated, we are handling them the
|
||||||
|
// same way as "$2y$", which will yield correct results for 7-bit
|
||||||
|
// character passwords, but is wrong for 8-bit character
|
||||||
|
// passwords. You have to upgrade to "$2y$" if you want sant 8-bit
|
||||||
|
// character password support with bcrypt. To add to the mess,
|
||||||
|
// OpenBSD 5.5. introduced "$2b$" prefix, which behaves exactly
|
||||||
|
// like "$2y$" according to the same source.
|
||||||
|
{"$2a$", bcrypt.CompareHashAndPassword},
|
||||||
|
{"$2b$", bcrypt.CompareHashAndPassword},
|
||||||
|
{"$2x$", bcrypt.CompareHashAndPassword},
|
||||||
|
{"$2y$", bcrypt.CompareHashAndPassword},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type BasicAuth struct {
|
||||||
|
Realm string
|
||||||
|
Secrets SecretProvider
|
||||||
|
// Headers used by authenticator. Set to ProxyHeaders to use with
|
||||||
|
// proxy server. When nil, NormalHeaders are used.
|
||||||
|
Headers *Headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that BasicAuth implements AuthenticatorInterface
|
||||||
|
var _ = (AuthenticatorInterface)((*BasicAuth)(nil))
|
||||||
|
|
||||||
|
/*
|
||||||
|
Checks the username/password combination from the request. Returns
|
||||||
|
either an empty string (authentication failed) or the name of the
|
||||||
|
authenticated user.
|
||||||
|
|
||||||
|
Supports MD5 and SHA1 password entries
|
||||||
|
*/
|
||||||
|
func (a *BasicAuth) CheckAuth(r *http.Request) string {
|
||||||
|
s := strings.SplitN(r.Header.Get(a.Headers.V().Authorization), " ", 2)
|
||||||
|
if len(s) != 2 || s[0] != "Basic" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
pair := strings.SplitN(string(b), ":", 2)
|
||||||
|
if len(pair) != 2 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
user, password := pair[0], pair[1]
|
||||||
|
secret := a.Secrets(user, a.Realm)
|
||||||
|
if secret == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
compare := compareFuncs[0].compare
|
||||||
|
for _, cmp := range compareFuncs[1:] {
|
||||||
|
if strings.HasPrefix(secret, cmp.prefix) {
|
||||||
|
compare = cmp.compare
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if compare([]byte(secret), []byte(password)) != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return pair[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareShaHashAndPassword(hashedPassword, password []byte) error {
|
||||||
|
d := sha1.New()
|
||||||
|
d.Write(password)
|
||||||
|
if subtle.ConstantTimeCompare(hashedPassword[5:], []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) != 1 {
|
||||||
|
return errMismatchedHashAndPassword
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareMD5HashAndPassword(hashedPassword, password []byte) error {
|
||||||
|
parts := bytes.SplitN(hashedPassword, []byte("$"), 4)
|
||||||
|
if len(parts) != 4 {
|
||||||
|
return errMismatchedHashAndPassword
|
||||||
|
}
|
||||||
|
magic := []byte("$" + string(parts[1]) + "$")
|
||||||
|
salt := parts[2]
|
||||||
|
if subtle.ConstantTimeCompare(hashedPassword, MD5Crypt(password, salt, magic)) != 1 {
|
||||||
|
return errMismatchedHashAndPassword
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
http.Handler for BasicAuth which initiates the authentication process
|
||||||
|
(or requires reauthentication).
|
||||||
|
*/
|
||||||
|
func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set(contentType, a.Headers.V().UnauthContentType)
|
||||||
|
w.Header().Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`)
|
||||||
|
w.WriteHeader(a.Headers.V().UnauthCode)
|
||||||
|
w.Write([]byte(a.Headers.V().UnauthResponse))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
BasicAuthenticator returns a function, which wraps an
|
||||||
|
AuthenticatedHandlerFunc converting it to http.HandlerFunc. This
|
||||||
|
wrapper function checks the authentication and either sends back
|
||||||
|
required authentication headers, or calls the wrapped function with
|
||||||
|
authenticated username in the AuthenticatedRequest.
|
||||||
|
*/
|
||||||
|
func (a *BasicAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if username := a.CheckAuth(r); username == "" {
|
||||||
|
a.RequireAuth(w, r)
|
||||||
|
} else {
|
||||||
|
ar := &AuthenticatedRequest{Request: *r, Username: username}
|
||||||
|
wrapped(w, ar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext returns a context carrying authentication information for the request.
|
||||||
|
func (a *BasicAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
|
||||||
|
info := &Info{Username: a.CheckAuth(r), ResponseHeaders: make(http.Header)}
|
||||||
|
info.Authenticated = (info.Username != "")
|
||||||
|
if !info.Authenticated {
|
||||||
|
info.ResponseHeaders.Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`)
|
||||||
|
}
|
||||||
|
return context.WithValue(ctx, infoKey, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBasicAuthenticator(realm string, secrets SecretProvider) *BasicAuth {
|
||||||
|
return &BasicAuth{Realm: realm, Secrets: secrets}
|
||||||
|
}
|
||||||
274
vendor/github.com/abbot/go-http-auth/digest.go
generated
vendored
Executable file
274
vendor/github.com/abbot/go-http-auth/digest.go
generated
vendored
Executable file
@@ -0,0 +1,274 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type digest_client struct {
|
||||||
|
nc uint64
|
||||||
|
last_seen int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type DigestAuth struct {
|
||||||
|
Realm string
|
||||||
|
Opaque string
|
||||||
|
Secrets SecretProvider
|
||||||
|
PlainTextSecrets bool
|
||||||
|
IgnoreNonceCount bool
|
||||||
|
// Headers used by authenticator. Set to ProxyHeaders to use with
|
||||||
|
// proxy server. When nil, NormalHeaders are used.
|
||||||
|
Headers *Headers
|
||||||
|
|
||||||
|
/*
|
||||||
|
Approximate size of Client's Cache. When actual number of
|
||||||
|
tracked client nonces exceeds
|
||||||
|
ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
|
||||||
|
older entries are purged.
|
||||||
|
*/
|
||||||
|
ClientCacheSize int
|
||||||
|
ClientCacheTolerance int
|
||||||
|
|
||||||
|
clients map[string]*digest_client
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that DigestAuth implements AuthenticatorInterface
|
||||||
|
var _ = (AuthenticatorInterface)((*DigestAuth)(nil))
|
||||||
|
|
||||||
|
type digest_cache_entry struct {
|
||||||
|
nonce string
|
||||||
|
last_seen int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type digest_cache []digest_cache_entry
|
||||||
|
|
||||||
|
func (c digest_cache) Less(i, j int) bool {
|
||||||
|
return c[i].last_seen < c[j].last_seen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c digest_cache) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c digest_cache) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove count oldest entries from DigestAuth.clients
|
||||||
|
*/
|
||||||
|
func (a *DigestAuth) Purge(count int) {
|
||||||
|
entries := make([]digest_cache_entry, 0, len(a.clients))
|
||||||
|
for nonce, client := range a.clients {
|
||||||
|
entries = append(entries, digest_cache_entry{nonce, client.last_seen})
|
||||||
|
}
|
||||||
|
cache := digest_cache(entries)
|
||||||
|
sort.Sort(cache)
|
||||||
|
for _, client := range cache[:count] {
|
||||||
|
delete(a.clients, client.nonce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
http.Handler for DigestAuth which initiates the authentication process
|
||||||
|
(or requires reauthentication).
|
||||||
|
*/
|
||||||
|
func (a *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance {
|
||||||
|
a.Purge(a.ClientCacheTolerance * 2)
|
||||||
|
}
|
||||||
|
nonce := RandomKey()
|
||||||
|
a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
|
||||||
|
w.Header().Set(contentType, a.Headers.V().UnauthContentType)
|
||||||
|
w.Header().Set(a.Headers.V().Authenticate,
|
||||||
|
fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`,
|
||||||
|
a.Realm, nonce, a.Opaque))
|
||||||
|
w.WriteHeader(a.Headers.V().UnauthCode)
|
||||||
|
w.Write([]byte(a.Headers.V().UnauthResponse))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parse Authorization header from the http.Request. Returns a map of
|
||||||
|
auth parameters or nil if the header is not a valid parsable Digest
|
||||||
|
auth header.
|
||||||
|
*/
|
||||||
|
func DigestAuthParams(authorization string) map[string]string {
|
||||||
|
s := strings.SplitN(authorization, " ", 2)
|
||||||
|
if len(s) != 2 || s[0] != "Digest" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParsePairs(s[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check if request contains valid authentication data. Returns a pair
|
||||||
|
of username, authinfo where username is the name of the authenticated
|
||||||
|
user or an empty string and authinfo is the contents for the optional
|
||||||
|
Authentication-Info response header.
|
||||||
|
*/
|
||||||
|
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
|
||||||
|
da.mutex.Lock()
|
||||||
|
defer da.mutex.Unlock()
|
||||||
|
username = ""
|
||||||
|
authinfo = nil
|
||||||
|
auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
|
||||||
|
if auth == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
// RFC2617 Section 3.2.1 specifies that unset value of algorithm in
|
||||||
|
// WWW-Authenticate Response header should be treated as
|
||||||
|
// "MD5". According to section 3.2.2 the "algorithm" value in
|
||||||
|
// subsequent Request Authorization header must be set to whatever
|
||||||
|
// was supplied in the WWW-Authenticate Response header. This
|
||||||
|
// implementation always returns an algorithm in WWW-Authenticate
|
||||||
|
// header, however there seems to be broken clients in the wild
|
||||||
|
// which do not set the algorithm. Assume the unset algorithm in
|
||||||
|
// Authorization header to be equal to MD5.
|
||||||
|
if _, ok := auth["algorithm"]; !ok {
|
||||||
|
auth["algorithm"] = "MD5"
|
||||||
|
}
|
||||||
|
if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the requested URI matches auth header
|
||||||
|
if r.RequestURI != auth["uri"] {
|
||||||
|
// We allow auth["uri"] to be a full path prefix of request-uri
|
||||||
|
// for some reason lost in history, which is probably wrong, but
|
||||||
|
// used to be like that for quite some time
|
||||||
|
// (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly
|
||||||
|
// says that auth["uri"] is the request-uri).
|
||||||
|
//
|
||||||
|
// TODO: make an option to allow only strict checking.
|
||||||
|
switch u, err := url.Parse(auth["uri"]); {
|
||||||
|
case err != nil:
|
||||||
|
return "", nil
|
||||||
|
case r.URL == nil:
|
||||||
|
return "", nil
|
||||||
|
case len(u.Path) > len(r.URL.Path):
|
||||||
|
return "", nil
|
||||||
|
case !strings.HasPrefix(r.URL.Path, u.Path):
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HA1 := da.Secrets(auth["username"], da.Realm)
|
||||||
|
if da.PlainTextSecrets {
|
||||||
|
HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
|
||||||
|
}
|
||||||
|
HA2 := H(r.Method + ":" + auth["uri"])
|
||||||
|
KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))
|
||||||
|
|
||||||
|
if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point crypto checks are completed and validated.
|
||||||
|
// Now check if the session is valid.
|
||||||
|
|
||||||
|
nc, err := strconv.ParseUint(auth["nc"], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if client, ok := da.clients[auth["nonce"]]; !ok {
|
||||||
|
return "", nil
|
||||||
|
} else {
|
||||||
|
if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
client.nc = nc
|
||||||
|
client.last_seen = time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
resp_HA2 := H(":" + auth["uri"])
|
||||||
|
rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], resp_HA2}, ":"))
|
||||||
|
|
||||||
|
info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
|
||||||
|
return auth["username"], &info
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
|
||||||
|
*/
|
||||||
|
const DefaultClientCacheSize = 1000
|
||||||
|
const DefaultClientCacheTolerance = 100
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wrap returns an Authenticator which uses HTTP Digest
|
||||||
|
authentication. Arguments:
|
||||||
|
|
||||||
|
realm: The authentication realm.
|
||||||
|
|
||||||
|
secrets: SecretProvider which must return HA1 digests for the same
|
||||||
|
realm as above.
|
||||||
|
*/
|
||||||
|
func (a *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if username, authinfo := a.CheckAuth(r); username == "" {
|
||||||
|
a.RequireAuth(w, r)
|
||||||
|
} else {
|
||||||
|
ar := &AuthenticatedRequest{Request: *r, Username: username}
|
||||||
|
if authinfo != nil {
|
||||||
|
w.Header().Set(a.Headers.V().AuthInfo, *authinfo)
|
||||||
|
}
|
||||||
|
wrapped(w, ar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
JustCheck returns function which converts an http.HandlerFunc into a
|
||||||
|
http.HandlerFunc which requires authentication. Username is passed as
|
||||||
|
an extra X-Authenticated-Username header.
|
||||||
|
*/
|
||||||
|
func (a *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return a.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
|
||||||
|
ar.Header.Set(AuthUsernameHeader, ar.Username)
|
||||||
|
wrapped(w, &ar.Request)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext returns a context carrying authentication information for the request.
|
||||||
|
func (a *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
|
||||||
|
username, authinfo := a.CheckAuth(r)
|
||||||
|
info := &Info{Username: username, ResponseHeaders: make(http.Header)}
|
||||||
|
if username != "" {
|
||||||
|
info.Authenticated = true
|
||||||
|
info.ResponseHeaders.Set(a.Headers.V().AuthInfo, *authinfo)
|
||||||
|
} else {
|
||||||
|
// return back digest WWW-Authenticate header
|
||||||
|
if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance {
|
||||||
|
a.Purge(a.ClientCacheTolerance * 2)
|
||||||
|
}
|
||||||
|
nonce := RandomKey()
|
||||||
|
a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
|
||||||
|
info.ResponseHeaders.Set(a.Headers.V().Authenticate,
|
||||||
|
fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`,
|
||||||
|
a.Realm, nonce, a.Opaque))
|
||||||
|
}
|
||||||
|
return context.WithValue(ctx, infoKey, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
|
||||||
|
da := &DigestAuth{
|
||||||
|
Opaque: RandomKey(),
|
||||||
|
Realm: realm,
|
||||||
|
Secrets: secrets,
|
||||||
|
PlainTextSecrets: false,
|
||||||
|
ClientCacheSize: DefaultClientCacheSize,
|
||||||
|
ClientCacheTolerance: DefaultClientCacheTolerance,
|
||||||
|
clients: map[string]*digest_client{}}
|
||||||
|
return da
|
||||||
|
}
|
||||||
92
vendor/github.com/abbot/go-http-auth/md5crypt.go
generated
vendored
Executable file
92
vendor/github.com/abbot/go-http-auth/md5crypt.go
generated
vendored
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import "crypto/md5"
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
const itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
var md5_crypt_swaps = [16]int{12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11}
|
||||||
|
|
||||||
|
type MD5Entry struct {
|
||||||
|
Magic, Salt, Hash []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMD5Entry(e string) *MD5Entry {
|
||||||
|
parts := strings.SplitN(e, "$", 4)
|
||||||
|
if len(parts) != 4 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &MD5Entry{
|
||||||
|
Magic: []byte("$" + parts[1] + "$"),
|
||||||
|
Salt: []byte(parts[2]),
|
||||||
|
Hash: []byte(parts[3]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MD5 password crypt implementation
|
||||||
|
*/
|
||||||
|
func MD5Crypt(password, salt, magic []byte) []byte {
|
||||||
|
d := md5.New()
|
||||||
|
|
||||||
|
d.Write(password)
|
||||||
|
d.Write(magic)
|
||||||
|
d.Write(salt)
|
||||||
|
|
||||||
|
d2 := md5.New()
|
||||||
|
d2.Write(password)
|
||||||
|
d2.Write(salt)
|
||||||
|
d2.Write(password)
|
||||||
|
|
||||||
|
for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
|
||||||
|
d.Write([]byte{mixin[i%16]})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(password); i != 0; i >>= 1 {
|
||||||
|
if i&1 == 0 {
|
||||||
|
d.Write([]byte{password[0]})
|
||||||
|
} else {
|
||||||
|
d.Write([]byte{0})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final := d.Sum(nil)
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
d2 := md5.New()
|
||||||
|
if i&1 == 0 {
|
||||||
|
d2.Write(final)
|
||||||
|
} else {
|
||||||
|
d2.Write(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i%3 != 0 {
|
||||||
|
d2.Write(salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i%7 != 0 {
|
||||||
|
d2.Write(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i&1 == 0 {
|
||||||
|
d2.Write(password)
|
||||||
|
} else {
|
||||||
|
d2.Write(final)
|
||||||
|
}
|
||||||
|
final = d2.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]byte, 0, 22)
|
||||||
|
v := uint(0)
|
||||||
|
bits := uint(0)
|
||||||
|
for _, i := range md5_crypt_swaps {
|
||||||
|
v |= (uint(final[i]) << bits)
|
||||||
|
for bits = bits + 8; bits > 6; bits -= 6 {
|
||||||
|
result = append(result, itoa64[v&0x3f])
|
||||||
|
v >>= 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, itoa64[v&0x3f])
|
||||||
|
|
||||||
|
return append(append(append(magic, salt...), '$'), result...)
|
||||||
|
}
|
||||||
141
vendor/github.com/abbot/go-http-auth/misc.go
generated
vendored
Executable file
141
vendor/github.com/abbot/go-http-auth/misc.go
generated
vendored
Executable file
@@ -0,0 +1,141 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RandomKey returns a random 16-byte base64 alphabet string
|
||||||
|
func RandomKey() string {
|
||||||
|
k := make([]byte, 12)
|
||||||
|
for bytes := 0; bytes < len(k); {
|
||||||
|
n, err := rand.Read(k[bytes:])
|
||||||
|
if err != nil {
|
||||||
|
panic("rand.Read() failed")
|
||||||
|
}
|
||||||
|
bytes += n
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// H function for MD5 algorithm (returns a lower-case hex MD5 digest)
|
||||||
|
func H(data string) string {
|
||||||
|
digest := md5.New()
|
||||||
|
digest.Write([]byte(data))
|
||||||
|
return fmt.Sprintf("%x", digest.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseList parses a comma-separated list of values as described by
|
||||||
|
// RFC 2068 and returns list elements.
|
||||||
|
//
|
||||||
|
// Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go
|
||||||
|
// which was ported from urllib2.parse_http_list, from the Python
|
||||||
|
// standard library.
|
||||||
|
func ParseList(value string) []string {
|
||||||
|
var list []string
|
||||||
|
var escape, quote bool
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
for _, r := range value {
|
||||||
|
switch {
|
||||||
|
case escape:
|
||||||
|
b.WriteRune(r)
|
||||||
|
escape = false
|
||||||
|
case quote:
|
||||||
|
if r == '\\' {
|
||||||
|
escape = true
|
||||||
|
} else {
|
||||||
|
if r == '"' {
|
||||||
|
quote = false
|
||||||
|
}
|
||||||
|
b.WriteRune(r)
|
||||||
|
}
|
||||||
|
case r == ',':
|
||||||
|
list = append(list, strings.TrimSpace(b.String()))
|
||||||
|
b.Reset()
|
||||||
|
case r == '"':
|
||||||
|
quote = true
|
||||||
|
b.WriteRune(r)
|
||||||
|
default:
|
||||||
|
b.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Append last part.
|
||||||
|
if s := b.String(); s != "" {
|
||||||
|
list = append(list, strings.TrimSpace(s))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePairs extracts key/value pairs from a comma-separated list of
|
||||||
|
// values as described by RFC 2068 and returns a map[key]value. The
|
||||||
|
// resulting values are unquoted. If a list element doesn't contain a
|
||||||
|
// "=", the key is the element itself and the value is an empty
|
||||||
|
// string.
|
||||||
|
//
|
||||||
|
// Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go
|
||||||
|
func ParsePairs(value string) map[string]string {
|
||||||
|
m := make(map[string]string)
|
||||||
|
for _, pair := range ParseList(strings.TrimSpace(value)) {
|
||||||
|
if i := strings.Index(pair, "="); i < 0 {
|
||||||
|
m[pair] = ""
|
||||||
|
} else {
|
||||||
|
v := pair[i+1:]
|
||||||
|
if v[0] == '"' && v[len(v)-1] == '"' {
|
||||||
|
// Unquote it.
|
||||||
|
v = v[1 : len(v)-1]
|
||||||
|
}
|
||||||
|
m[pair[:i]] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers contains header and error codes used by authenticator.
|
||||||
|
type Headers struct {
|
||||||
|
Authenticate string // WWW-Authenticate
|
||||||
|
Authorization string // Authorization
|
||||||
|
AuthInfo string // Authentication-Info
|
||||||
|
UnauthCode int // 401
|
||||||
|
UnauthContentType string // text/plain
|
||||||
|
UnauthResponse string // Unauthorized.
|
||||||
|
}
|
||||||
|
|
||||||
|
// V returns NormalHeaders when h is nil, or h otherwise. Allows to
|
||||||
|
// use uninitialized *Headers values in structs.
|
||||||
|
func (h *Headers) V() *Headers {
|
||||||
|
if h == nil {
|
||||||
|
return NormalHeaders
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// NormalHeaders are the regular Headers used by an HTTP Server for
|
||||||
|
// request authentication.
|
||||||
|
NormalHeaders = &Headers{
|
||||||
|
Authenticate: "WWW-Authenticate",
|
||||||
|
Authorization: "Authorization",
|
||||||
|
AuthInfo: "Authentication-Info",
|
||||||
|
UnauthCode: http.StatusUnauthorized,
|
||||||
|
UnauthContentType: "text/plain",
|
||||||
|
UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyHeaders are Headers used by an HTTP Proxy server for proxy
|
||||||
|
// access authentication.
|
||||||
|
ProxyHeaders = &Headers{
|
||||||
|
Authenticate: "Proxy-Authenticate",
|
||||||
|
Authorization: "Proxy-Authorization",
|
||||||
|
AuthInfo: "Proxy-Authentication-Info",
|
||||||
|
UnauthCode: http.StatusProxyAuthRequired,
|
||||||
|
UnauthContentType: "text/plain",
|
||||||
|
UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusProxyAuthRequired, http.StatusText(http.StatusProxyAuthRequired)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const contentType = "Content-Type"
|
||||||
1
vendor/github.com/abbot/go-http-auth/test.htdigest
generated
vendored
Executable file
1
vendor/github.com/abbot/go-http-auth/test.htdigest
generated
vendored
Executable file
@@ -0,0 +1 @@
|
|||||||
|
test:example.com:aa78524fceb0e50fd8ca96dd818b8cf9
|
||||||
4
vendor/github.com/abbot/go-http-auth/test.htpasswd
generated
vendored
Executable file
4
vendor/github.com/abbot/go-http-auth/test.htpasswd
generated
vendored
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
test:{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=
|
||||||
|
test2:$apr1$a0j62R97$mYqFkloXH0/UOaUnAiV2b0
|
||||||
|
test16:$apr1$JI4wh3am$AmhephVqLTUyAVpFQeHZC0
|
||||||
|
test3:$2y$05$ih3C91zUBSTFcAh2mQnZYuob0UOZVEf16wl/ukgjDhjvj.xgM1WwS
|
||||||
154
vendor/github.com/abbot/go-http-auth/users.go
generated
vendored
Executable file
154
vendor/github.com/abbot/go-http-auth/users.go
generated
vendored
Executable file
@@ -0,0 +1,154 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
SecretProvider is used by authenticators. Takes user name and realm
|
||||||
|
as an argument, returns secret required for authentication (HA1 for
|
||||||
|
digest authentication, properly encrypted password for basic).
|
||||||
|
|
||||||
|
Returning an empty string means failing the authentication.
|
||||||
|
*/
|
||||||
|
type SecretProvider func(user, realm string) string
|
||||||
|
|
||||||
|
/*
|
||||||
|
Common functions for file auto-reloading
|
||||||
|
*/
|
||||||
|
type File struct {
|
||||||
|
Path string
|
||||||
|
Info os.FileInfo
|
||||||
|
/* must be set in inherited types during initialization */
|
||||||
|
Reload func()
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) ReloadIfNeeded() {
|
||||||
|
info, err := os.Stat(f.Path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
f.mu.Lock()
|
||||||
|
defer f.mu.Unlock()
|
||||||
|
if f.Info == nil || f.Info.ModTime() != info.ModTime() {
|
||||||
|
f.Info = info
|
||||||
|
f.Reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Structure used for htdigest file authentication. Users map realms to
|
||||||
|
maps of users to their HA1 digests.
|
||||||
|
*/
|
||||||
|
type HtdigestFile struct {
|
||||||
|
File
|
||||||
|
Users map[string]map[string]string
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func reload_htdigest(hf *HtdigestFile) {
|
||||||
|
r, err := os.Open(hf.Path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
csv_reader := csv.NewReader(r)
|
||||||
|
csv_reader.Comma = ':'
|
||||||
|
csv_reader.Comment = '#'
|
||||||
|
csv_reader.TrimLeadingSpace = true
|
||||||
|
|
||||||
|
records, err := csv_reader.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hf.mu.Lock()
|
||||||
|
defer hf.mu.Unlock()
|
||||||
|
hf.Users = make(map[string]map[string]string)
|
||||||
|
for _, record := range records {
|
||||||
|
_, exists := hf.Users[record[1]]
|
||||||
|
if !exists {
|
||||||
|
hf.Users[record[1]] = make(map[string]string)
|
||||||
|
}
|
||||||
|
hf.Users[record[1]][record[0]] = record[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SecretProvider implementation based on htdigest-formated files. Will
|
||||||
|
reload htdigest file on changes. Will panic on syntax errors in
|
||||||
|
htdigest files.
|
||||||
|
*/
|
||||||
|
func HtdigestFileProvider(filename string) SecretProvider {
|
||||||
|
hf := &HtdigestFile{File: File{Path: filename}}
|
||||||
|
hf.Reload = func() { reload_htdigest(hf) }
|
||||||
|
return func(user, realm string) string {
|
||||||
|
hf.ReloadIfNeeded()
|
||||||
|
hf.mu.RLock()
|
||||||
|
defer hf.mu.RUnlock()
|
||||||
|
_, exists := hf.Users[realm]
|
||||||
|
if !exists {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
digest, exists := hf.Users[realm][user]
|
||||||
|
if !exists {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return digest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Structure used for htdigest file authentication. Users map users to
|
||||||
|
their salted encrypted password
|
||||||
|
*/
|
||||||
|
type HtpasswdFile struct {
|
||||||
|
File
|
||||||
|
Users map[string]string
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func reload_htpasswd(h *HtpasswdFile) {
|
||||||
|
r, err := os.Open(h.Path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
csv_reader := csv.NewReader(r)
|
||||||
|
csv_reader.Comma = ':'
|
||||||
|
csv_reader.Comment = '#'
|
||||||
|
csv_reader.TrimLeadingSpace = true
|
||||||
|
|
||||||
|
records, err := csv_reader.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.mu.Lock()
|
||||||
|
defer h.mu.Unlock()
|
||||||
|
h.Users = make(map[string]string)
|
||||||
|
for _, record := range records {
|
||||||
|
h.Users[record[0]] = record[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SecretProvider implementation based on htpasswd-formated files. Will
|
||||||
|
reload htpasswd file on changes. Will panic on syntax errors in
|
||||||
|
htpasswd files. Realm argument of the SecretProvider is ignored.
|
||||||
|
*/
|
||||||
|
func HtpasswdFileProvider(filename string) SecretProvider {
|
||||||
|
h := &HtpasswdFile{File: File{Path: filename}}
|
||||||
|
h.Reload = func() { reload_htpasswd(h) }
|
||||||
|
return func(user, realm string) string {
|
||||||
|
h.ReloadIfNeeded()
|
||||||
|
h.mu.RLock()
|
||||||
|
password, exists := h.Users[user]
|
||||||
|
h.mu.RUnlock()
|
||||||
|
if !exists {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
}
|
||||||
24
vendor/github.com/andybalholm/cascadia/LICENSE
generated
vendored
Executable file
24
vendor/github.com/andybalholm/cascadia/LICENSE
generated
vendored
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) 2011 Andy Balholm. 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.
|
||||||
|
|
||||||
|
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.
|
||||||
9
vendor/github.com/andybalholm/cascadia/README.md
generated
vendored
Normal file
9
vendor/github.com/andybalholm/cascadia/README.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# cascadia
|
||||||
|
|
||||||
|
[](https://travis-ci.org/andybalholm/cascadia)
|
||||||
|
|
||||||
|
The Cascadia package implements CSS selectors for use with the parse trees produced by the html package.
|
||||||
|
|
||||||
|
To test CSS selectors without writing Go code, check out [cascadia](https://github.com/suntong/cascadia) the command line tool, a thin wrapper around this package.
|
||||||
|
|
||||||
|
[Refer to godoc here](https://godoc.org/github.com/andybalholm/cascadia).
|
||||||
5
vendor/github.com/andybalholm/cascadia/go.mod
generated
vendored
Normal file
5
vendor/github.com/andybalholm/cascadia/go.mod
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module github.com/andybalholm/cascadia
|
||||||
|
|
||||||
|
require golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01
|
||||||
|
|
||||||
|
go 1.13
|
||||||
838
vendor/github.com/andybalholm/cascadia/parser.go
generated
vendored
Normal file
838
vendor/github.com/andybalholm/cascadia/parser.go
generated
vendored
Normal file
@@ -0,0 +1,838 @@
|
|||||||
|
// Package cascadia is an implementation of CSS selectors.
|
||||||
|
package cascadia
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// a parser for CSS selectors
|
||||||
|
type parser struct {
|
||||||
|
s string // the source text
|
||||||
|
i int // the current position
|
||||||
|
|
||||||
|
// if `false`, parsing a pseudo-element
|
||||||
|
// returns an error.
|
||||||
|
acceptPseudoElements bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseEscape parses a backslash escape.
|
||||||
|
func (p *parser) parseEscape() (result string, err error) {
|
||||||
|
if len(p.s) < p.i+2 || p.s[p.i] != '\\' {
|
||||||
|
return "", errors.New("invalid escape sequence")
|
||||||
|
}
|
||||||
|
|
||||||
|
start := p.i + 1
|
||||||
|
c := p.s[start]
|
||||||
|
switch {
|
||||||
|
case c == '\r' || c == '\n' || c == '\f':
|
||||||
|
return "", errors.New("escaped line ending outside string")
|
||||||
|
case hexDigit(c):
|
||||||
|
// unicode escape (hex)
|
||||||
|
var i int
|
||||||
|
for i = start; i < start+6 && i < len(p.s) && hexDigit(p.s[i]); i++ {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
v, _ := strconv.ParseUint(p.s[start:i], 16, 21)
|
||||||
|
if len(p.s) > i {
|
||||||
|
switch p.s[i] {
|
||||||
|
case '\r':
|
||||||
|
i++
|
||||||
|
if len(p.s) > i && p.s[i] == '\n' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
case ' ', '\t', '\n', '\f':
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.i = i
|
||||||
|
return string(rune(v)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the literal character after the backslash.
|
||||||
|
result = p.s[start : start+1]
|
||||||
|
p.i += 2
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toLowerASCII returns s with all ASCII capital letters lowercased.
|
||||||
|
func toLowerASCII(s string) string {
|
||||||
|
var b []byte
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if c := s[i]; 'A' <= c && c <= 'Z' {
|
||||||
|
if b == nil {
|
||||||
|
b = make([]byte, len(s))
|
||||||
|
copy(b, s)
|
||||||
|
}
|
||||||
|
b[i] = s[i] + ('a' - 'A')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hexDigit(c byte) bool {
|
||||||
|
return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
|
||||||
|
}
|
||||||
|
|
||||||
|
// nameStart returns whether c can be the first character of an identifier
|
||||||
|
// (not counting an initial hyphen, or an escape sequence).
|
||||||
|
func nameStart(c byte) bool {
|
||||||
|
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127
|
||||||
|
}
|
||||||
|
|
||||||
|
// nameChar returns whether c can be a character within an identifier
|
||||||
|
// (not counting an escape sequence).
|
||||||
|
func nameChar(c byte) bool {
|
||||||
|
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127 ||
|
||||||
|
c == '-' || '0' <= c && c <= '9'
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseIdentifier parses an identifier.
|
||||||
|
func (p *parser) parseIdentifier() (result string, err error) {
|
||||||
|
startingDash := false
|
||||||
|
if len(p.s) > p.i && p.s[p.i] == '-' {
|
||||||
|
startingDash = true
|
||||||
|
p.i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.s) <= p.i {
|
||||||
|
return "", errors.New("expected identifier, found EOF instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c := p.s[p.i]; !(nameStart(c) || c == '\\') {
|
||||||
|
return "", fmt.Errorf("expected identifier, found %c instead", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err = p.parseName()
|
||||||
|
if startingDash && err == nil {
|
||||||
|
result = "-" + result
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseName parses a name (which is like an identifier, but doesn't have
|
||||||
|
// extra restrictions on the first character).
|
||||||
|
func (p *parser) parseName() (result string, err error) {
|
||||||
|
i := p.i
|
||||||
|
loop:
|
||||||
|
for i < len(p.s) {
|
||||||
|
c := p.s[i]
|
||||||
|
switch {
|
||||||
|
case nameChar(c):
|
||||||
|
start := i
|
||||||
|
for i < len(p.s) && nameChar(p.s[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
result += p.s[start:i]
|
||||||
|
case c == '\\':
|
||||||
|
p.i = i
|
||||||
|
val, err := p.parseEscape()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
i = p.i
|
||||||
|
result += val
|
||||||
|
default:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == "" {
|
||||||
|
return "", errors.New("expected name, found EOF instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.i = i
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseString parses a single- or double-quoted string.
|
||||||
|
func (p *parser) parseString() (result string, err error) {
|
||||||
|
i := p.i
|
||||||
|
if len(p.s) < i+2 {
|
||||||
|
return "", errors.New("expected string, found EOF instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
quote := p.s[i]
|
||||||
|
i++
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for i < len(p.s) {
|
||||||
|
switch p.s[i] {
|
||||||
|
case '\\':
|
||||||
|
if len(p.s) > i+1 {
|
||||||
|
switch c := p.s[i+1]; c {
|
||||||
|
case '\r':
|
||||||
|
if len(p.s) > i+2 && p.s[i+2] == '\n' {
|
||||||
|
i += 3
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case '\n', '\f':
|
||||||
|
i += 2
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.i = i
|
||||||
|
val, err := p.parseEscape()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
i = p.i
|
||||||
|
result += val
|
||||||
|
case quote:
|
||||||
|
break loop
|
||||||
|
case '\r', '\n', '\f':
|
||||||
|
return "", errors.New("unexpected end of line in string")
|
||||||
|
default:
|
||||||
|
start := i
|
||||||
|
for i < len(p.s) {
|
||||||
|
if c := p.s[i]; c == quote || c == '\\' || c == '\r' || c == '\n' || c == '\f' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
result += p.s[start:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= len(p.s) {
|
||||||
|
return "", errors.New("EOF in string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume the final quote.
|
||||||
|
i++
|
||||||
|
|
||||||
|
p.i = i
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseRegex parses a regular expression; the end is defined by encountering an
|
||||||
|
// unmatched closing ')' or ']' which is not consumed
|
||||||
|
func (p *parser) parseRegex() (rx *regexp.Regexp, err error) {
|
||||||
|
i := p.i
|
||||||
|
if len(p.s) < i+2 {
|
||||||
|
return nil, errors.New("expected regular expression, found EOF instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
// number of open parens or brackets;
|
||||||
|
// when it becomes negative, finished parsing regex
|
||||||
|
open := 0
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for i < len(p.s) {
|
||||||
|
switch p.s[i] {
|
||||||
|
case '(', '[':
|
||||||
|
open++
|
||||||
|
case ')', ']':
|
||||||
|
open--
|
||||||
|
if open < 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= len(p.s) {
|
||||||
|
return nil, errors.New("EOF in regular expression")
|
||||||
|
}
|
||||||
|
rx, err = regexp.Compile(p.s[p.i:i])
|
||||||
|
p.i = i
|
||||||
|
return rx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// skipWhitespace consumes whitespace characters and comments.
|
||||||
|
// It returns true if there was actually anything to skip.
|
||||||
|
func (p *parser) skipWhitespace() bool {
|
||||||
|
i := p.i
|
||||||
|
for i < len(p.s) {
|
||||||
|
switch p.s[i] {
|
||||||
|
case ' ', '\t', '\r', '\n', '\f':
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
case '/':
|
||||||
|
if strings.HasPrefix(p.s[i:], "/*") {
|
||||||
|
end := strings.Index(p.s[i+len("/*"):], "*/")
|
||||||
|
if end != -1 {
|
||||||
|
i += end + len("/**/")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > p.i {
|
||||||
|
p.i = i
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeParenthesis consumes an opening parenthesis and any following
|
||||||
|
// whitespace. It returns true if there was actually a parenthesis to skip.
|
||||||
|
func (p *parser) consumeParenthesis() bool {
|
||||||
|
if p.i < len(p.s) && p.s[p.i] == '(' {
|
||||||
|
p.i++
|
||||||
|
p.skipWhitespace()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeClosingParenthesis consumes a closing parenthesis and any preceding
|
||||||
|
// whitespace. It returns true if there was actually a parenthesis to skip.
|
||||||
|
func (p *parser) consumeClosingParenthesis() bool {
|
||||||
|
i := p.i
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.i < len(p.s) && p.s[p.i] == ')' {
|
||||||
|
p.i++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
p.i = i
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTypeSelector parses a type selector (one that matches by tag name).
|
||||||
|
func (p *parser) parseTypeSelector() (result tagSelector, err error) {
|
||||||
|
tag, err := p.parseIdentifier()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return tagSelector{tag: toLowerASCII(tag)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseIDSelector parses a selector that matches by id attribute.
|
||||||
|
func (p *parser) parseIDSelector() (idSelector, error) {
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return idSelector{}, fmt.Errorf("expected id selector (#id), found EOF instead")
|
||||||
|
}
|
||||||
|
if p.s[p.i] != '#' {
|
||||||
|
return idSelector{}, fmt.Errorf("expected id selector (#id), found '%c' instead", p.s[p.i])
|
||||||
|
}
|
||||||
|
|
||||||
|
p.i++
|
||||||
|
id, err := p.parseName()
|
||||||
|
if err != nil {
|
||||||
|
return idSelector{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return idSelector{id: id}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseClassSelector parses a selector that matches by class attribute.
|
||||||
|
func (p *parser) parseClassSelector() (classSelector, error) {
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return classSelector{}, fmt.Errorf("expected class selector (.class), found EOF instead")
|
||||||
|
}
|
||||||
|
if p.s[p.i] != '.' {
|
||||||
|
return classSelector{}, fmt.Errorf("expected class selector (.class), found '%c' instead", p.s[p.i])
|
||||||
|
}
|
||||||
|
|
||||||
|
p.i++
|
||||||
|
class, err := p.parseIdentifier()
|
||||||
|
if err != nil {
|
||||||
|
return classSelector{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return classSelector{class: class}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseAttributeSelector parses a selector that matches by attribute value.
|
||||||
|
func (p *parser) parseAttributeSelector() (attrSelector, error) {
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead")
|
||||||
|
}
|
||||||
|
if p.s[p.i] != '[' {
|
||||||
|
return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", p.s[p.i])
|
||||||
|
}
|
||||||
|
|
||||||
|
p.i++
|
||||||
|
p.skipWhitespace()
|
||||||
|
key, err := p.parseIdentifier()
|
||||||
|
if err != nil {
|
||||||
|
return attrSelector{}, err
|
||||||
|
}
|
||||||
|
key = toLowerASCII(key)
|
||||||
|
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.s[p.i] == ']' {
|
||||||
|
p.i++
|
||||||
|
return attrSelector{key: key, operation: ""}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.i+2 >= len(p.s) {
|
||||||
|
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
||||||
|
}
|
||||||
|
|
||||||
|
op := p.s[p.i : p.i+2]
|
||||||
|
if op[0] == '=' {
|
||||||
|
op = "="
|
||||||
|
} else if op[1] != '=' {
|
||||||
|
return attrSelector{}, fmt.Errorf(`expected equality operator, found "%s" instead`, op)
|
||||||
|
}
|
||||||
|
p.i += len(op)
|
||||||
|
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
||||||
|
}
|
||||||
|
var val string
|
||||||
|
var rx *regexp.Regexp
|
||||||
|
if op == "#=" {
|
||||||
|
rx, err = p.parseRegex()
|
||||||
|
} else {
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '\'', '"':
|
||||||
|
val, err = p.parseString()
|
||||||
|
default:
|
||||||
|
val, err = p.parseIdentifier()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return attrSelector{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
||||||
|
}
|
||||||
|
if p.s[p.i] != ']' {
|
||||||
|
return attrSelector{}, fmt.Errorf("expected ']', found '%c' instead", p.s[p.i])
|
||||||
|
}
|
||||||
|
p.i++
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case "=", "!=", "~=", "|=", "^=", "$=", "*=", "#=":
|
||||||
|
return attrSelector{key: key, val: val, operation: op, regexp: rx}, nil
|
||||||
|
default:
|
||||||
|
return attrSelector{}, fmt.Errorf("attribute operator %q is not supported", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errExpectedParenthesis = errors.New("expected '(' but didn't find it")
|
||||||
|
var errExpectedClosingParenthesis = errors.New("expected ')' but didn't find it")
|
||||||
|
var errUnmatchedParenthesis = errors.New("unmatched '('")
|
||||||
|
|
||||||
|
// parsePseudoclassSelector parses a pseudoclass selector like :not(p) or a pseudo-element
|
||||||
|
// For backwards compatibility, both ':' and '::' prefix are allowed for pseudo-elements.
|
||||||
|
// https://drafts.csswg.org/selectors-3/#pseudo-elements
|
||||||
|
// Returning a nil `Sel` (and a nil `error`) means we found a pseudo-element.
|
||||||
|
func (p *parser) parsePseudoclassSelector() (out Sel, pseudoElement string, err error) {
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return nil, "", fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead")
|
||||||
|
}
|
||||||
|
if p.s[p.i] != ':' {
|
||||||
|
return nil, "", fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", p.s[p.i])
|
||||||
|
}
|
||||||
|
|
||||||
|
p.i++
|
||||||
|
var mustBePseudoElement bool
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return nil, "", fmt.Errorf("got empty pseudoclass (or pseudoelement)")
|
||||||
|
}
|
||||||
|
if p.s[p.i] == ':' { // we found a pseudo-element
|
||||||
|
mustBePseudoElement = true
|
||||||
|
p.i++
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := p.parseIdentifier()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name = toLowerASCII(name)
|
||||||
|
if mustBePseudoElement && (name != "after" && name != "backdrop" && name != "before" &&
|
||||||
|
name != "cue" && name != "first-letter" && name != "first-line" && name != "grammar-error" &&
|
||||||
|
name != "marker" && name != "placeholder" && name != "selection" && name != "spelling-error") {
|
||||||
|
return out, "", fmt.Errorf("unknown pseudoelement :%s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch name {
|
||||||
|
case "not", "has", "haschild":
|
||||||
|
if !p.consumeParenthesis() {
|
||||||
|
return out, "", errExpectedParenthesis
|
||||||
|
}
|
||||||
|
sel, parseErr := p.parseSelectorGroup()
|
||||||
|
if parseErr != nil {
|
||||||
|
return out, "", parseErr
|
||||||
|
}
|
||||||
|
if !p.consumeClosingParenthesis() {
|
||||||
|
return out, "", errExpectedClosingParenthesis
|
||||||
|
}
|
||||||
|
|
||||||
|
out = relativePseudoClassSelector{name: name, match: sel}
|
||||||
|
|
||||||
|
case "contains", "containsown":
|
||||||
|
if !p.consumeParenthesis() {
|
||||||
|
return out, "", errExpectedParenthesis
|
||||||
|
}
|
||||||
|
if p.i == len(p.s) {
|
||||||
|
return out, "", errUnmatchedParenthesis
|
||||||
|
}
|
||||||
|
var val string
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '\'', '"':
|
||||||
|
val, err = p.parseString()
|
||||||
|
default:
|
||||||
|
val, err = p.parseIdentifier()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return out, "", err
|
||||||
|
}
|
||||||
|
val = strings.ToLower(val)
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return out, "", errors.New("unexpected EOF in pseudo selector")
|
||||||
|
}
|
||||||
|
if !p.consumeClosingParenthesis() {
|
||||||
|
return out, "", errExpectedClosingParenthesis
|
||||||
|
}
|
||||||
|
|
||||||
|
out = containsPseudoClassSelector{own: name == "containsown", value: val}
|
||||||
|
|
||||||
|
case "matches", "matchesown":
|
||||||
|
if !p.consumeParenthesis() {
|
||||||
|
return out, "", errExpectedParenthesis
|
||||||
|
}
|
||||||
|
rx, err := p.parseRegex()
|
||||||
|
if err != nil {
|
||||||
|
return out, "", err
|
||||||
|
}
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return out, "", errors.New("unexpected EOF in pseudo selector")
|
||||||
|
}
|
||||||
|
if !p.consumeClosingParenthesis() {
|
||||||
|
return out, "", errExpectedClosingParenthesis
|
||||||
|
}
|
||||||
|
|
||||||
|
out = regexpPseudoClassSelector{own: name == "matchesown", regexp: rx}
|
||||||
|
|
||||||
|
case "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type":
|
||||||
|
if !p.consumeParenthesis() {
|
||||||
|
return out, "", errExpectedParenthesis
|
||||||
|
}
|
||||||
|
a, b, err := p.parseNth()
|
||||||
|
if err != nil {
|
||||||
|
return out, "", err
|
||||||
|
}
|
||||||
|
if !p.consumeClosingParenthesis() {
|
||||||
|
return out, "", errExpectedClosingParenthesis
|
||||||
|
}
|
||||||
|
last := name == "nth-last-child" || name == "nth-last-of-type"
|
||||||
|
ofType := name == "nth-of-type" || name == "nth-last-of-type"
|
||||||
|
out = nthPseudoClassSelector{a: a, b: b, last: last, ofType: ofType}
|
||||||
|
|
||||||
|
case "first-child":
|
||||||
|
out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: false}
|
||||||
|
case "last-child":
|
||||||
|
out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: true}
|
||||||
|
case "first-of-type":
|
||||||
|
out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: false}
|
||||||
|
case "last-of-type":
|
||||||
|
out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: true}
|
||||||
|
case "only-child":
|
||||||
|
out = onlyChildPseudoClassSelector{ofType: false}
|
||||||
|
case "only-of-type":
|
||||||
|
out = onlyChildPseudoClassSelector{ofType: true}
|
||||||
|
case "input":
|
||||||
|
out = inputPseudoClassSelector{}
|
||||||
|
case "empty":
|
||||||
|
out = emptyElementPseudoClassSelector{}
|
||||||
|
case "root":
|
||||||
|
out = rootPseudoClassSelector{}
|
||||||
|
case "after", "backdrop", "before", "cue", "first-letter", "first-line", "grammar-error", "marker", "placeholder", "selection", "spelling-error":
|
||||||
|
return nil, name, nil
|
||||||
|
default:
|
||||||
|
return out, "", fmt.Errorf("unknown pseudoclass or pseudoelement :%s", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseInteger parses a decimal integer.
|
||||||
|
func (p *parser) parseInteger() (int, error) {
|
||||||
|
i := p.i
|
||||||
|
start := i
|
||||||
|
for i < len(p.s) && '0' <= p.s[i] && p.s[i] <= '9' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == start {
|
||||||
|
return 0, errors.New("expected integer, but didn't find it")
|
||||||
|
}
|
||||||
|
p.i = i
|
||||||
|
|
||||||
|
val, err := strconv.Atoi(p.s[start:i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseNth parses the argument for :nth-child (normally of the form an+b).
|
||||||
|
func (p *parser) parseNth() (a, b int, err error) {
|
||||||
|
// initial state
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
goto eof
|
||||||
|
}
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '-':
|
||||||
|
p.i++
|
||||||
|
goto negativeA
|
||||||
|
case '+':
|
||||||
|
p.i++
|
||||||
|
goto positiveA
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
goto positiveA
|
||||||
|
case 'n', 'N':
|
||||||
|
a = 1
|
||||||
|
p.i++
|
||||||
|
goto readN
|
||||||
|
case 'o', 'O', 'e', 'E':
|
||||||
|
id, nameErr := p.parseName()
|
||||||
|
if nameErr != nil {
|
||||||
|
return 0, 0, nameErr
|
||||||
|
}
|
||||||
|
id = toLowerASCII(id)
|
||||||
|
if id == "odd" {
|
||||||
|
return 2, 1, nil
|
||||||
|
}
|
||||||
|
if id == "even" {
|
||||||
|
return 2, 0, nil
|
||||||
|
}
|
||||||
|
return 0, 0, fmt.Errorf("expected 'odd' or 'even', but found '%s' instead", id)
|
||||||
|
default:
|
||||||
|
goto invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
positiveA:
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
goto eof
|
||||||
|
}
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
a, err = p.parseInteger()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
goto readA
|
||||||
|
case 'n', 'N':
|
||||||
|
a = 1
|
||||||
|
p.i++
|
||||||
|
goto readN
|
||||||
|
default:
|
||||||
|
goto invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
negativeA:
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
goto eof
|
||||||
|
}
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
a, err = p.parseInteger()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
a = -a
|
||||||
|
goto readA
|
||||||
|
case 'n', 'N':
|
||||||
|
a = -1
|
||||||
|
p.i++
|
||||||
|
goto readN
|
||||||
|
default:
|
||||||
|
goto invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
readA:
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
goto eof
|
||||||
|
}
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case 'n', 'N':
|
||||||
|
p.i++
|
||||||
|
goto readN
|
||||||
|
default:
|
||||||
|
// The number we read as a is actually b.
|
||||||
|
return 0, a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
readN:
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
goto eof
|
||||||
|
}
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '+':
|
||||||
|
p.i++
|
||||||
|
p.skipWhitespace()
|
||||||
|
b, err = p.parseInteger()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return a, b, nil
|
||||||
|
case '-':
|
||||||
|
p.i++
|
||||||
|
p.skipWhitespace()
|
||||||
|
b, err = p.parseInteger()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return a, -b, nil
|
||||||
|
default:
|
||||||
|
return a, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
eof:
|
||||||
|
return 0, 0, errors.New("unexpected EOF while attempting to parse expression of form an+b")
|
||||||
|
|
||||||
|
invalid:
|
||||||
|
return 0, 0, errors.New("unexpected character while attempting to parse expression of form an+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSimpleSelectorSequence parses a selector sequence that applies to
|
||||||
|
// a single element.
|
||||||
|
func (p *parser) parseSimpleSelectorSequence() (Sel, error) {
|
||||||
|
var selectors []Sel
|
||||||
|
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return nil, errors.New("expected selector, found EOF instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '*':
|
||||||
|
// It's the universal selector. Just skip over it, since it doesn't affect the meaning.
|
||||||
|
p.i++
|
||||||
|
case '#', '.', '[', ':':
|
||||||
|
// There's no type selector. Wait to process the other till the main loop.
|
||||||
|
default:
|
||||||
|
r, err := p.parseTypeSelector()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
selectors = append(selectors, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pseudoElement string
|
||||||
|
loop:
|
||||||
|
for p.i < len(p.s) {
|
||||||
|
var (
|
||||||
|
ns Sel
|
||||||
|
newPseudoElement string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '#':
|
||||||
|
ns, err = p.parseIDSelector()
|
||||||
|
case '.':
|
||||||
|
ns, err = p.parseClassSelector()
|
||||||
|
case '[':
|
||||||
|
ns, err = p.parseAttributeSelector()
|
||||||
|
case ':':
|
||||||
|
ns, newPseudoElement, err = p.parsePseudoclassSelector()
|
||||||
|
default:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// From https://drafts.csswg.org/selectors-3/#pseudo-elements :
|
||||||
|
// "Only one pseudo-element may appear per selector, and if present
|
||||||
|
// it must appear after the sequence of simple selectors that
|
||||||
|
// represents the subjects of the selector.""
|
||||||
|
if ns == nil { // we found a pseudo-element
|
||||||
|
if pseudoElement != "" {
|
||||||
|
return nil, fmt.Errorf("only one pseudo-element is accepted per selector, got %s and %s", pseudoElement, newPseudoElement)
|
||||||
|
}
|
||||||
|
if !p.acceptPseudoElements {
|
||||||
|
return nil, fmt.Errorf("pseudo-element %s found, but pseudo-elements support is disabled", newPseudoElement)
|
||||||
|
}
|
||||||
|
pseudoElement = newPseudoElement
|
||||||
|
} else {
|
||||||
|
if pseudoElement != "" {
|
||||||
|
return nil, fmt.Errorf("pseudo-element %s must be at the end of selector", pseudoElement)
|
||||||
|
}
|
||||||
|
selectors = append(selectors, ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if len(selectors) == 1 && pseudoElement == "" { // no need wrap the selectors in compoundSelector
|
||||||
|
return selectors[0], nil
|
||||||
|
}
|
||||||
|
return compoundSelector{selectors: selectors, pseudoElement: pseudoElement}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSelector parses a selector that may include combinators.
|
||||||
|
func (p *parser) parseSelector() (Sel, error) {
|
||||||
|
p.skipWhitespace()
|
||||||
|
result, err := p.parseSimpleSelectorSequence()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
var (
|
||||||
|
combinator byte
|
||||||
|
c Sel
|
||||||
|
)
|
||||||
|
if p.skipWhitespace() {
|
||||||
|
combinator = ' '
|
||||||
|
}
|
||||||
|
if p.i >= len(p.s) {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.s[p.i] {
|
||||||
|
case '+', '>', '~':
|
||||||
|
combinator = p.s[p.i]
|
||||||
|
p.i++
|
||||||
|
p.skipWhitespace()
|
||||||
|
case ',', ')':
|
||||||
|
// These characters can't begin a selector, but they can legally occur after one.
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if combinator == 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = p.parseSimpleSelectorSequence()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = combinedSelector{first: result, combinator: combinator, second: c}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSelectorGroup parses a group of selectors, separated by commas.
|
||||||
|
func (p *parser) parseSelectorGroup() (SelectorGroup, error) {
|
||||||
|
current, err := p.parseSelector()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := SelectorGroup{current}
|
||||||
|
|
||||||
|
for p.i < len(p.s) {
|
||||||
|
if p.s[p.i] != ',' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.i++
|
||||||
|
c, err := p.parseSelector()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, c)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
938
vendor/github.com/andybalholm/cascadia/selector.go
generated
vendored
Normal file
938
vendor/github.com/andybalholm/cascadia/selector.go
generated
vendored
Normal file
@@ -0,0 +1,938 @@
|
|||||||
|
package cascadia
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Matcher is the interface for basic selector functionality.
|
||||||
|
// Match returns whether a selector matches n.
|
||||||
|
type Matcher interface {
|
||||||
|
Match(n *html.Node) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sel is the interface for all the functionality provided by selectors.
|
||||||
|
type Sel interface {
|
||||||
|
Matcher
|
||||||
|
Specificity() Specificity
|
||||||
|
|
||||||
|
// Returns a CSS input compiling to this selector.
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// Returns a pseudo-element, or an empty string.
|
||||||
|
PseudoElement() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses a selector. Use `ParseWithPseudoElement`
|
||||||
|
// if you need support for pseudo-elements.
|
||||||
|
func Parse(sel string) (Sel, error) {
|
||||||
|
p := &parser{s: sel}
|
||||||
|
compiled, err := p.parseSelector()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.i < len(sel) {
|
||||||
|
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return compiled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseWithPseudoElement parses a single selector,
|
||||||
|
// with support for pseudo-element.
|
||||||
|
func ParseWithPseudoElement(sel string) (Sel, error) {
|
||||||
|
p := &parser{s: sel, acceptPseudoElements: true}
|
||||||
|
compiled, err := p.parseSelector()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.i < len(sel) {
|
||||||
|
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return compiled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseGroup parses a selector, or a group of selectors separated by commas.
|
||||||
|
// Use `ParseGroupWithPseudoElements`
|
||||||
|
// if you need support for pseudo-elements.
|
||||||
|
func ParseGroup(sel string) (SelectorGroup, error) {
|
||||||
|
p := &parser{s: sel}
|
||||||
|
compiled, err := p.parseSelectorGroup()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.i < len(sel) {
|
||||||
|
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return compiled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseGroupWithPseudoElements parses a selector, or a group of selectors separated by commas.
|
||||||
|
// It supports pseudo-elements.
|
||||||
|
func ParseGroupWithPseudoElements(sel string) (SelectorGroup, error) {
|
||||||
|
p := &parser{s: sel, acceptPseudoElements: true}
|
||||||
|
compiled, err := p.parseSelectorGroup()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.i < len(sel) {
|
||||||
|
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return compiled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Selector is a function which tells whether a node matches or not.
|
||||||
|
//
|
||||||
|
// This type is maintained for compatibility; I recommend using the newer and
|
||||||
|
// more idiomatic interfaces Sel and Matcher.
|
||||||
|
type Selector func(*html.Node) bool
|
||||||
|
|
||||||
|
// Compile parses a selector and returns, if successful, a Selector object
|
||||||
|
// that can be used to match against html.Node objects.
|
||||||
|
func Compile(sel string) (Selector, error) {
|
||||||
|
compiled, err := ParseGroup(sel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Selector(compiled.Match), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCompile is like Compile, but panics instead of returning an error.
|
||||||
|
func MustCompile(sel string) Selector {
|
||||||
|
compiled, err := Compile(sel)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return compiled
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchAll returns a slice of the nodes that match the selector,
|
||||||
|
// from n and its children.
|
||||||
|
func (s Selector) MatchAll(n *html.Node) []*html.Node {
|
||||||
|
return s.matchAllInto(n, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Selector) matchAllInto(n *html.Node, storage []*html.Node) []*html.Node {
|
||||||
|
if s(n) {
|
||||||
|
storage = append(storage, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
for child := n.FirstChild; child != nil; child = child.NextSibling {
|
||||||
|
storage = s.matchAllInto(child, storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return storage
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryInto(n *html.Node, m Matcher, storage []*html.Node) []*html.Node {
|
||||||
|
for child := n.FirstChild; child != nil; child = child.NextSibling {
|
||||||
|
if m.Match(child) {
|
||||||
|
storage = append(storage, child)
|
||||||
|
}
|
||||||
|
storage = queryInto(child, m, storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return storage
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryAll returns a slice of all the nodes that match m, from the descendants
|
||||||
|
// of n.
|
||||||
|
func QueryAll(n *html.Node, m Matcher) []*html.Node {
|
||||||
|
return queryInto(n, m, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the node matches the selector.
|
||||||
|
func (s Selector) Match(n *html.Node) bool {
|
||||||
|
return s(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchFirst returns the first node that matches s, from n and its children.
|
||||||
|
func (s Selector) MatchFirst(n *html.Node) *html.Node {
|
||||||
|
if s.Match(n) {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
m := s.MatchFirst(c)
|
||||||
|
if m != nil {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns the first node that matches m, from the descendants of n.
|
||||||
|
// If none matches, it returns nil.
|
||||||
|
func Query(n *html.Node, m Matcher) *html.Node {
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if m.Match(c) {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
if matched := Query(c, m); matched != nil {
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns the nodes in nodes that match the selector.
|
||||||
|
func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) {
|
||||||
|
for _, n := range nodes {
|
||||||
|
if s(n) {
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns the nodes that match m.
|
||||||
|
func Filter(nodes []*html.Node, m Matcher) (result []*html.Node) {
|
||||||
|
for _, n := range nodes {
|
||||||
|
if m.Match(n) {
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagSelector struct {
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches elements with a given tag name.
|
||||||
|
func (t tagSelector) Match(n *html.Node) bool {
|
||||||
|
return n.Type == html.ElementNode && n.Data == t.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c tagSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 0, 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c tagSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type classSelector struct {
|
||||||
|
class string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches elements by class attribute.
|
||||||
|
func (t classSelector) Match(n *html.Node) bool {
|
||||||
|
return matchAttribute(n, "class", func(s string) bool {
|
||||||
|
return matchInclude(t.class, s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c classSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c classSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type idSelector struct {
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches elements by id attribute.
|
||||||
|
func (t idSelector) Match(n *html.Node) bool {
|
||||||
|
return matchAttribute(n, "id", func(s string) bool {
|
||||||
|
return s == t.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c idSelector) Specificity() Specificity {
|
||||||
|
return Specificity{1, 0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c idSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type attrSelector struct {
|
||||||
|
key, val, operation string
|
||||||
|
regexp *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches elements by attribute value.
|
||||||
|
func (t attrSelector) Match(n *html.Node) bool {
|
||||||
|
switch t.operation {
|
||||||
|
case "":
|
||||||
|
return matchAttribute(n, t.key, func(string) bool { return true })
|
||||||
|
case "=":
|
||||||
|
return matchAttribute(n, t.key, func(s string) bool { return s == t.val })
|
||||||
|
case "!=":
|
||||||
|
return attributeNotEqualMatch(t.key, t.val, n)
|
||||||
|
case "~=":
|
||||||
|
// matches elements where the attribute named key is a whitespace-separated list that includes val.
|
||||||
|
return matchAttribute(n, t.key, func(s string) bool { return matchInclude(t.val, s) })
|
||||||
|
case "|=":
|
||||||
|
return attributeDashMatch(t.key, t.val, n)
|
||||||
|
case "^=":
|
||||||
|
return attributePrefixMatch(t.key, t.val, n)
|
||||||
|
case "$=":
|
||||||
|
return attributeSuffixMatch(t.key, t.val, n)
|
||||||
|
case "*=":
|
||||||
|
return attributeSubstringMatch(t.key, t.val, n)
|
||||||
|
case "#=":
|
||||||
|
return attributeRegexMatch(t.key, t.regexp, n)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsuported operation : %s", t.operation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches elements where the attribute named key satisifes the function f.
|
||||||
|
func matchAttribute(n *html.Node, key string, f func(string) bool) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, a := range n.Attr {
|
||||||
|
if a.Key == key && f(a.Val) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributeNotEqualMatch matches elements where
|
||||||
|
// the attribute named key does not have the value val.
|
||||||
|
func attributeNotEqualMatch(key, val string, n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, a := range n.Attr {
|
||||||
|
if a.Key == key && a.Val == val {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if s is a whitespace-separated list that includes val.
|
||||||
|
func matchInclude(val, s string) bool {
|
||||||
|
for s != "" {
|
||||||
|
i := strings.IndexAny(s, " \t\r\n\f")
|
||||||
|
if i == -1 {
|
||||||
|
return s == val
|
||||||
|
}
|
||||||
|
if s[:i] == val {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s = s[i+1:]
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches elements where the attribute named key equals val or starts with val plus a hyphen.
|
||||||
|
func attributeDashMatch(key, val string, n *html.Node) bool {
|
||||||
|
return matchAttribute(n, key,
|
||||||
|
func(s string) bool {
|
||||||
|
if s == val {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(s) <= len(val) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[:len(val)] == val && s[len(val)] == '-' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributePrefixMatch returns a Selector that matches elements where
|
||||||
|
// the attribute named key starts with val.
|
||||||
|
func attributePrefixMatch(key, val string, n *html.Node) bool {
|
||||||
|
return matchAttribute(n, key,
|
||||||
|
func(s string) bool {
|
||||||
|
if strings.TrimSpace(s) == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.HasPrefix(s, val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributeSuffixMatch matches elements where
|
||||||
|
// the attribute named key ends with val.
|
||||||
|
func attributeSuffixMatch(key, val string, n *html.Node) bool {
|
||||||
|
return matchAttribute(n, key,
|
||||||
|
func(s string) bool {
|
||||||
|
if strings.TrimSpace(s) == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.HasSuffix(s, val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributeSubstringMatch matches nodes where
|
||||||
|
// the attribute named key contains val.
|
||||||
|
func attributeSubstringMatch(key, val string, n *html.Node) bool {
|
||||||
|
return matchAttribute(n, key,
|
||||||
|
func(s string) bool {
|
||||||
|
if strings.TrimSpace(s) == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.Contains(s, val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributeRegexMatch matches nodes where
|
||||||
|
// the attribute named key matches the regular expression rx
|
||||||
|
func attributeRegexMatch(key string, rx *regexp.Regexp, n *html.Node) bool {
|
||||||
|
return matchAttribute(n, key,
|
||||||
|
func(s string) bool {
|
||||||
|
return rx.MatchString(s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c attrSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c attrSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- Pseudo class selectors ----------------
|
||||||
|
// we use severals concrete types of pseudo-class selectors
|
||||||
|
|
||||||
|
type relativePseudoClassSelector struct {
|
||||||
|
name string // one of "not", "has", "haschild"
|
||||||
|
match SelectorGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s relativePseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch s.name {
|
||||||
|
case "not":
|
||||||
|
// matches elements that do not match a.
|
||||||
|
return !s.match.Match(n)
|
||||||
|
case "has":
|
||||||
|
// matches elements with any descendant that matches a.
|
||||||
|
return hasDescendantMatch(n, s.match)
|
||||||
|
case "haschild":
|
||||||
|
// matches elements with a child that matches a.
|
||||||
|
return hasChildMatch(n, s.match)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported relative pseudo class selector : %s", s.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasChildMatch returns whether n has any child that matches a.
|
||||||
|
func hasChildMatch(n *html.Node, a Matcher) bool {
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if a.Match(c) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasDescendantMatch performs a depth-first search of n's descendants,
|
||||||
|
// testing whether any of them match a. It returns true as soon as a match is
|
||||||
|
// found, or false if no match is found.
|
||||||
|
func hasDescendantMatch(n *html.Node, a Matcher) bool {
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if a.Match(c) || (c.Type == html.ElementNode && hasDescendantMatch(c, a)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specificity returns the specificity of the most specific selectors
|
||||||
|
// in the pseudo-class arguments.
|
||||||
|
// See https://www.w3.org/TR/selectors/#specificity-rules
|
||||||
|
func (s relativePseudoClassSelector) Specificity() Specificity {
|
||||||
|
var max Specificity
|
||||||
|
for _, sel := range s.match {
|
||||||
|
newSpe := sel.Specificity()
|
||||||
|
if max.Less(newSpe) {
|
||||||
|
max = newSpe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c relativePseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type containsPseudoClassSelector struct {
|
||||||
|
own bool
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s containsPseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
var text string
|
||||||
|
if s.own {
|
||||||
|
// matches nodes that directly contain the given text
|
||||||
|
text = strings.ToLower(nodeOwnText(n))
|
||||||
|
} else {
|
||||||
|
// matches nodes that contain the given text.
|
||||||
|
text = strings.ToLower(nodeText(n))
|
||||||
|
}
|
||||||
|
return strings.Contains(text, s.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s containsPseudoClassSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c containsPseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type regexpPseudoClassSelector struct {
|
||||||
|
own bool
|
||||||
|
regexp *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s regexpPseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
var text string
|
||||||
|
if s.own {
|
||||||
|
// matches nodes whose text directly matches the specified regular expression
|
||||||
|
text = nodeOwnText(n)
|
||||||
|
} else {
|
||||||
|
// matches nodes whose text matches the specified regular expression
|
||||||
|
text = nodeText(n)
|
||||||
|
}
|
||||||
|
return s.regexp.MatchString(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeNodeText writes the text contained in n and its descendants to b.
|
||||||
|
func writeNodeText(n *html.Node, b *bytes.Buffer) {
|
||||||
|
switch n.Type {
|
||||||
|
case html.TextNode:
|
||||||
|
b.WriteString(n.Data)
|
||||||
|
case html.ElementNode:
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
writeNodeText(c, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nodeText returns the text contained in n and its descendants.
|
||||||
|
func nodeText(n *html.Node) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
writeNodeText(n, &b)
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// nodeOwnText returns the contents of the text nodes that are direct
|
||||||
|
// children of n.
|
||||||
|
func nodeOwnText(n *html.Node) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if c.Type == html.TextNode {
|
||||||
|
b.WriteString(c.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s regexpPseudoClassSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c regexpPseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type nthPseudoClassSelector struct {
|
||||||
|
a, b int
|
||||||
|
last, ofType bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s nthPseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
if s.a == 0 {
|
||||||
|
if s.last {
|
||||||
|
return simpleNthLastChildMatch(s.b, s.ofType, n)
|
||||||
|
} else {
|
||||||
|
return simpleNthChildMatch(s.b, s.ofType, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nthChildMatch(s.a, s.b, s.last, s.ofType, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nthChildMatch implements :nth-child(an+b).
|
||||||
|
// If last is true, implements :nth-last-child instead.
|
||||||
|
// If ofType is true, implements :nth-of-type instead.
|
||||||
|
func nthChildMatch(a, b int, last, ofType bool, n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := n.Parent
|
||||||
|
if parent == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent.Type == html.DocumentNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
i := -1
|
||||||
|
count := 0
|
||||||
|
for c := parent.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
if c == n {
|
||||||
|
i = count
|
||||||
|
if !last {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == -1 {
|
||||||
|
// This shouldn't happen, since n should always be one of its parent's children.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if last {
|
||||||
|
i = count - i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
i -= b
|
||||||
|
if a == 0 {
|
||||||
|
return i == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return i%a == 0 && i/a >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// simpleNthChildMatch implements :nth-child(b).
|
||||||
|
// If ofType is true, implements :nth-of-type instead.
|
||||||
|
func simpleNthChildMatch(b int, ofType bool, n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := n.Parent
|
||||||
|
if parent == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent.Type == html.DocumentNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for c := parent.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if c.Type != html.ElementNode || (ofType && c.Data != n.Data) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
if c == n {
|
||||||
|
return count == b
|
||||||
|
}
|
||||||
|
if count >= b {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// simpleNthLastChildMatch implements :nth-last-child(b).
|
||||||
|
// If ofType is true, implements :nth-last-of-type instead.
|
||||||
|
func simpleNthLastChildMatch(b int, ofType bool, n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := n.Parent
|
||||||
|
if parent == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent.Type == html.DocumentNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for c := parent.LastChild; c != nil; c = c.PrevSibling {
|
||||||
|
if c.Type != html.ElementNode || (ofType && c.Data != n.Data) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
if c == n {
|
||||||
|
return count == b
|
||||||
|
}
|
||||||
|
if count >= b {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specificity for nth-child pseudo-class.
|
||||||
|
// Does not support a list of selectors
|
||||||
|
func (s nthPseudoClassSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c nthPseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type onlyChildPseudoClassSelector struct {
|
||||||
|
ofType bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements :only-child.
|
||||||
|
// If `ofType` is true, it implements :only-of-type instead.
|
||||||
|
func (s onlyChildPseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := n.Parent
|
||||||
|
if parent == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent.Type == html.DocumentNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for c := parent.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if (c.Type != html.ElementNode) || (s.ofType && c.Data != n.Data) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
if count > 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s onlyChildPseudoClassSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c onlyChildPseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type inputPseudoClassSelector struct{}
|
||||||
|
|
||||||
|
// Matches input, select, textarea and button elements.
|
||||||
|
func (s inputPseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
return n.Type == html.ElementNode && (n.Data == "input" || n.Data == "select" || n.Data == "textarea" || n.Data == "button")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s inputPseudoClassSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c inputPseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type emptyElementPseudoClassSelector struct{}
|
||||||
|
|
||||||
|
// Matches empty elements.
|
||||||
|
func (s emptyElementPseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
switch c.Type {
|
||||||
|
case html.ElementNode, html.TextNode:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s emptyElementPseudoClassSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c emptyElementPseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type rootPseudoClassSelector struct{}
|
||||||
|
|
||||||
|
// Match implements :root
|
||||||
|
func (s rootPseudoClassSelector) Match(n *html.Node) bool {
|
||||||
|
if n.Type != html.ElementNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n.Parent == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n.Parent.Type == html.DocumentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s rootPseudoClassSelector) Specificity() Specificity {
|
||||||
|
return Specificity{0, 1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c rootPseudoClassSelector) PseudoElement() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type compoundSelector struct {
|
||||||
|
selectors []Sel
|
||||||
|
pseudoElement string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches elements if each sub-selectors matches.
|
||||||
|
func (t compoundSelector) Match(n *html.Node) bool {
|
||||||
|
if len(t.selectors) == 0 {
|
||||||
|
return n.Type == html.ElementNode
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sel := range t.selectors {
|
||||||
|
if !sel.Match(n) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s compoundSelector) Specificity() Specificity {
|
||||||
|
var out Specificity
|
||||||
|
for _, sel := range s.selectors {
|
||||||
|
out = out.Add(sel.Specificity())
|
||||||
|
}
|
||||||
|
if s.pseudoElement != "" {
|
||||||
|
// https://drafts.csswg.org/selectors-3/#specificity
|
||||||
|
out = out.Add(Specificity{0, 0, 1})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c compoundSelector) PseudoElement() string {
|
||||||
|
return c.pseudoElement
|
||||||
|
}
|
||||||
|
|
||||||
|
type combinedSelector struct {
|
||||||
|
first Sel
|
||||||
|
combinator byte
|
||||||
|
second Sel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t combinedSelector) Match(n *html.Node) bool {
|
||||||
|
if t.first == nil {
|
||||||
|
return false // maybe we should panic
|
||||||
|
}
|
||||||
|
switch t.combinator {
|
||||||
|
case 0:
|
||||||
|
return t.first.Match(n)
|
||||||
|
case ' ':
|
||||||
|
return descendantMatch(t.first, t.second, n)
|
||||||
|
case '>':
|
||||||
|
return childMatch(t.first, t.second, n)
|
||||||
|
case '+':
|
||||||
|
return siblingMatch(t.first, t.second, true, n)
|
||||||
|
case '~':
|
||||||
|
return siblingMatch(t.first, t.second, false, n)
|
||||||
|
default:
|
||||||
|
panic("unknown combinator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches an element if it matches d and has an ancestor that matches a.
|
||||||
|
func descendantMatch(a, d Matcher, n *html.Node) bool {
|
||||||
|
if !d.Match(n) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for p := n.Parent; p != nil; p = p.Parent {
|
||||||
|
if a.Match(p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches an element if it matches d and its parent matches a.
|
||||||
|
func childMatch(a, d Matcher, n *html.Node) bool {
|
||||||
|
return d.Match(n) && n.Parent != nil && a.Match(n.Parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches an element if it matches s2 and is preceded by an element that matches s1.
|
||||||
|
// If adjacent is true, the sibling must be immediately before the element.
|
||||||
|
func siblingMatch(s1, s2 Matcher, adjacent bool, n *html.Node) bool {
|
||||||
|
if !s2.Match(n) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjacent {
|
||||||
|
for n = n.PrevSibling; n != nil; n = n.PrevSibling {
|
||||||
|
if n.Type == html.TextNode || n.Type == html.CommentNode {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return s1.Match(n)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk backwards looking for element that matches s1
|
||||||
|
for c := n.PrevSibling; c != nil; c = c.PrevSibling {
|
||||||
|
if s1.Match(c) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s combinedSelector) Specificity() Specificity {
|
||||||
|
spec := s.first.Specificity()
|
||||||
|
if s.second != nil {
|
||||||
|
spec = spec.Add(s.second.Specificity())
|
||||||
|
}
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
|
// on combinedSelector, a pseudo-element only makes sens on the last
|
||||||
|
// selector, although others increase specificity.
|
||||||
|
func (c combinedSelector) PseudoElement() string {
|
||||||
|
if c.second == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return c.second.PseudoElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SelectorGroup is a list of selectors, which matches if any of the
|
||||||
|
// individual selectors matches.
|
||||||
|
type SelectorGroup []Sel
|
||||||
|
|
||||||
|
// Match returns true if the node matches one of the single selectors.
|
||||||
|
func (s SelectorGroup) Match(n *html.Node) bool {
|
||||||
|
for _, sel := range s {
|
||||||
|
if sel.Match(n) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
120
vendor/github.com/andybalholm/cascadia/serialize.go
generated
vendored
Normal file
120
vendor/github.com/andybalholm/cascadia/serialize.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package cascadia
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// implements the reverse operation Sel -> string
|
||||||
|
|
||||||
|
func (c tagSelector) String() string {
|
||||||
|
return c.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c idSelector) String() string {
|
||||||
|
return "#" + c.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c classSelector) String() string {
|
||||||
|
return "." + c.class
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c attrSelector) String() string {
|
||||||
|
val := c.val
|
||||||
|
if c.operation == "#=" {
|
||||||
|
val = c.regexp.String()
|
||||||
|
} else if c.operation != "" {
|
||||||
|
val = fmt.Sprintf(`"%s"`, val)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(`[%s%s%s]`, c.key, c.operation, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c relativePseudoClassSelector) String() string {
|
||||||
|
return fmt.Sprintf(":%s(%s)", c.name, c.match.String())
|
||||||
|
}
|
||||||
|
func (c containsPseudoClassSelector) String() string {
|
||||||
|
s := "contains"
|
||||||
|
if c.own {
|
||||||
|
s += "Own"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(`:%s("%s")`, s, c.value)
|
||||||
|
}
|
||||||
|
func (c regexpPseudoClassSelector) String() string {
|
||||||
|
s := "matches"
|
||||||
|
if c.own {
|
||||||
|
s += "Own"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(":%s(%s)", s, c.regexp.String())
|
||||||
|
}
|
||||||
|
func (c nthPseudoClassSelector) String() string {
|
||||||
|
if c.a == 0 && c.b == 1 { // special cases
|
||||||
|
s := ":first-"
|
||||||
|
if c.last {
|
||||||
|
s = ":last-"
|
||||||
|
}
|
||||||
|
if c.ofType {
|
||||||
|
s += "of-type"
|
||||||
|
} else {
|
||||||
|
s += "child"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
var name string
|
||||||
|
switch [2]bool{c.last, c.ofType} {
|
||||||
|
case [2]bool{true, true}:
|
||||||
|
name = "nth-last-of-type"
|
||||||
|
case [2]bool{true, false}:
|
||||||
|
name = "nth-last-child"
|
||||||
|
case [2]bool{false, true}:
|
||||||
|
name = "nth-of-type"
|
||||||
|
case [2]bool{false, false}:
|
||||||
|
name = "nth-child"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(":%s(%dn+%d)", name, c.a, c.b)
|
||||||
|
}
|
||||||
|
func (c onlyChildPseudoClassSelector) String() string {
|
||||||
|
if c.ofType {
|
||||||
|
return ":only-of-type"
|
||||||
|
}
|
||||||
|
return ":only-child"
|
||||||
|
}
|
||||||
|
func (c inputPseudoClassSelector) String() string {
|
||||||
|
return ":input"
|
||||||
|
}
|
||||||
|
func (c emptyElementPseudoClassSelector) String() string {
|
||||||
|
return ":empty"
|
||||||
|
}
|
||||||
|
func (c rootPseudoClassSelector) String() string {
|
||||||
|
return ":root"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c compoundSelector) String() string {
|
||||||
|
if len(c.selectors) == 0 && c.pseudoElement == "" {
|
||||||
|
return "*"
|
||||||
|
}
|
||||||
|
chunks := make([]string, len(c.selectors))
|
||||||
|
for i, sel := range c.selectors {
|
||||||
|
chunks[i] = sel.String()
|
||||||
|
}
|
||||||
|
s := strings.Join(chunks, "")
|
||||||
|
if c.pseudoElement != "" {
|
||||||
|
s += "::" + c.pseudoElement
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c combinedSelector) String() string {
|
||||||
|
start := c.first.String()
|
||||||
|
if c.second != nil {
|
||||||
|
start += fmt.Sprintf(" %s %s", string(c.combinator), c.second.String())
|
||||||
|
}
|
||||||
|
return start
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SelectorGroup) String() string {
|
||||||
|
ck := make([]string, len(c))
|
||||||
|
for i, s := range c {
|
||||||
|
ck[i] = s.String()
|
||||||
|
}
|
||||||
|
return strings.Join(ck, ", ")
|
||||||
|
}
|
||||||
26
vendor/github.com/andybalholm/cascadia/specificity.go
generated
vendored
Normal file
26
vendor/github.com/andybalholm/cascadia/specificity.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package cascadia
|
||||||
|
|
||||||
|
// Specificity is the CSS specificity as defined in
|
||||||
|
// https://www.w3.org/TR/selectors/#specificity-rules
|
||||||
|
// with the convention Specificity = [A,B,C].
|
||||||
|
type Specificity [3]int
|
||||||
|
|
||||||
|
// returns `true` if s < other (strictly), false otherwise
|
||||||
|
func (s Specificity) Less(other Specificity) bool {
|
||||||
|
for i := range s {
|
||||||
|
if s[i] < other[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s[i] > other[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Specificity) Add(other Specificity) Specificity {
|
||||||
|
for i, sp := range other {
|
||||||
|
s[i] += sp
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
202
vendor/github.com/aws/aws-sdk-go/LICENSE.txt
generated
vendored
Executable file
202
vendor/github.com/aws/aws-sdk-go/LICENSE.txt
generated
vendored
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
3
vendor/github.com/aws/aws-sdk-go/NOTICE.txt
generated
vendored
Executable file
3
vendor/github.com/aws/aws-sdk-go/NOTICE.txt
generated
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
AWS SDK for Go
|
||||||
|
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
Copyright 2014-2015 Stripe, Inc.
|
||||||
145
vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
Executable file
145
vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
Executable file
@@ -0,0 +1,145 @@
|
|||||||
|
// Package awserr represents API error interface accessors for the SDK.
|
||||||
|
package awserr
|
||||||
|
|
||||||
|
// An Error wraps lower level errors with code, message and an original error.
|
||||||
|
// The underlying concrete error type may also satisfy other interfaces which
|
||||||
|
// can be to used to obtain more specific information about the error.
|
||||||
|
//
|
||||||
|
// Calling Error() or String() will always include the full information about
|
||||||
|
// an error based on its underlying type.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// output, err := s3manage.Upload(svc, input, opts)
|
||||||
|
// if err != nil {
|
||||||
|
// if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
// // Get error details
|
||||||
|
// log.Println("Error:", awsErr.Code(), awsErr.Message())
|
||||||
|
//
|
||||||
|
// // Prints out full error message, including original error if there was one.
|
||||||
|
// log.Println("Error:", awsErr.Error())
|
||||||
|
//
|
||||||
|
// // Get original error
|
||||||
|
// if origErr := awsErr.OrigErr(); origErr != nil {
|
||||||
|
// // operate on original error.
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// fmt.Println(err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type Error interface {
|
||||||
|
// Satisfy the generic error interface.
|
||||||
|
error
|
||||||
|
|
||||||
|
// Returns the short phrase depicting the classification of the error.
|
||||||
|
Code() string
|
||||||
|
|
||||||
|
// Returns the error details message.
|
||||||
|
Message() string
|
||||||
|
|
||||||
|
// Returns the original error if one was set. Nil is returned if not set.
|
||||||
|
OrigErr() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchError is a batch of errors which also wraps lower level errors with
|
||||||
|
// code, message, and original errors. Calling Error() will include all errors
|
||||||
|
// that occurred in the batch.
|
||||||
|
//
|
||||||
|
// Deprecated: Replaced with BatchedErrors. Only defined for backwards
|
||||||
|
// compatibility.
|
||||||
|
type BatchError interface {
|
||||||
|
// Satisfy the generic error interface.
|
||||||
|
error
|
||||||
|
|
||||||
|
// Returns the short phrase depicting the classification of the error.
|
||||||
|
Code() string
|
||||||
|
|
||||||
|
// Returns the error details message.
|
||||||
|
Message() string
|
||||||
|
|
||||||
|
// Returns the original error if one was set. Nil is returned if not set.
|
||||||
|
OrigErrs() []error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchedErrors is a batch of errors which also wraps lower level errors with
|
||||||
|
// code, message, and original errors. Calling Error() will include all errors
|
||||||
|
// that occurred in the batch.
|
||||||
|
//
|
||||||
|
// Replaces BatchError
|
||||||
|
type BatchedErrors interface {
|
||||||
|
// Satisfy the base Error interface.
|
||||||
|
Error
|
||||||
|
|
||||||
|
// Returns the original error if one was set. Nil is returned if not set.
|
||||||
|
OrigErrs() []error
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns an Error object described by the code, message, and origErr.
|
||||||
|
//
|
||||||
|
// If origErr satisfies the Error interface it will not be wrapped within a new
|
||||||
|
// Error object and will instead be returned.
|
||||||
|
func New(code, message string, origErr error) Error {
|
||||||
|
var errs []error
|
||||||
|
if origErr != nil {
|
||||||
|
errs = append(errs, origErr)
|
||||||
|
}
|
||||||
|
return newBaseError(code, message, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatchError returns an BatchedErrors with a collection of errors as an
|
||||||
|
// array of errors.
|
||||||
|
func NewBatchError(code, message string, errs []error) BatchedErrors {
|
||||||
|
return newBaseError(code, message, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A RequestFailure is an interface to extract request failure information from
|
||||||
|
// an Error such as the request ID of the failed request returned by a service.
|
||||||
|
// RequestFailures may not always have a requestID value if the request failed
|
||||||
|
// prior to reaching the service such as a connection error.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// output, err := s3manage.Upload(svc, input, opts)
|
||||||
|
// if err != nil {
|
||||||
|
// if reqerr, ok := err.(RequestFailure); ok {
|
||||||
|
// log.Println("Request failed", reqerr.Code(), reqerr.Message(), reqerr.RequestID())
|
||||||
|
// } else {
|
||||||
|
// log.Println("Error:", err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Combined with awserr.Error:
|
||||||
|
//
|
||||||
|
// output, err := s3manage.Upload(svc, input, opts)
|
||||||
|
// if err != nil {
|
||||||
|
// if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
// // Generic AWS Error with Code, Message, and original error (if any)
|
||||||
|
// fmt.Println(awsErr.Code(), awsErr.Message(), awsErr.OrigErr())
|
||||||
|
//
|
||||||
|
// if reqErr, ok := err.(awserr.RequestFailure); ok {
|
||||||
|
// // A service error occurred
|
||||||
|
// fmt.Println(reqErr.StatusCode(), reqErr.RequestID())
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// fmt.Println(err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type RequestFailure interface {
|
||||||
|
Error
|
||||||
|
|
||||||
|
// The status code of the HTTP response.
|
||||||
|
StatusCode() int
|
||||||
|
|
||||||
|
// The request ID returned by the service for a request failure. This will
|
||||||
|
// be empty if no request ID is available such as the request failed due
|
||||||
|
// to a connection error.
|
||||||
|
RequestID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequestFailure returns a new request error wrapper for the given Error
|
||||||
|
// provided.
|
||||||
|
func NewRequestFailure(err Error, statusCode int, reqID string) RequestFailure {
|
||||||
|
return newRequestError(err, statusCode, reqID)
|
||||||
|
}
|
||||||
194
vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
Executable file
194
vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
Executable file
@@ -0,0 +1,194 @@
|
|||||||
|
package awserr
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// SprintError returns a string of the formatted error code.
|
||||||
|
//
|
||||||
|
// Both extra and origErr are optional. If they are included their lines
|
||||||
|
// will be added, but if they are not included their lines will be ignored.
|
||||||
|
func SprintError(code, message, extra string, origErr error) string {
|
||||||
|
msg := fmt.Sprintf("%s: %s", code, message)
|
||||||
|
if extra != "" {
|
||||||
|
msg = fmt.Sprintf("%s\n\t%s", msg, extra)
|
||||||
|
}
|
||||||
|
if origErr != nil {
|
||||||
|
msg = fmt.Sprintf("%s\ncaused by: %s", msg, origErr.Error())
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// A baseError wraps the code and message which defines an error. It also
|
||||||
|
// can be used to wrap an original error object.
|
||||||
|
//
|
||||||
|
// Should be used as the root for errors satisfying the awserr.Error. Also
|
||||||
|
// for any error which does not fit into a specific error wrapper type.
|
||||||
|
type baseError struct {
|
||||||
|
// Classification of error
|
||||||
|
code string
|
||||||
|
|
||||||
|
// Detailed information about error
|
||||||
|
message string
|
||||||
|
|
||||||
|
// Optional original error this error is based off of. Allows building
|
||||||
|
// chained errors.
|
||||||
|
errs []error
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBaseError returns an error object for the code, message, and errors.
|
||||||
|
//
|
||||||
|
// code is a short no whitespace phrase depicting the classification of
|
||||||
|
// the error that is being created.
|
||||||
|
//
|
||||||
|
// message is the free flow string containing detailed information about the
|
||||||
|
// error.
|
||||||
|
//
|
||||||
|
// origErrs is the error objects which will be nested under the new errors to
|
||||||
|
// be returned.
|
||||||
|
func newBaseError(code, message string, origErrs []error) *baseError {
|
||||||
|
b := &baseError{
|
||||||
|
code: code,
|
||||||
|
message: message,
|
||||||
|
errs: origErrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
//
|
||||||
|
// See ErrorWithExtra for formatting.
|
||||||
|
//
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (b baseError) Error() string {
|
||||||
|
size := len(b.errs)
|
||||||
|
if size > 0 {
|
||||||
|
return SprintError(b.code, b.message, "", errorList(b.errs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return SprintError(b.code, b.message, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
// Alias for Error to satisfy the stringer interface.
|
||||||
|
func (b baseError) String() string {
|
||||||
|
return b.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code returns the short phrase depicting the classification of the error.
|
||||||
|
func (b baseError) Code() string {
|
||||||
|
return b.code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message returns the error details message.
|
||||||
|
func (b baseError) Message() string {
|
||||||
|
return b.message
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrigErr returns the original error if one was set. Nil is returned if no
|
||||||
|
// error was set. This only returns the first element in the list. If the full
|
||||||
|
// list is needed, use BatchedErrors.
|
||||||
|
func (b baseError) OrigErr() error {
|
||||||
|
switch len(b.errs) {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
return b.errs[0]
|
||||||
|
default:
|
||||||
|
if err, ok := b.errs[0].(Error); ok {
|
||||||
|
return NewBatchError(err.Code(), err.Message(), b.errs[1:])
|
||||||
|
}
|
||||||
|
return NewBatchError("BatchedErrors",
|
||||||
|
"multiple errors occurred", b.errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrigErrs returns the original errors if one was set. An empty slice is
|
||||||
|
// returned if no error was set.
|
||||||
|
func (b baseError) OrigErrs() []error {
|
||||||
|
return b.errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// So that the Error interface type can be included as an anonymous field
|
||||||
|
// in the requestError struct and not conflict with the error.Error() method.
|
||||||
|
type awsError Error
|
||||||
|
|
||||||
|
// A requestError wraps a request or service error.
|
||||||
|
//
|
||||||
|
// Composed of baseError for code, message, and original error.
|
||||||
|
type requestError struct {
|
||||||
|
awsError
|
||||||
|
statusCode int
|
||||||
|
requestID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRequestError returns a wrapped error with additional information for
|
||||||
|
// request status code, and service requestID.
|
||||||
|
//
|
||||||
|
// Should be used to wrap all request which involve service requests. Even if
|
||||||
|
// the request failed without a service response, but had an HTTP status code
|
||||||
|
// that may be meaningful.
|
||||||
|
//
|
||||||
|
// Also wraps original errors via the baseError.
|
||||||
|
func newRequestError(err Error, statusCode int, requestID string) *requestError {
|
||||||
|
return &requestError{
|
||||||
|
awsError: err,
|
||||||
|
statusCode: statusCode,
|
||||||
|
requestID: requestID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (r requestError) Error() string {
|
||||||
|
extra := fmt.Sprintf("status code: %d, request id: %s",
|
||||||
|
r.statusCode, r.requestID)
|
||||||
|
return SprintError(r.Code(), r.Message(), extra, r.OrigErr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
// Alias for Error to satisfy the stringer interface.
|
||||||
|
func (r requestError) String() string {
|
||||||
|
return r.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusCode returns the wrapped status code for the error
|
||||||
|
func (r requestError) StatusCode() int {
|
||||||
|
return r.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestID returns the wrapped requestID
|
||||||
|
func (r requestError) RequestID() string {
|
||||||
|
return r.requestID
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrigErrs returns the original errors if one was set. An empty slice is
|
||||||
|
// returned if no error was set.
|
||||||
|
func (r requestError) OrigErrs() []error {
|
||||||
|
if b, ok := r.awsError.(BatchedErrors); ok {
|
||||||
|
return b.OrigErrs()
|
||||||
|
}
|
||||||
|
return []error{r.OrigErr()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An error list that satisfies the golang interface
|
||||||
|
type errorList []error
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
//
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (e errorList) Error() string {
|
||||||
|
msg := ""
|
||||||
|
// How do we want to handle the array size being zero
|
||||||
|
if size := len(e); size > 0 {
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
msg += fmt.Sprintf("%s", e[i].Error())
|
||||||
|
// We check the next index to see if it is within the slice.
|
||||||
|
// If it is, then we append a newline. We do this, because unit tests
|
||||||
|
// could be broken with the additional '\n'
|
||||||
|
if i+1 < size {
|
||||||
|
msg += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
108
vendor/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
Executable file
108
vendor/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copy deeply copies a src structure to dst. Useful for copying request and
|
||||||
|
// response structures.
|
||||||
|
//
|
||||||
|
// Can copy between structs of different type, but will only copy fields which
|
||||||
|
// are assignable, and exist in both structs. Fields which are not assignable,
|
||||||
|
// or do not exist in both structs are ignored.
|
||||||
|
func Copy(dst, src interface{}) {
|
||||||
|
dstval := reflect.ValueOf(dst)
|
||||||
|
if !dstval.IsValid() {
|
||||||
|
panic("Copy dst cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
rcopy(dstval, reflect.ValueOf(src), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyOf returns a copy of src while also allocating the memory for dst.
|
||||||
|
// src must be a pointer type or this operation will fail.
|
||||||
|
func CopyOf(src interface{}) (dst interface{}) {
|
||||||
|
dsti := reflect.New(reflect.TypeOf(src).Elem())
|
||||||
|
dst = dsti.Interface()
|
||||||
|
rcopy(dsti, reflect.ValueOf(src), true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rcopy performs a recursive copy of values from the source to destination.
|
||||||
|
//
|
||||||
|
// root is used to skip certain aspects of the copy which are not valid
|
||||||
|
// for the root node of a object.
|
||||||
|
func rcopy(dst, src reflect.Value, root bool) {
|
||||||
|
if !src.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch src.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if _, ok := src.Interface().(io.Reader); ok {
|
||||||
|
if dst.Kind() == reflect.Ptr && dst.Elem().CanSet() {
|
||||||
|
dst.Elem().Set(src)
|
||||||
|
} else if dst.CanSet() {
|
||||||
|
dst.Set(src)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e := src.Type().Elem()
|
||||||
|
if dst.CanSet() && !src.IsNil() {
|
||||||
|
if _, ok := src.Interface().(*time.Time); !ok {
|
||||||
|
dst.Set(reflect.New(e))
|
||||||
|
} else {
|
||||||
|
tempValue := reflect.New(e)
|
||||||
|
tempValue.Elem().Set(src.Elem())
|
||||||
|
// Sets time.Time's unexported values
|
||||||
|
dst.Set(tempValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if src.Elem().IsValid() {
|
||||||
|
// Keep the current root state since the depth hasn't changed
|
||||||
|
rcopy(dst.Elem(), src.Elem(), root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
t := dst.Type()
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
name := t.Field(i).Name
|
||||||
|
srcVal := src.FieldByName(name)
|
||||||
|
dstVal := dst.FieldByName(name)
|
||||||
|
if srcVal.IsValid() && dstVal.CanSet() {
|
||||||
|
rcopy(dstVal, srcVal, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if src.IsNil() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
dst.Set(s)
|
||||||
|
for i := 0; i < src.Len(); i++ {
|
||||||
|
rcopy(dst.Index(i), src.Index(i), false)
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if src.IsNil() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s := reflect.MakeMap(src.Type())
|
||||||
|
dst.Set(s)
|
||||||
|
for _, k := range src.MapKeys() {
|
||||||
|
v := src.MapIndex(k)
|
||||||
|
v2 := reflect.New(v.Type()).Elem()
|
||||||
|
rcopy(v2, v, false)
|
||||||
|
dst.SetMapIndex(k, v2)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Assign the value if possible. If its not assignable, the value would
|
||||||
|
// need to be converted and the impact of that may be unexpected, or is
|
||||||
|
// not compatible with the dst type.
|
||||||
|
if src.Type().AssignableTo(dst.Type()) {
|
||||||
|
dst.Set(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go
generated
vendored
Executable file
27
vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go
generated
vendored
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeepEqual returns if the two values are deeply equal like reflect.DeepEqual.
|
||||||
|
// In addition to this, this method will also dereference the input values if
|
||||||
|
// possible so the DeepEqual performed will not fail if one parameter is a
|
||||||
|
// pointer and the other is not.
|
||||||
|
//
|
||||||
|
// DeepEqual will not perform indirection of nested values of the input parameters.
|
||||||
|
func DeepEqual(a, b interface{}) bool {
|
||||||
|
ra := reflect.Indirect(reflect.ValueOf(a))
|
||||||
|
rb := reflect.Indirect(reflect.ValueOf(b))
|
||||||
|
|
||||||
|
if raValid, rbValid := ra.IsValid(), rb.IsValid(); !raValid && !rbValid {
|
||||||
|
// If the elements are both nil, and of the same type they are equal
|
||||||
|
// If they are of different types they are not equal
|
||||||
|
return reflect.TypeOf(a) == reflect.TypeOf(b)
|
||||||
|
} else if raValid != rbValid {
|
||||||
|
// Both values must be valid to be equal
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.DeepEqual(ra.Interface(), rb.Interface())
|
||||||
|
}
|
||||||
222
vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
Executable file
222
vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
Executable file
@@ -0,0 +1,222 @@
|
|||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jmespath/go-jmespath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
|
||||||
|
|
||||||
|
// rValuesAtPath returns a slice of values found in value v. The values
|
||||||
|
// in v are explored recursively so all nested values are collected.
|
||||||
|
func rValuesAtPath(v interface{}, path string, createPath, caseSensitive, nilTerm bool) []reflect.Value {
|
||||||
|
pathparts := strings.Split(path, "||")
|
||||||
|
if len(pathparts) > 1 {
|
||||||
|
for _, pathpart := range pathparts {
|
||||||
|
vals := rValuesAtPath(v, pathpart, createPath, caseSensitive, nilTerm)
|
||||||
|
if len(vals) > 0 {
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))}
|
||||||
|
components := strings.Split(path, ".")
|
||||||
|
for len(values) > 0 && len(components) > 0 {
|
||||||
|
var index *int64
|
||||||
|
var indexStar bool
|
||||||
|
c := strings.TrimSpace(components[0])
|
||||||
|
if c == "" { // no actual component, illegal syntax
|
||||||
|
return nil
|
||||||
|
} else if caseSensitive && c != "*" && strings.ToLower(c[0:1]) == c[0:1] {
|
||||||
|
// TODO normalize case for user
|
||||||
|
return nil // don't support unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse this component
|
||||||
|
if m := indexRe.FindStringSubmatch(c); m != nil {
|
||||||
|
c = m[1]
|
||||||
|
if m[2] == "" {
|
||||||
|
index = nil
|
||||||
|
indexStar = true
|
||||||
|
} else {
|
||||||
|
i, _ := strconv.ParseInt(m[2], 10, 32)
|
||||||
|
index = &i
|
||||||
|
indexStar = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextvals := []reflect.Value{}
|
||||||
|
for _, value := range values {
|
||||||
|
// pull component name out of struct member
|
||||||
|
if value.Kind() != reflect.Struct {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == "*" { // pull all members
|
||||||
|
for i := 0; i < value.NumField(); i++ {
|
||||||
|
if f := reflect.Indirect(value.Field(i)); f.IsValid() {
|
||||||
|
nextvals = append(nextvals, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.FieldByNameFunc(func(name string) bool {
|
||||||
|
if c == name {
|
||||||
|
return true
|
||||||
|
} else if !caseSensitive && strings.ToLower(name) == strings.ToLower(c) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if nilTerm && value.Kind() == reflect.Ptr && len(components[1:]) == 0 {
|
||||||
|
if !value.IsNil() {
|
||||||
|
value.Set(reflect.Zero(value.Type()))
|
||||||
|
}
|
||||||
|
return []reflect.Value{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
if createPath && value.Kind() == reflect.Ptr && value.IsNil() {
|
||||||
|
// TODO if the value is the terminus it should not be created
|
||||||
|
// if the value to be set to its position is nil.
|
||||||
|
value.Set(reflect.New(value.Type().Elem()))
|
||||||
|
value = value.Elem()
|
||||||
|
} else {
|
||||||
|
value = reflect.Indirect(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
|
||||||
|
if !createPath && value.IsNil() {
|
||||||
|
value = reflect.ValueOf(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsValid() {
|
||||||
|
nextvals = append(nextvals, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values = nextvals
|
||||||
|
|
||||||
|
if indexStar || index != nil {
|
||||||
|
nextvals = []reflect.Value{}
|
||||||
|
for _, valItem := range values {
|
||||||
|
value := reflect.Indirect(valItem)
|
||||||
|
if value.Kind() != reflect.Slice {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if indexStar { // grab all indices
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
idx := reflect.Indirect(value.Index(i))
|
||||||
|
if idx.IsValid() {
|
||||||
|
nextvals = append(nextvals, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// pull out index
|
||||||
|
i := int(*index)
|
||||||
|
if i >= value.Len() { // check out of bounds
|
||||||
|
if createPath {
|
||||||
|
// TODO resize slice
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if i < 0 { // support negative indexing
|
||||||
|
i = value.Len() + i
|
||||||
|
}
|
||||||
|
value = reflect.Indirect(value.Index(i))
|
||||||
|
|
||||||
|
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
|
||||||
|
if !createPath && value.IsNil() {
|
||||||
|
value = reflect.ValueOf(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsValid() {
|
||||||
|
nextvals = append(nextvals, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values = nextvals
|
||||||
|
}
|
||||||
|
|
||||||
|
components = components[1:]
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValuesAtPath returns a list of values at the case insensitive lexical
|
||||||
|
// path inside of a structure.
|
||||||
|
func ValuesAtPath(i interface{}, path string) ([]interface{}, error) {
|
||||||
|
result, err := jmespath.Search(path, i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(result)
|
||||||
|
if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if s, ok := result.([]interface{}); ok {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Map && v.Len() == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Slice {
|
||||||
|
out := make([]interface{}, v.Len())
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
out[i] = v.Index(i).Interface()
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []interface{}{result}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValueAtPath sets a value at the case insensitive lexical path inside
|
||||||
|
// of a structure.
|
||||||
|
func SetValueAtPath(i interface{}, path string, v interface{}) {
|
||||||
|
if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil {
|
||||||
|
for _, rval := range rvals {
|
||||||
|
if rval.Kind() == reflect.Ptr && rval.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
setValue(rval, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setValue(dstVal reflect.Value, src interface{}) {
|
||||||
|
if dstVal.Kind() == reflect.Ptr {
|
||||||
|
dstVal = reflect.Indirect(dstVal)
|
||||||
|
}
|
||||||
|
srcVal := reflect.ValueOf(src)
|
||||||
|
|
||||||
|
if !srcVal.IsValid() { // src is literal nil
|
||||||
|
if dstVal.CanAddr() {
|
||||||
|
// Convert to pointer so that pointer's value can be nil'ed
|
||||||
|
// dstVal = dstVal.Addr()
|
||||||
|
}
|
||||||
|
dstVal.Set(reflect.Zero(dstVal.Type()))
|
||||||
|
|
||||||
|
} else if srcVal.Kind() == reflect.Ptr {
|
||||||
|
if srcVal.IsNil() {
|
||||||
|
srcVal = reflect.Zero(dstVal.Type())
|
||||||
|
} else {
|
||||||
|
srcVal = reflect.ValueOf(src).Elem()
|
||||||
|
}
|
||||||
|
dstVal.Set(srcVal)
|
||||||
|
} else {
|
||||||
|
dstVal.Set(srcVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
113
vendor/github.com/aws/aws-sdk-go/aws/awsutil/prettify.go
generated
vendored
Executable file
113
vendor/github.com/aws/aws-sdk-go/aws/awsutil/prettify.go
generated
vendored
Executable file
@@ -0,0 +1,113 @@
|
|||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prettify returns the string representation of a value.
|
||||||
|
func Prettify(i interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
prettify(reflect.ValueOf(i), 0, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettify will recursively walk value v to build a textual
|
||||||
|
// representation of the value.
|
||||||
|
func prettify(v reflect.Value, indent int, buf *bytes.Buffer) {
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
strtype := v.Type().String()
|
||||||
|
if strtype == "time.Time" {
|
||||||
|
fmt.Fprintf(buf, "%s", v.Interface())
|
||||||
|
break
|
||||||
|
} else if strings.HasPrefix(strtype, "io.") {
|
||||||
|
buf.WriteString("<buffer>")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("{\n")
|
||||||
|
|
||||||
|
names := []string{}
|
||||||
|
for i := 0; i < v.Type().NumField(); i++ {
|
||||||
|
name := v.Type().Field(i).Name
|
||||||
|
f := v.Field(i)
|
||||||
|
if name[0:1] == strings.ToLower(name[0:1]) {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice || f.Kind() == reflect.Map) && f.IsNil() {
|
||||||
|
continue // ignore unset fields
|
||||||
|
}
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, n := range names {
|
||||||
|
val := v.FieldByName(n)
|
||||||
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
|
buf.WriteString(n + ": ")
|
||||||
|
prettify(val, indent+2, buf)
|
||||||
|
|
||||||
|
if i < len(names)-1 {
|
||||||
|
buf.WriteString(",\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||||
|
case reflect.Slice:
|
||||||
|
strtype := v.Type().String()
|
||||||
|
if strtype == "[]uint8" {
|
||||||
|
fmt.Fprintf(buf, "<binary> len %d", v.Len())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
nl, id, id2 := "", "", ""
|
||||||
|
if v.Len() > 3 {
|
||||||
|
nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2)
|
||||||
|
}
|
||||||
|
buf.WriteString("[" + nl)
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
buf.WriteString(id2)
|
||||||
|
prettify(v.Index(i), indent+2, buf)
|
||||||
|
|
||||||
|
if i < v.Len()-1 {
|
||||||
|
buf.WriteString("," + nl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(nl + id + "]")
|
||||||
|
case reflect.Map:
|
||||||
|
buf.WriteString("{\n")
|
||||||
|
|
||||||
|
for i, k := range v.MapKeys() {
|
||||||
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
|
buf.WriteString(k.String() + ": ")
|
||||||
|
prettify(v.MapIndex(k), indent+2, buf)
|
||||||
|
|
||||||
|
if i < v.Len()-1 {
|
||||||
|
buf.WriteString(",\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||||
|
default:
|
||||||
|
if !v.IsValid() {
|
||||||
|
fmt.Fprint(buf, "<invalid value>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
format := "%v"
|
||||||
|
switch v.Interface().(type) {
|
||||||
|
case string:
|
||||||
|
format = "%q"
|
||||||
|
case io.ReadSeeker, io.Reader:
|
||||||
|
format = "buffer(%p)"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, format, v.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
88
vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go
generated
vendored
Executable file
88
vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go
generated
vendored
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringValue returns the string representation of a value.
|
||||||
|
func StringValue(i interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
stringValue(reflect.ValueOf(i), 0, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringValue(v reflect.Value, indent int, buf *bytes.Buffer) {
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
buf.WriteString("{\n")
|
||||||
|
|
||||||
|
for i := 0; i < v.Type().NumField(); i++ {
|
||||||
|
ft := v.Type().Field(i)
|
||||||
|
fv := v.Field(i)
|
||||||
|
|
||||||
|
if ft.Name[0:1] == strings.ToLower(ft.Name[0:1]) {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
if (fv.Kind() == reflect.Ptr || fv.Kind() == reflect.Slice) && fv.IsNil() {
|
||||||
|
continue // ignore unset fields
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
|
buf.WriteString(ft.Name + ": ")
|
||||||
|
|
||||||
|
if tag := ft.Tag.Get("sensitive"); tag == "true" {
|
||||||
|
buf.WriteString("<sensitive>")
|
||||||
|
} else {
|
||||||
|
stringValue(fv, indent+2, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(",\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||||
|
case reflect.Slice:
|
||||||
|
nl, id, id2 := "", "", ""
|
||||||
|
if v.Len() > 3 {
|
||||||
|
nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2)
|
||||||
|
}
|
||||||
|
buf.WriteString("[" + nl)
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
buf.WriteString(id2)
|
||||||
|
stringValue(v.Index(i), indent+2, buf)
|
||||||
|
|
||||||
|
if i < v.Len()-1 {
|
||||||
|
buf.WriteString("," + nl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(nl + id + "]")
|
||||||
|
case reflect.Map:
|
||||||
|
buf.WriteString("{\n")
|
||||||
|
|
||||||
|
for i, k := range v.MapKeys() {
|
||||||
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
|
buf.WriteString(k.String() + ": ")
|
||||||
|
stringValue(v.MapIndex(k), indent+2, buf)
|
||||||
|
|
||||||
|
if i < v.Len()-1 {
|
||||||
|
buf.WriteString(",\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||||
|
default:
|
||||||
|
format := "%v"
|
||||||
|
switch v.Interface().(type) {
|
||||||
|
case string:
|
||||||
|
format = "%q"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, format, v.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
96
vendor/github.com/aws/aws-sdk-go/aws/client/client.go
generated
vendored
Executable file
96
vendor/github.com/aws/aws-sdk-go/aws/client/client.go
generated
vendored
Executable file
@@ -0,0 +1,96 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Config provides configuration to a service client instance.
|
||||||
|
type Config struct {
|
||||||
|
Config *aws.Config
|
||||||
|
Handlers request.Handlers
|
||||||
|
Endpoint string
|
||||||
|
SigningRegion string
|
||||||
|
SigningName string
|
||||||
|
|
||||||
|
// States that the signing name did not come from a modeled source but
|
||||||
|
// was derived based on other data. Used by service client constructors
|
||||||
|
// to determine if the signin name can be overridden based on metadata the
|
||||||
|
// service has.
|
||||||
|
SigningNameDerived bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigProvider provides a generic way for a service client to receive
|
||||||
|
// the ClientConfig without circular dependencies.
|
||||||
|
type ConfigProvider interface {
|
||||||
|
ClientConfig(serviceName string, cfgs ...*aws.Config) Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigNoResolveEndpointProvider same as ConfigProvider except it will not
|
||||||
|
// resolve the endpoint automatically. The service client's endpoint must be
|
||||||
|
// provided via the aws.Config.Endpoint field.
|
||||||
|
type ConfigNoResolveEndpointProvider interface {
|
||||||
|
ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Client implements the base client request and response handling
|
||||||
|
// used by all service clients.
|
||||||
|
type Client struct {
|
||||||
|
request.Retryer
|
||||||
|
metadata.ClientInfo
|
||||||
|
|
||||||
|
Config aws.Config
|
||||||
|
Handlers request.Handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// New will return a pointer to a new initialized service client.
|
||||||
|
func New(cfg aws.Config, info metadata.ClientInfo, handlers request.Handlers, options ...func(*Client)) *Client {
|
||||||
|
svc := &Client{
|
||||||
|
Config: cfg,
|
||||||
|
ClientInfo: info,
|
||||||
|
Handlers: handlers.Copy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch retryer, ok := cfg.Retryer.(request.Retryer); {
|
||||||
|
case ok:
|
||||||
|
svc.Retryer = retryer
|
||||||
|
case cfg.Retryer != nil && cfg.Logger != nil:
|
||||||
|
s := fmt.Sprintf("WARNING: %T does not implement request.Retryer; using DefaultRetryer instead", cfg.Retryer)
|
||||||
|
cfg.Logger.Log(s)
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
maxRetries := aws.IntValue(cfg.MaxRetries)
|
||||||
|
if cfg.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries {
|
||||||
|
maxRetries = 3
|
||||||
|
}
|
||||||
|
svc.Retryer = DefaultRetryer{NumMaxRetries: maxRetries}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.AddDebugHandlers()
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest returns a new Request pointer for the service API
|
||||||
|
// operation and parameters.
|
||||||
|
func (c *Client) NewRequest(operation *request.Operation, params interface{}, data interface{}) *request.Request {
|
||||||
|
return request.New(c.Config, c.ClientInfo, c.Handlers, c.Retryer, operation, params, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDebugHandlers injects debug logging handlers into the service to log request
|
||||||
|
// debug information.
|
||||||
|
func (c *Client) AddDebugHandlers() {
|
||||||
|
if !c.Config.LogLevel.AtLeast(aws.LogDebug) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Handlers.Send.PushFrontNamed(LogHTTPRequestHandler)
|
||||||
|
c.Handlers.Send.PushBackNamed(LogHTTPResponseHandler)
|
||||||
|
}
|
||||||
116
vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
generated
vendored
Executable file
116
vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
generated
vendored
Executable file
@@ -0,0 +1,116 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/sdkrand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultRetryer implements basic retry logic using exponential backoff for
|
||||||
|
// most services. If you want to implement custom retry logic, implement the
|
||||||
|
// request.Retryer interface or create a structure type that composes this
|
||||||
|
// struct and override the specific methods. For example, to override only
|
||||||
|
// the MaxRetries method:
|
||||||
|
//
|
||||||
|
// type retryer struct {
|
||||||
|
// client.DefaultRetryer
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // This implementation always has 100 max retries
|
||||||
|
// func (d retryer) MaxRetries() int { return 100 }
|
||||||
|
type DefaultRetryer struct {
|
||||||
|
NumMaxRetries int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxRetries returns the number of maximum returns the service will use to make
|
||||||
|
// an individual API request.
|
||||||
|
func (d DefaultRetryer) MaxRetries() int {
|
||||||
|
return d.NumMaxRetries
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryRules returns the delay duration before retrying this request again
|
||||||
|
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
|
||||||
|
// Set the upper limit of delay in retrying at ~five minutes
|
||||||
|
minTime := 30
|
||||||
|
throttle := d.shouldThrottle(r)
|
||||||
|
if throttle {
|
||||||
|
if delay, ok := getRetryDelay(r); ok {
|
||||||
|
return delay
|
||||||
|
}
|
||||||
|
|
||||||
|
minTime = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
retryCount := r.RetryCount
|
||||||
|
if throttle && retryCount > 8 {
|
||||||
|
retryCount = 8
|
||||||
|
} else if retryCount > 13 {
|
||||||
|
retryCount = 13
|
||||||
|
}
|
||||||
|
|
||||||
|
delay := (1 << uint(retryCount)) * (sdkrand.SeededRand.Intn(minTime) + minTime)
|
||||||
|
return time.Duration(delay) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldRetry returns true if the request should be retried.
|
||||||
|
func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
|
||||||
|
// If one of the other handlers already set the retry state
|
||||||
|
// we don't want to override it based on the service's state
|
||||||
|
if r.Retryable != nil {
|
||||||
|
return *r.Retryable
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.HTTPResponse.StatusCode >= 500 && r.HTTPResponse.StatusCode != 501 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return r.IsErrorRetryable() || d.shouldThrottle(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldThrottle returns true if the request should be throttled.
|
||||||
|
func (d DefaultRetryer) shouldThrottle(r *request.Request) bool {
|
||||||
|
switch r.HTTPResponse.StatusCode {
|
||||||
|
case 429:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
default:
|
||||||
|
return r.IsErrorThrottle()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will look in the Retry-After header, RFC 7231, for how long
|
||||||
|
// it will wait before attempting another request
|
||||||
|
func getRetryDelay(r *request.Request) (time.Duration, bool) {
|
||||||
|
if !canUseRetryAfterHeader(r) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
delayStr := r.HTTPResponse.Header.Get("Retry-After")
|
||||||
|
if len(delayStr) == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
delay, err := strconv.Atoi(delayStr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Duration(delay) * time.Second, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will look at the status code to see if the retry header pertains to
|
||||||
|
// the status code.
|
||||||
|
func canUseRetryAfterHeader(r *request.Request) bool {
|
||||||
|
switch r.HTTPResponse.StatusCode {
|
||||||
|
case 429:
|
||||||
|
case 503:
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
190
vendor/github.com/aws/aws-sdk-go/aws/client/logger.go
generated
vendored
Executable file
190
vendor/github.com/aws/aws-sdk-go/aws/client/logger.go
generated
vendored
Executable file
@@ -0,0 +1,190 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http/httputil"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
const logReqMsg = `DEBUG: Request %s/%s Details:
|
||||||
|
---[ REQUEST POST-SIGN ]-----------------------------
|
||||||
|
%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
|
||||||
|
const logReqErrMsg = `DEBUG ERROR: Request %s/%s:
|
||||||
|
---[ REQUEST DUMP ERROR ]-----------------------------
|
||||||
|
%s
|
||||||
|
------------------------------------------------------`
|
||||||
|
|
||||||
|
type logWriter struct {
|
||||||
|
// Logger is what we will use to log the payload of a response.
|
||||||
|
Logger aws.Logger
|
||||||
|
// buf stores the contents of what has been read
|
||||||
|
buf *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *logWriter) Write(b []byte) (int, error) {
|
||||||
|
return logger.buf.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
type teeReaderCloser struct {
|
||||||
|
// io.Reader will be a tee reader that is used during logging.
|
||||||
|
// This structure will read from a body and write the contents to a logger.
|
||||||
|
io.Reader
|
||||||
|
// Source is used just to close when we are done reading.
|
||||||
|
Source io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (reader *teeReaderCloser) Close() error {
|
||||||
|
return reader.Source.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogHTTPRequestHandler is a SDK request handler to log the HTTP request sent
|
||||||
|
// to a service. Will include the HTTP request body if the LogLevel of the
|
||||||
|
// request matches LogDebugWithHTTPBody.
|
||||||
|
var LogHTTPRequestHandler = request.NamedHandler{
|
||||||
|
Name: "awssdk.client.LogRequest",
|
||||||
|
Fn: logRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
func logRequest(r *request.Request) {
|
||||||
|
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||||
|
bodySeekable := aws.IsReaderSeekable(r.Body)
|
||||||
|
|
||||||
|
b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
|
||||||
|
if err != nil {
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if logBody {
|
||||||
|
if !bodySeekable {
|
||||||
|
r.SetReaderBody(aws.ReadSeekCloser(r.HTTPRequest.Body))
|
||||||
|
}
|
||||||
|
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
|
||||||
|
// Body as a NoOpCloser and will not be reset after read by the HTTP
|
||||||
|
// client reader.
|
||||||
|
r.ResetBody()
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogHTTPRequestHeaderHandler is a SDK request handler to log the HTTP request sent
|
||||||
|
// to a service. Will only log the HTTP request's headers. The request payload
|
||||||
|
// will not be read.
|
||||||
|
var LogHTTPRequestHeaderHandler = request.NamedHandler{
|
||||||
|
Name: "awssdk.client.LogRequestHeader",
|
||||||
|
Fn: logRequestHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
func logRequestHeader(r *request.Request) {
|
||||||
|
b, err := httputil.DumpRequestOut(r.HTTPRequest, false)
|
||||||
|
if err != nil {
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const logRespMsg = `DEBUG: Response %s/%s Details:
|
||||||
|
---[ RESPONSE ]--------------------------------------
|
||||||
|
%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
|
||||||
|
const logRespErrMsg = `DEBUG ERROR: Response %s/%s:
|
||||||
|
---[ RESPONSE DUMP ERROR ]-----------------------------
|
||||||
|
%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
|
||||||
|
// LogHTTPResponseHandler is a SDK request handler to log the HTTP response
|
||||||
|
// received from a service. Will include the HTTP response body if the LogLevel
|
||||||
|
// of the request matches LogDebugWithHTTPBody.
|
||||||
|
var LogHTTPResponseHandler = request.NamedHandler{
|
||||||
|
Name: "awssdk.client.LogResponse",
|
||||||
|
Fn: logResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
func logResponse(r *request.Request) {
|
||||||
|
lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)}
|
||||||
|
|
||||||
|
if r.HTTPResponse == nil {
|
||||||
|
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, "request's HTTPResponse is nil"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||||
|
if logBody {
|
||||||
|
r.HTTPResponse.Body = &teeReaderCloser{
|
||||||
|
Reader: io.TeeReader(r.HTTPResponse.Body, lw),
|
||||||
|
Source: r.HTTPResponse.Body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerFn := func(req *request.Request) {
|
||||||
|
b, err := httputil.DumpResponse(req.HTTPResponse, false)
|
||||||
|
if err != nil {
|
||||||
|
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||||
|
req.ClientInfo.ServiceName, req.Operation.Name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lw.Logger.Log(fmt.Sprintf(logRespMsg,
|
||||||
|
req.ClientInfo.ServiceName, req.Operation.Name, string(b)))
|
||||||
|
|
||||||
|
if logBody {
|
||||||
|
b, err := ioutil.ReadAll(lw.buf)
|
||||||
|
if err != nil {
|
||||||
|
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||||
|
req.ClientInfo.ServiceName, req.Operation.Name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lw.Logger.Log(string(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlerName = "awsdk.client.LogResponse.ResponseBody"
|
||||||
|
|
||||||
|
r.Handlers.Unmarshal.SetBackNamed(request.NamedHandler{
|
||||||
|
Name: handlerName, Fn: handlerFn,
|
||||||
|
})
|
||||||
|
r.Handlers.UnmarshalError.SetBackNamed(request.NamedHandler{
|
||||||
|
Name: handlerName, Fn: handlerFn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogHTTPResponseHeaderHandler is a SDK request handler to log the HTTP
|
||||||
|
// response received from a service. Will only log the HTTP response's headers.
|
||||||
|
// The response payload will not be read.
|
||||||
|
var LogHTTPResponseHeaderHandler = request.NamedHandler{
|
||||||
|
Name: "awssdk.client.LogResponseHeader",
|
||||||
|
Fn: logResponseHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
func logResponseHeader(r *request.Request) {
|
||||||
|
if r.Config.Logger == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := httputil.DumpResponse(r.HTTPResponse, false)
|
||||||
|
if err != nil {
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logRespMsg,
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||||
|
}
|
||||||
13
vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go
generated
vendored
Executable file
13
vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go
generated
vendored
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
package metadata
|
||||||
|
|
||||||
|
// ClientInfo wraps immutable data from the client.Client structure.
|
||||||
|
type ClientInfo struct {
|
||||||
|
ServiceName string
|
||||||
|
ServiceID string
|
||||||
|
APIVersion string
|
||||||
|
Endpoint string
|
||||||
|
SigningName string
|
||||||
|
SigningRegion string
|
||||||
|
JSONVersion string
|
||||||
|
TargetPrefix string
|
||||||
|
}
|
||||||
536
vendor/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
Executable file
536
vendor/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
Executable file
@@ -0,0 +1,536 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UseServiceDefaultRetries instructs the config to use the service's own
|
||||||
|
// default number of retries. This will be the default action if
|
||||||
|
// Config.MaxRetries is nil also.
|
||||||
|
const UseServiceDefaultRetries = -1
|
||||||
|
|
||||||
|
// RequestRetryer is an alias for a type that implements the request.Retryer
|
||||||
|
// interface.
|
||||||
|
type RequestRetryer interface{}
|
||||||
|
|
||||||
|
// A Config provides service configuration for service clients. By default,
|
||||||
|
// all clients will use the defaults.DefaultConfig structure.
|
||||||
|
//
|
||||||
|
// // Create Session with MaxRetry configuration to be shared by multiple
|
||||||
|
// // service clients.
|
||||||
|
// sess := session.Must(session.NewSession(&aws.Config{
|
||||||
|
// MaxRetries: aws.Int(3),
|
||||||
|
// }))
|
||||||
|
//
|
||||||
|
// // Create S3 service client with a specific Region.
|
||||||
|
// svc := s3.New(sess, &aws.Config{
|
||||||
|
// Region: aws.String("us-west-2"),
|
||||||
|
// })
|
||||||
|
type Config struct {
|
||||||
|
// Enables verbose error printing of all credential chain errors.
|
||||||
|
// Should be used when wanting to see all errors while attempting to
|
||||||
|
// retrieve credentials.
|
||||||
|
CredentialsChainVerboseErrors *bool
|
||||||
|
|
||||||
|
// The credentials object to use when signing requests. Defaults to a
|
||||||
|
// chain of credential providers to search for credentials in environment
|
||||||
|
// variables, shared credential file, and EC2 Instance Roles.
|
||||||
|
Credentials *credentials.Credentials
|
||||||
|
|
||||||
|
// An optional endpoint URL (hostname only or fully qualified URI)
|
||||||
|
// that overrides the default generated endpoint for a client. Set this
|
||||||
|
// to `""` to use the default generated endpoint.
|
||||||
|
//
|
||||||
|
// Note: You must still provide a `Region` value when specifying an
|
||||||
|
// endpoint for a client.
|
||||||
|
Endpoint *string
|
||||||
|
|
||||||
|
// The resolver to use for looking up endpoints for AWS service clients
|
||||||
|
// to use based on region.
|
||||||
|
EndpointResolver endpoints.Resolver
|
||||||
|
|
||||||
|
// EnforceShouldRetryCheck is used in the AfterRetryHandler to always call
|
||||||
|
// ShouldRetry regardless of whether or not if request.Retryable is set.
|
||||||
|
// This will utilize ShouldRetry method of custom retryers. If EnforceShouldRetryCheck
|
||||||
|
// is not set, then ShouldRetry will only be called if request.Retryable is nil.
|
||||||
|
// Proper handling of the request.Retryable field is important when setting this field.
|
||||||
|
EnforceShouldRetryCheck *bool
|
||||||
|
|
||||||
|
// The region to send requests to. This parameter is required and must
|
||||||
|
// be configured globally or on a per-client basis unless otherwise
|
||||||
|
// noted. A full list of regions is found in the "Regions and Endpoints"
|
||||||
|
// document.
|
||||||
|
//
|
||||||
|
// See http://docs.aws.amazon.com/general/latest/gr/rande.html for AWS
|
||||||
|
// Regions and Endpoints.
|
||||||
|
Region *string
|
||||||
|
|
||||||
|
// Set this to `true` to disable SSL when sending requests. Defaults
|
||||||
|
// to `false`.
|
||||||
|
DisableSSL *bool
|
||||||
|
|
||||||
|
// The HTTP client to use when sending requests. Defaults to
|
||||||
|
// `http.DefaultClient`.
|
||||||
|
HTTPClient *http.Client
|
||||||
|
|
||||||
|
// An integer value representing the logging level. The default log level
|
||||||
|
// is zero (LogOff), which represents no logging. To enable logging set
|
||||||
|
// to a LogLevel Value.
|
||||||
|
LogLevel *LogLevelType
|
||||||
|
|
||||||
|
// The logger writer interface to write logging messages to. Defaults to
|
||||||
|
// standard out.
|
||||||
|
Logger Logger
|
||||||
|
|
||||||
|
// The maximum number of times that a request will be retried for failures.
|
||||||
|
// Defaults to -1, which defers the max retry setting to the service
|
||||||
|
// specific configuration.
|
||||||
|
MaxRetries *int
|
||||||
|
|
||||||
|
// Retryer guides how HTTP requests should be retried in case of
|
||||||
|
// recoverable failures.
|
||||||
|
//
|
||||||
|
// When nil or the value does not implement the request.Retryer interface,
|
||||||
|
// the client.DefaultRetryer will be used.
|
||||||
|
//
|
||||||
|
// When both Retryer and MaxRetries are non-nil, the former is used and
|
||||||
|
// the latter ignored.
|
||||||
|
//
|
||||||
|
// To set the Retryer field in a type-safe manner and with chaining, use
|
||||||
|
// the request.WithRetryer helper function:
|
||||||
|
//
|
||||||
|
// cfg := request.WithRetryer(aws.NewConfig(), myRetryer)
|
||||||
|
//
|
||||||
|
Retryer RequestRetryer
|
||||||
|
|
||||||
|
// Disables semantic parameter validation, which validates input for
|
||||||
|
// missing required fields and/or other semantic request input errors.
|
||||||
|
DisableParamValidation *bool
|
||||||
|
|
||||||
|
// Disables the computation of request and response checksums, e.g.,
|
||||||
|
// CRC32 checksums in Amazon DynamoDB.
|
||||||
|
DisableComputeChecksums *bool
|
||||||
|
|
||||||
|
// Set this to `true` to force the request to use path-style addressing,
|
||||||
|
// i.e., `http://s3.amazonaws.com/BUCKET/KEY`. By default, the S3 client
|
||||||
|
// will use virtual hosted bucket addressing when possible
|
||||||
|
// (`http://BUCKET.s3.amazonaws.com/KEY`).
|
||||||
|
//
|
||||||
|
// Note: This configuration option is specific to the Amazon S3 service.
|
||||||
|
//
|
||||||
|
// See http://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html
|
||||||
|
// for Amazon S3: Virtual Hosting of Buckets
|
||||||
|
S3ForcePathStyle *bool
|
||||||
|
|
||||||
|
// Set this to `true` to disable the SDK adding the `Expect: 100-Continue`
|
||||||
|
// header to PUT requests over 2MB of content. 100-Continue instructs the
|
||||||
|
// HTTP client not to send the body until the service responds with a
|
||||||
|
// `continue` status. This is useful to prevent sending the request body
|
||||||
|
// until after the request is authenticated, and validated.
|
||||||
|
//
|
||||||
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
|
||||||
|
//
|
||||||
|
// 100-Continue is only enabled for Go 1.6 and above. See `http.Transport`'s
|
||||||
|
// `ExpectContinueTimeout` for information on adjusting the continue wait
|
||||||
|
// timeout. https://golang.org/pkg/net/http/#Transport
|
||||||
|
//
|
||||||
|
// You should use this flag to disble 100-Continue if you experience issues
|
||||||
|
// with proxies or third party S3 compatible services.
|
||||||
|
S3Disable100Continue *bool
|
||||||
|
|
||||||
|
// Set this to `true` to enable S3 Accelerate feature. For all operations
|
||||||
|
// compatible with S3 Accelerate will use the accelerate endpoint for
|
||||||
|
// requests. Requests not compatible will fall back to normal S3 requests.
|
||||||
|
//
|
||||||
|
// The bucket must be enable for accelerate to be used with S3 client with
|
||||||
|
// accelerate enabled. If the bucket is not enabled for accelerate an error
|
||||||
|
// will be returned. The bucket name must be DNS compatible to also work
|
||||||
|
// with accelerate.
|
||||||
|
S3UseAccelerate *bool
|
||||||
|
|
||||||
|
// S3DisableContentMD5Validation config option is temporarily disabled,
|
||||||
|
// For S3 GetObject API calls, #1837.
|
||||||
|
//
|
||||||
|
// Set this to `true` to disable the S3 service client from automatically
|
||||||
|
// adding the ContentMD5 to S3 Object Put and Upload API calls. This option
|
||||||
|
// will also disable the SDK from performing object ContentMD5 validation
|
||||||
|
// on GetObject API calls.
|
||||||
|
S3DisableContentMD5Validation *bool
|
||||||
|
|
||||||
|
// Set this to `true` to disable the EC2Metadata client from overriding the
|
||||||
|
// default http.Client's Timeout. This is helpful if you do not want the
|
||||||
|
// EC2Metadata client to create a new http.Client. This options is only
|
||||||
|
// meaningful if you're not already using a custom HTTP client with the
|
||||||
|
// SDK. Enabled by default.
|
||||||
|
//
|
||||||
|
// Must be set and provided to the session.NewSession() in order to disable
|
||||||
|
// the EC2Metadata overriding the timeout for default credentials chain.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// sess := session.Must(session.NewSession(aws.NewConfig()
|
||||||
|
// .WithEC2MetadataDiableTimeoutOverride(true)))
|
||||||
|
//
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
//
|
||||||
|
EC2MetadataDisableTimeoutOverride *bool
|
||||||
|
|
||||||
|
// Instructs the endpoint to be generated for a service client to
|
||||||
|
// be the dual stack endpoint. The dual stack endpoint will support
|
||||||
|
// both IPv4 and IPv6 addressing.
|
||||||
|
//
|
||||||
|
// Setting this for a service which does not support dual stack will fail
|
||||||
|
// to make requets. It is not recommended to set this value on the session
|
||||||
|
// as it will apply to all service clients created with the session. Even
|
||||||
|
// services which don't support dual stack endpoints.
|
||||||
|
//
|
||||||
|
// If the Endpoint config value is also provided the UseDualStack flag
|
||||||
|
// will be ignored.
|
||||||
|
//
|
||||||
|
// Only supported with.
|
||||||
|
//
|
||||||
|
// sess := session.Must(session.NewSession())
|
||||||
|
//
|
||||||
|
// svc := s3.New(sess, &aws.Config{
|
||||||
|
// UseDualStack: aws.Bool(true),
|
||||||
|
// })
|
||||||
|
UseDualStack *bool
|
||||||
|
|
||||||
|
// SleepDelay is an override for the func the SDK will call when sleeping
|
||||||
|
// during the lifecycle of a request. Specifically this will be used for
|
||||||
|
// request delays. This value should only be used for testing. To adjust
|
||||||
|
// the delay of a request see the aws/client.DefaultRetryer and
|
||||||
|
// aws/request.Retryer.
|
||||||
|
//
|
||||||
|
// SleepDelay will prevent any Context from being used for canceling retry
|
||||||
|
// delay of an API operation. It is recommended to not use SleepDelay at all
|
||||||
|
// and specify a Retryer instead.
|
||||||
|
SleepDelay func(time.Duration)
|
||||||
|
|
||||||
|
// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.
|
||||||
|
// Will default to false. This would only be used for empty directory names in s3 requests.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// sess := session.Must(session.NewSession(&aws.Config{
|
||||||
|
// DisableRestProtocolURICleaning: aws.Bool(true),
|
||||||
|
// }))
|
||||||
|
//
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
// out, err := svc.GetObject(&s3.GetObjectInput {
|
||||||
|
// Bucket: aws.String("bucketname"),
|
||||||
|
// Key: aws.String("//foo//bar//moo"),
|
||||||
|
// })
|
||||||
|
DisableRestProtocolURICleaning *bool
|
||||||
|
|
||||||
|
// EnableEndpointDiscovery will allow for endpoint discovery on operations that
|
||||||
|
// have the definition in its model. By default, endpoint discovery is off.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// sess := session.Must(session.NewSession(&aws.Config{
|
||||||
|
// EnableEndpointDiscovery: aws.Bool(true),
|
||||||
|
// }))
|
||||||
|
//
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
// out, err := svc.GetObject(&s3.GetObjectInput {
|
||||||
|
// Bucket: aws.String("bucketname"),
|
||||||
|
// Key: aws.String("/foo/bar/moo"),
|
||||||
|
// })
|
||||||
|
EnableEndpointDiscovery *bool
|
||||||
|
|
||||||
|
// DisableEndpointHostPrefix will disable the SDK's behavior of prefixing
|
||||||
|
// request endpoint hosts with modeled information.
|
||||||
|
//
|
||||||
|
// Disabling this feature is useful when you want to use local endpoints
|
||||||
|
// for testing that do not support the modeled host prefix pattern.
|
||||||
|
DisableEndpointHostPrefix *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfig returns a new Config pointer that can be chained with builder
|
||||||
|
// methods to set multiple configuration values inline without using pointers.
|
||||||
|
//
|
||||||
|
// // Create Session with MaxRetry configuration to be shared by multiple
|
||||||
|
// // service clients.
|
||||||
|
// sess := session.Must(session.NewSession(aws.NewConfig().
|
||||||
|
// WithMaxRetries(3),
|
||||||
|
// ))
|
||||||
|
//
|
||||||
|
// // Create S3 service client with a specific Region.
|
||||||
|
// svc := s3.New(sess, aws.NewConfig().
|
||||||
|
// WithRegion("us-west-2"),
|
||||||
|
// )
|
||||||
|
func NewConfig() *Config {
|
||||||
|
return &Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCredentialsChainVerboseErrors sets a config verbose errors boolean and returning
|
||||||
|
// a Config pointer.
|
||||||
|
func (c *Config) WithCredentialsChainVerboseErrors(verboseErrs bool) *Config {
|
||||||
|
c.CredentialsChainVerboseErrors = &verboseErrs
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCredentials sets a config Credentials value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithCredentials(creds *credentials.Credentials) *Config {
|
||||||
|
c.Credentials = creds
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEndpoint sets a config Endpoint value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithEndpoint(endpoint string) *Config {
|
||||||
|
c.Endpoint = &endpoint
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEndpointResolver sets a config EndpointResolver value returning a
|
||||||
|
// Config pointer for chaining.
|
||||||
|
func (c *Config) WithEndpointResolver(resolver endpoints.Resolver) *Config {
|
||||||
|
c.EndpointResolver = resolver
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRegion sets a config Region value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithRegion(region string) *Config {
|
||||||
|
c.Region = ®ion
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableSSL sets a config DisableSSL value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithDisableSSL(disable bool) *Config {
|
||||||
|
c.DisableSSL = &disable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHTTPClient sets a config HTTPClient value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithHTTPClient(client *http.Client) *Config {
|
||||||
|
c.HTTPClient = client
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMaxRetries sets a config MaxRetries value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithMaxRetries(max int) *Config {
|
||||||
|
c.MaxRetries = &max
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableParamValidation sets a config DisableParamValidation value
|
||||||
|
// returning a Config pointer for chaining.
|
||||||
|
func (c *Config) WithDisableParamValidation(disable bool) *Config {
|
||||||
|
c.DisableParamValidation = &disable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableComputeChecksums sets a config DisableComputeChecksums value
|
||||||
|
// returning a Config pointer for chaining.
|
||||||
|
func (c *Config) WithDisableComputeChecksums(disable bool) *Config {
|
||||||
|
c.DisableComputeChecksums = &disable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogLevel sets a config LogLevel value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithLogLevel(level LogLevelType) *Config {
|
||||||
|
c.LogLevel = &level
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogger sets a config Logger value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithLogger(logger Logger) *Config {
|
||||||
|
c.Logger = logger
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithS3ForcePathStyle sets a config S3ForcePathStyle value returning a Config
|
||||||
|
// pointer for chaining.
|
||||||
|
func (c *Config) WithS3ForcePathStyle(force bool) *Config {
|
||||||
|
c.S3ForcePathStyle = &force
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithS3Disable100Continue sets a config S3Disable100Continue value returning
|
||||||
|
// a Config pointer for chaining.
|
||||||
|
func (c *Config) WithS3Disable100Continue(disable bool) *Config {
|
||||||
|
c.S3Disable100Continue = &disable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithS3UseAccelerate sets a config S3UseAccelerate value returning a Config
|
||||||
|
// pointer for chaining.
|
||||||
|
func (c *Config) WithS3UseAccelerate(enable bool) *Config {
|
||||||
|
c.S3UseAccelerate = &enable
|
||||||
|
return c
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithS3DisableContentMD5Validation sets a config
|
||||||
|
// S3DisableContentMD5Validation value returning a Config pointer for chaining.
|
||||||
|
func (c *Config) WithS3DisableContentMD5Validation(enable bool) *Config {
|
||||||
|
c.S3DisableContentMD5Validation = &enable
|
||||||
|
return c
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUseDualStack sets a config UseDualStack value returning a Config
|
||||||
|
// pointer for chaining.
|
||||||
|
func (c *Config) WithUseDualStack(enable bool) *Config {
|
||||||
|
c.UseDualStack = &enable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEC2MetadataDisableTimeoutOverride sets a config EC2MetadataDisableTimeoutOverride value
|
||||||
|
// returning a Config pointer for chaining.
|
||||||
|
func (c *Config) WithEC2MetadataDisableTimeoutOverride(enable bool) *Config {
|
||||||
|
c.EC2MetadataDisableTimeoutOverride = &enable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSleepDelay overrides the function used to sleep while waiting for the
|
||||||
|
// next retry. Defaults to time.Sleep.
|
||||||
|
func (c *Config) WithSleepDelay(fn func(time.Duration)) *Config {
|
||||||
|
c.SleepDelay = fn
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEndpointDiscovery will set whether or not to use endpoint discovery.
|
||||||
|
func (c *Config) WithEndpointDiscovery(t bool) *Config {
|
||||||
|
c.EnableEndpointDiscovery = &t
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableEndpointHostPrefix will set whether or not to use modeled host prefix
|
||||||
|
// when making requests.
|
||||||
|
func (c *Config) WithDisableEndpointHostPrefix(t bool) *Config {
|
||||||
|
c.DisableEndpointHostPrefix = &t
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeIn merges the passed in configs into the existing config object.
|
||||||
|
func (c *Config) MergeIn(cfgs ...*Config) {
|
||||||
|
for _, other := range cfgs {
|
||||||
|
mergeInConfig(c, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeInConfig(dst *Config, other *Config) {
|
||||||
|
if other == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.CredentialsChainVerboseErrors != nil {
|
||||||
|
dst.CredentialsChainVerboseErrors = other.CredentialsChainVerboseErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Credentials != nil {
|
||||||
|
dst.Credentials = other.Credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Endpoint != nil {
|
||||||
|
dst.Endpoint = other.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.EndpointResolver != nil {
|
||||||
|
dst.EndpointResolver = other.EndpointResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Region != nil {
|
||||||
|
dst.Region = other.Region
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableSSL != nil {
|
||||||
|
dst.DisableSSL = other.DisableSSL
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.HTTPClient != nil {
|
||||||
|
dst.HTTPClient = other.HTTPClient
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.LogLevel != nil {
|
||||||
|
dst.LogLevel = other.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Logger != nil {
|
||||||
|
dst.Logger = other.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.MaxRetries != nil {
|
||||||
|
dst.MaxRetries = other.MaxRetries
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Retryer != nil {
|
||||||
|
dst.Retryer = other.Retryer
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableParamValidation != nil {
|
||||||
|
dst.DisableParamValidation = other.DisableParamValidation
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableComputeChecksums != nil {
|
||||||
|
dst.DisableComputeChecksums = other.DisableComputeChecksums
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.S3ForcePathStyle != nil {
|
||||||
|
dst.S3ForcePathStyle = other.S3ForcePathStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.S3Disable100Continue != nil {
|
||||||
|
dst.S3Disable100Continue = other.S3Disable100Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.S3UseAccelerate != nil {
|
||||||
|
dst.S3UseAccelerate = other.S3UseAccelerate
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.S3DisableContentMD5Validation != nil {
|
||||||
|
dst.S3DisableContentMD5Validation = other.S3DisableContentMD5Validation
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.UseDualStack != nil {
|
||||||
|
dst.UseDualStack = other.UseDualStack
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.EC2MetadataDisableTimeoutOverride != nil {
|
||||||
|
dst.EC2MetadataDisableTimeoutOverride = other.EC2MetadataDisableTimeoutOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.SleepDelay != nil {
|
||||||
|
dst.SleepDelay = other.SleepDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableRestProtocolURICleaning != nil {
|
||||||
|
dst.DisableRestProtocolURICleaning = other.DisableRestProtocolURICleaning
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.EnforceShouldRetryCheck != nil {
|
||||||
|
dst.EnforceShouldRetryCheck = other.EnforceShouldRetryCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.EnableEndpointDiscovery != nil {
|
||||||
|
dst.EnableEndpointDiscovery = other.EnableEndpointDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableEndpointHostPrefix != nil {
|
||||||
|
dst.DisableEndpointHostPrefix = other.DisableEndpointHostPrefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy will return a shallow copy of the Config object. If any additional
|
||||||
|
// configurations are provided they will be merged into the new config returned.
|
||||||
|
func (c *Config) Copy(cfgs ...*Config) *Config {
|
||||||
|
dst := &Config{}
|
||||||
|
dst.MergeIn(c)
|
||||||
|
|
||||||
|
for _, cfg := range cfgs {
|
||||||
|
dst.MergeIn(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
37
vendor/github.com/aws/aws-sdk-go/aws/context_1_5.go
generated
vendored
Executable file
37
vendor/github.com/aws/aws-sdk-go/aws/context_1_5.go
generated
vendored
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
// +build !go1.9
|
||||||
|
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Context is an copy of the Go v1.7 stdlib's context.Context interface.
|
||||||
|
// It is represented as a SDK interface to enable you to use the "WithContext"
|
||||||
|
// API methods with Go v1.6 and a Context type such as golang.org/x/net/context.
|
||||||
|
//
|
||||||
|
// See https://golang.org/pkg/context on how to use contexts.
|
||||||
|
type Context interface {
|
||||||
|
// Deadline returns the time when work done on behalf of this context
|
||||||
|
// should be canceled. Deadline returns ok==false when no deadline is
|
||||||
|
// set. Successive calls to Deadline return the same results.
|
||||||
|
Deadline() (deadline time.Time, ok bool)
|
||||||
|
|
||||||
|
// Done returns a channel that's closed when work done on behalf of this
|
||||||
|
// context should be canceled. Done may return nil if this context can
|
||||||
|
// never be canceled. Successive calls to Done return the same value.
|
||||||
|
Done() <-chan struct{}
|
||||||
|
|
||||||
|
// Err returns a non-nil error value after Done is closed. Err returns
|
||||||
|
// Canceled if the context was canceled or DeadlineExceeded if the
|
||||||
|
// context's deadline passed. No other values for Err are defined.
|
||||||
|
// After Done is closed, successive calls to Err return the same value.
|
||||||
|
Err() error
|
||||||
|
|
||||||
|
// Value returns the value associated with this context for key, or nil
|
||||||
|
// if no value is associated with key. Successive calls to Value with
|
||||||
|
// the same key returns the same result.
|
||||||
|
//
|
||||||
|
// Use context values only for request-scoped data that transits
|
||||||
|
// processes and API boundaries, not for passing optional parameters to
|
||||||
|
// functions.
|
||||||
|
Value(key interface{}) interface{}
|
||||||
|
}
|
||||||
11
vendor/github.com/aws/aws-sdk-go/aws/context_1_9.go
generated
vendored
Executable file
11
vendor/github.com/aws/aws-sdk-go/aws/context_1_9.go
generated
vendored
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Context is an alias of the Go stdlib's context.Context interface.
|
||||||
|
// It can be used within the SDK's API operation "WithContext" methods.
|
||||||
|
//
|
||||||
|
// See https://golang.org/pkg/context on how to use contexts.
|
||||||
|
type Context = context.Context
|
||||||
56
vendor/github.com/aws/aws-sdk-go/aws/context_background_1_5.go
generated
vendored
Executable file
56
vendor/github.com/aws/aws-sdk-go/aws/context_background_1_5.go
generated
vendored
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
// +build !go1.7
|
||||||
|
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// An emptyCtx is a copy of the Go 1.7 context.emptyCtx type. This is copied to
|
||||||
|
// provide a 1.6 and 1.5 safe version of context that is compatible with Go
|
||||||
|
// 1.7's Context.
|
||||||
|
//
|
||||||
|
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
||||||
|
// struct{}, since vars of this type must have distinct addresses.
|
||||||
|
type emptyCtx int
|
||||||
|
|
||||||
|
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*emptyCtx) Done() <-chan struct{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*emptyCtx) Err() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*emptyCtx) Value(key interface{}) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *emptyCtx) String() string {
|
||||||
|
switch e {
|
||||||
|
case backgroundCtx:
|
||||||
|
return "aws.BackgroundContext"
|
||||||
|
}
|
||||||
|
return "unknown empty Context"
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
backgroundCtx = new(emptyCtx)
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackgroundContext returns a context that will never be canceled, has no
|
||||||
|
// values, and no deadline. This context is used by the SDK to provide
|
||||||
|
// backwards compatibility with non-context API operations and functionality.
|
||||||
|
//
|
||||||
|
// Go 1.6 and before:
|
||||||
|
// This context function is equivalent to context.Background in the Go stdlib.
|
||||||
|
//
|
||||||
|
// Go 1.7 and later:
|
||||||
|
// The context returned will be the value returned by context.Background()
|
||||||
|
//
|
||||||
|
// See https://golang.org/pkg/context for more information on Contexts.
|
||||||
|
func BackgroundContext() Context {
|
||||||
|
return backgroundCtx
|
||||||
|
}
|
||||||
20
vendor/github.com/aws/aws-sdk-go/aws/context_background_1_7.go
generated
vendored
Executable file
20
vendor/github.com/aws/aws-sdk-go/aws/context_background_1_7.go
generated
vendored
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// BackgroundContext returns a context that will never be canceled, has no
|
||||||
|
// values, and no deadline. This context is used by the SDK to provide
|
||||||
|
// backwards compatibility with non-context API operations and functionality.
|
||||||
|
//
|
||||||
|
// Go 1.6 and before:
|
||||||
|
// This context function is equivalent to context.Background in the Go stdlib.
|
||||||
|
//
|
||||||
|
// Go 1.7 and later:
|
||||||
|
// The context returned will be the value returned by context.Background()
|
||||||
|
//
|
||||||
|
// See https://golang.org/pkg/context for more information on Contexts.
|
||||||
|
func BackgroundContext() Context {
|
||||||
|
return context.Background()
|
||||||
|
}
|
||||||
24
vendor/github.com/aws/aws-sdk-go/aws/context_sleep.go
generated
vendored
Executable file
24
vendor/github.com/aws/aws-sdk-go/aws/context_sleep.go
generated
vendored
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SleepWithContext will wait for the timer duration to expire, or the context
|
||||||
|
// is canceled. Which ever happens first. If the context is canceled the Context's
|
||||||
|
// error will be returned.
|
||||||
|
//
|
||||||
|
// Expects Context to always return a non-nil error if the Done channel is closed.
|
||||||
|
func SleepWithContext(ctx Context, dur time.Duration) error {
|
||||||
|
t := time.NewTimer(dur)
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
break
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
387
vendor/github.com/aws/aws-sdk-go/aws/convert_types.go
generated
vendored
Executable file
387
vendor/github.com/aws/aws-sdk-go/aws/convert_types.go
generated
vendored
Executable file
@@ -0,0 +1,387 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// String returns a pointer to the string value passed in.
|
||||||
|
func String(v string) *string {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValue returns the value of the string pointer passed in or
|
||||||
|
// "" if the pointer is nil.
|
||||||
|
func StringValue(v *string) string {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice converts a slice of string values into a slice of
|
||||||
|
// string pointers
|
||||||
|
func StringSlice(src []string) []*string {
|
||||||
|
dst := make([]*string, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValueSlice converts a slice of string pointers into a slice of
|
||||||
|
// string values
|
||||||
|
func StringValueSlice(src []*string) []string {
|
||||||
|
dst := make([]string, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringMap converts a string map of string values into a string
|
||||||
|
// map of string pointers
|
||||||
|
func StringMap(src map[string]string) map[string]*string {
|
||||||
|
dst := make(map[string]*string)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValueMap converts a string map of string pointers into a string
|
||||||
|
// map of string values
|
||||||
|
func StringValueMap(src map[string]*string) map[string]string {
|
||||||
|
dst := make(map[string]string)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool returns a pointer to the bool value passed in.
|
||||||
|
func Bool(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValue returns the value of the bool pointer passed in or
|
||||||
|
// false if the pointer is nil.
|
||||||
|
func BoolValue(v *bool) bool {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSlice converts a slice of bool values into a slice of
|
||||||
|
// bool pointers
|
||||||
|
func BoolSlice(src []bool) []*bool {
|
||||||
|
dst := make([]*bool, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValueSlice converts a slice of bool pointers into a slice of
|
||||||
|
// bool values
|
||||||
|
func BoolValueSlice(src []*bool) []bool {
|
||||||
|
dst := make([]bool, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolMap converts a string map of bool values into a string
|
||||||
|
// map of bool pointers
|
||||||
|
func BoolMap(src map[string]bool) map[string]*bool {
|
||||||
|
dst := make(map[string]*bool)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValueMap converts a string map of bool pointers into a string
|
||||||
|
// map of bool values
|
||||||
|
func BoolValueMap(src map[string]*bool) map[string]bool {
|
||||||
|
dst := make(map[string]bool)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns a pointer to the int value passed in.
|
||||||
|
func Int(v int) *int {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValue returns the value of the int pointer passed in or
|
||||||
|
// 0 if the pointer is nil.
|
||||||
|
func IntValue(v *int) int {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice converts a slice of int values into a slice of
|
||||||
|
// int pointers
|
||||||
|
func IntSlice(src []int) []*int {
|
||||||
|
dst := make([]*int, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValueSlice converts a slice of int pointers into a slice of
|
||||||
|
// int values
|
||||||
|
func IntValueSlice(src []*int) []int {
|
||||||
|
dst := make([]int, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntMap converts a string map of int values into a string
|
||||||
|
// map of int pointers
|
||||||
|
func IntMap(src map[string]int) map[string]*int {
|
||||||
|
dst := make(map[string]*int)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValueMap converts a string map of int pointers into a string
|
||||||
|
// map of int values
|
||||||
|
func IntValueMap(src map[string]*int) map[string]int {
|
||||||
|
dst := make(map[string]int)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 returns a pointer to the int64 value passed in.
|
||||||
|
func Int64(v int64) *int64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Value returns the value of the int64 pointer passed in or
|
||||||
|
// 0 if the pointer is nil.
|
||||||
|
func Int64Value(v *int64) int64 {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice converts a slice of int64 values into a slice of
|
||||||
|
// int64 pointers
|
||||||
|
func Int64Slice(src []int64) []*int64 {
|
||||||
|
dst := make([]*int64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64ValueSlice converts a slice of int64 pointers into a slice of
|
||||||
|
// int64 values
|
||||||
|
func Int64ValueSlice(src []*int64) []int64 {
|
||||||
|
dst := make([]int64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Map converts a string map of int64 values into a string
|
||||||
|
// map of int64 pointers
|
||||||
|
func Int64Map(src map[string]int64) map[string]*int64 {
|
||||||
|
dst := make(map[string]*int64)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64ValueMap converts a string map of int64 pointers into a string
|
||||||
|
// map of int64 values
|
||||||
|
func Int64ValueMap(src map[string]*int64) map[string]int64 {
|
||||||
|
dst := make(map[string]int64)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 returns a pointer to the float64 value passed in.
|
||||||
|
func Float64(v float64) *float64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Value returns the value of the float64 pointer passed in or
|
||||||
|
// 0 if the pointer is nil.
|
||||||
|
func Float64Value(v *float64) float64 {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Slice converts a slice of float64 values into a slice of
|
||||||
|
// float64 pointers
|
||||||
|
func Float64Slice(src []float64) []*float64 {
|
||||||
|
dst := make([]*float64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64ValueSlice converts a slice of float64 pointers into a slice of
|
||||||
|
// float64 values
|
||||||
|
func Float64ValueSlice(src []*float64) []float64 {
|
||||||
|
dst := make([]float64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Map converts a string map of float64 values into a string
|
||||||
|
// map of float64 pointers
|
||||||
|
func Float64Map(src map[string]float64) map[string]*float64 {
|
||||||
|
dst := make(map[string]*float64)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64ValueMap converts a string map of float64 pointers into a string
|
||||||
|
// map of float64 values
|
||||||
|
func Float64ValueMap(src map[string]*float64) map[string]float64 {
|
||||||
|
dst := make(map[string]float64)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns a pointer to the time.Time value passed in.
|
||||||
|
func Time(v time.Time) *time.Time {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeValue returns the value of the time.Time pointer passed in or
|
||||||
|
// time.Time{} if the pointer is nil.
|
||||||
|
func TimeValue(v *time.Time) time.Time {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecondsTimeValue converts an int64 pointer to a time.Time value
|
||||||
|
// representing seconds since Epoch or time.Time{} if the pointer is nil.
|
||||||
|
func SecondsTimeValue(v *int64) time.Time {
|
||||||
|
if v != nil {
|
||||||
|
return time.Unix((*v / 1000), 0)
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MillisecondsTimeValue converts an int64 pointer to a time.Time value
|
||||||
|
// representing milliseconds sinch Epoch or time.Time{} if the pointer is nil.
|
||||||
|
func MillisecondsTimeValue(v *int64) time.Time {
|
||||||
|
if v != nil {
|
||||||
|
return time.Unix(0, (*v * 1000000))
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeUnixMilli returns a Unix timestamp in milliseconds from "January 1, 1970 UTC".
|
||||||
|
// The result is undefined if the Unix time cannot be represented by an int64.
|
||||||
|
// Which includes calling TimeUnixMilli on a zero Time is undefined.
|
||||||
|
//
|
||||||
|
// This utility is useful for service API's such as CloudWatch Logs which require
|
||||||
|
// their unix time values to be in milliseconds.
|
||||||
|
//
|
||||||
|
// See Go stdlib https://golang.org/pkg/time/#Time.UnixNano for more information.
|
||||||
|
func TimeUnixMilli(t time.Time) int64 {
|
||||||
|
return t.UnixNano() / int64(time.Millisecond/time.Nanosecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeSlice converts a slice of time.Time values into a slice of
|
||||||
|
// time.Time pointers
|
||||||
|
func TimeSlice(src []time.Time) []*time.Time {
|
||||||
|
dst := make([]*time.Time, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeValueSlice converts a slice of time.Time pointers into a slice of
|
||||||
|
// time.Time values
|
||||||
|
func TimeValueSlice(src []*time.Time) []time.Time {
|
||||||
|
dst := make([]time.Time, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeMap converts a string map of time.Time values into a string
|
||||||
|
// map of time.Time pointers
|
||||||
|
func TimeMap(src map[string]time.Time) map[string]*time.Time {
|
||||||
|
dst := make(map[string]*time.Time)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeValueMap converts a string map of time.Time pointers into a string
|
||||||
|
// map of time.Time values
|
||||||
|
func TimeValueMap(src map[string]*time.Time) map[string]time.Time {
|
||||||
|
dst := make(map[string]time.Time)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
228
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go
generated
vendored
Executable file
228
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go
generated
vendored
Executable file
@@ -0,0 +1,228 @@
|
|||||||
|
package corehandlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface for matching types which also have a Len method.
|
||||||
|
type lener interface {
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildContentLengthHandler builds the content length of a request based on the body,
|
||||||
|
// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
|
||||||
|
// to determine request body length and no "Content-Length" was specified it will panic.
|
||||||
|
//
|
||||||
|
// The Content-Length will only be added to the request if the length of the body
|
||||||
|
// is greater than 0. If the body is empty or the current `Content-Length`
|
||||||
|
// header is <= 0, the header will also be stripped.
|
||||||
|
var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
|
||||||
|
var length int64
|
||||||
|
|
||||||
|
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
|
||||||
|
length, _ = strconv.ParseInt(slength, 10, 64)
|
||||||
|
} else {
|
||||||
|
if r.Body != nil {
|
||||||
|
var err error
|
||||||
|
length, err = aws.SeekerLen(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New(request.ErrCodeSerialization, "failed to get request body's length", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > 0 {
|
||||||
|
r.HTTPRequest.ContentLength = length
|
||||||
|
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
|
||||||
|
} else {
|
||||||
|
r.HTTPRequest.ContentLength = 0
|
||||||
|
r.HTTPRequest.Header.Del("Content-Length")
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
var reStatusCode = regexp.MustCompile(`^(\d{3})`)
|
||||||
|
|
||||||
|
// ValidateReqSigHandler is a request handler to ensure that the request's
|
||||||
|
// signature doesn't expire before it is sent. This can happen when a request
|
||||||
|
// is built and signed significantly before it is sent. Or significant delays
|
||||||
|
// occur when retrying requests that would cause the signature to expire.
|
||||||
|
var ValidateReqSigHandler = request.NamedHandler{
|
||||||
|
Name: "core.ValidateReqSigHandler",
|
||||||
|
Fn: func(r *request.Request) {
|
||||||
|
// Unsigned requests are not signed
|
||||||
|
if r.Config.Credentials == credentials.AnonymousCredentials {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
signedTime := r.Time
|
||||||
|
if !r.LastSignedAt.IsZero() {
|
||||||
|
signedTime = r.LastSignedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 minutes to allow for some clock skew/delays in transmission.
|
||||||
|
// Would be improved with aws/aws-sdk-go#423
|
||||||
|
if signedTime.Add(5 * time.Minute).After(time.Now()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("request expired, resigning")
|
||||||
|
r.Sign()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendHandler is a request handler to send service request using HTTP client.
|
||||||
|
var SendHandler = request.NamedHandler{
|
||||||
|
Name: "core.SendHandler",
|
||||||
|
Fn: func(r *request.Request) {
|
||||||
|
sender := sendFollowRedirects
|
||||||
|
if r.DisableFollowRedirects {
|
||||||
|
sender = sendWithoutFollowRedirects
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.NoBody == r.HTTPRequest.Body {
|
||||||
|
// Strip off the request body if the NoBody reader was used as a
|
||||||
|
// place holder for a request body. This prevents the SDK from
|
||||||
|
// making requests with a request body when it would be invalid
|
||||||
|
// to do so.
|
||||||
|
//
|
||||||
|
// Use a shallow copy of the http.Request to ensure the race condition
|
||||||
|
// of transport on Body will not trigger
|
||||||
|
reqOrig, reqCopy := r.HTTPRequest, *r.HTTPRequest
|
||||||
|
reqCopy.Body = nil
|
||||||
|
r.HTTPRequest = &reqCopy
|
||||||
|
defer func() {
|
||||||
|
r.HTTPRequest = reqOrig
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
r.HTTPResponse, err = sender(r)
|
||||||
|
if err != nil {
|
||||||
|
handleSendError(r, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendFollowRedirects(r *request.Request) (*http.Response, error) {
|
||||||
|
return r.Config.HTTPClient.Do(r.HTTPRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendWithoutFollowRedirects(r *request.Request) (*http.Response, error) {
|
||||||
|
transport := r.Config.HTTPClient.Transport
|
||||||
|
if transport == nil {
|
||||||
|
transport = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport.RoundTrip(r.HTTPRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSendError(r *request.Request, err error) {
|
||||||
|
// Prevent leaking if an HTTPResponse was returned. Clean up
|
||||||
|
// the body.
|
||||||
|
if r.HTTPResponse != nil {
|
||||||
|
r.HTTPResponse.Body.Close()
|
||||||
|
}
|
||||||
|
// Capture the case where url.Error is returned for error processing
|
||||||
|
// response. e.g. 301 without location header comes back as string
|
||||||
|
// error and r.HTTPResponse is nil. Other URL redirect errors will
|
||||||
|
// comeback in a similar method.
|
||||||
|
if e, ok := err.(*url.Error); ok && e.Err != nil {
|
||||||
|
if s := reStatusCode.FindStringSubmatch(e.Err.Error()); s != nil {
|
||||||
|
code, _ := strconv.ParseInt(s[1], 10, 64)
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
StatusCode: int(code),
|
||||||
|
Status: http.StatusText(int(code)),
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.HTTPResponse == nil {
|
||||||
|
// Add a dummy request response object to ensure the HTTPResponse
|
||||||
|
// value is consistent.
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
StatusCode: int(0),
|
||||||
|
Status: http.StatusText(int(0)),
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Catch all other request errors.
|
||||||
|
r.Error = awserr.New("RequestError", "send request failed", err)
|
||||||
|
r.Retryable = aws.Bool(true) // network errors are retryable
|
||||||
|
|
||||||
|
// Override the error with a context canceled error, if that was canceled.
|
||||||
|
ctx := r.Context()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
r.Error = awserr.New(request.CanceledErrorCode,
|
||||||
|
"request context canceled", ctx.Err())
|
||||||
|
r.Retryable = aws.Bool(false)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateResponseHandler is a request handler to validate service response.
|
||||||
|
var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseHandler", Fn: func(r *request.Request) {
|
||||||
|
if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 {
|
||||||
|
// this may be replaced by an UnmarshalError handler
|
||||||
|
r.Error = awserr.New("UnknownError", "unknown error", nil)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
// AfterRetryHandler performs final checks to determine if the request should
|
||||||
|
// be retried and how long to delay.
|
||||||
|
var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.Request) {
|
||||||
|
// If one of the other handlers already set the retry state
|
||||||
|
// we don't want to override it based on the service's state
|
||||||
|
if r.Retryable == nil || aws.BoolValue(r.Config.EnforceShouldRetryCheck) {
|
||||||
|
r.Retryable = aws.Bool(r.ShouldRetry(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.WillRetry() {
|
||||||
|
r.RetryDelay = r.RetryRules(r)
|
||||||
|
|
||||||
|
if sleepFn := r.Config.SleepDelay; sleepFn != nil {
|
||||||
|
// Support SleepDelay for backwards compatibility and testing
|
||||||
|
sleepFn(r.RetryDelay)
|
||||||
|
} else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil {
|
||||||
|
r.Error = awserr.New(request.CanceledErrorCode,
|
||||||
|
"request context canceled", err)
|
||||||
|
r.Retryable = aws.Bool(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// when the expired token exception occurs the credentials
|
||||||
|
// need to be expired locally so that the next request to
|
||||||
|
// get credentials will trigger a credentials refresh.
|
||||||
|
if r.IsErrorExpired() {
|
||||||
|
r.Config.Credentials.Expire()
|
||||||
|
}
|
||||||
|
|
||||||
|
r.RetryCount++
|
||||||
|
r.Error = nil
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
// ValidateEndpointHandler is a request handler to validate a request had the
|
||||||
|
// appropriate Region and Endpoint set. Will set r.Error if the endpoint or
|
||||||
|
// region is not valid.
|
||||||
|
var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointHandler", Fn: func(r *request.Request) {
|
||||||
|
if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
|
||||||
|
r.Error = aws.ErrMissingRegion
|
||||||
|
} else if r.ClientInfo.Endpoint == "" {
|
||||||
|
r.Error = aws.ErrMissingEndpoint
|
||||||
|
}
|
||||||
|
}}
|
||||||
17
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go
generated
vendored
Executable file
17
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go
generated
vendored
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
package corehandlers
|
||||||
|
|
||||||
|
import "github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
|
||||||
|
// ValidateParametersHandler is a request handler to validate the input parameters.
|
||||||
|
// Validating parameters only has meaning if done prior to the request being sent.
|
||||||
|
var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) {
|
||||||
|
if !r.ParamsFilled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := r.Params.(request.Validator); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
r.Error = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
37
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/user_agent.go
generated
vendored
Executable file
37
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/user_agent.go
generated
vendored
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
package corehandlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version
|
||||||
|
// to the user agent.
|
||||||
|
var SDKVersionUserAgentHandler = request.NamedHandler{
|
||||||
|
Name: "core.SDKVersionUserAgentHandler",
|
||||||
|
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
|
||||||
|
runtime.Version(), runtime.GOOS, runtime.GOARCH),
|
||||||
|
}
|
||||||
|
|
||||||
|
const execEnvVar = `AWS_EXECUTION_ENV`
|
||||||
|
const execEnvUAKey = `exec-env`
|
||||||
|
|
||||||
|
// AddHostExecEnvUserAgentHander is a request handler appending the SDK's
|
||||||
|
// execution environment to the user agent.
|
||||||
|
//
|
||||||
|
// If the environment variable AWS_EXECUTION_ENV is set, its value will be
|
||||||
|
// appended to the user agent string.
|
||||||
|
var AddHostExecEnvUserAgentHander = request.NamedHandler{
|
||||||
|
Name: "core.AddHostExecEnvUserAgentHander",
|
||||||
|
Fn: func(r *request.Request) {
|
||||||
|
v := os.Getenv(execEnvVar)
|
||||||
|
if len(v) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
request.AddToUserAgent(r, execEnvUAKey+"/"+v)
|
||||||
|
},
|
||||||
|
}
|
||||||
100
vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
Executable file
100
vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNoValidProvidersFoundInChain Is returned when there are no valid
|
||||||
|
// providers in the ChainProvider.
|
||||||
|
//
|
||||||
|
// This has been deprecated. For verbose error messaging set
|
||||||
|
// aws.Config.CredentialsChainVerboseErrors to true.
|
||||||
|
ErrNoValidProvidersFoundInChain = awserr.New("NoCredentialProviders",
|
||||||
|
`no valid providers in chain. Deprecated.
|
||||||
|
For verbose messaging see aws.Config.CredentialsChainVerboseErrors`,
|
||||||
|
nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ChainProvider will search for a provider which returns credentials
|
||||||
|
// and cache that provider until Retrieve is called again.
|
||||||
|
//
|
||||||
|
// The ChainProvider provides a way of chaining multiple providers together
|
||||||
|
// which will pick the first available using priority order of the Providers
|
||||||
|
// in the list.
|
||||||
|
//
|
||||||
|
// If none of the Providers retrieve valid credentials Value, ChainProvider's
|
||||||
|
// Retrieve() will return the error ErrNoValidProvidersFoundInChain.
|
||||||
|
//
|
||||||
|
// If a Provider is found which returns valid credentials Value ChainProvider
|
||||||
|
// will cache that Provider for all calls to IsExpired(), until Retrieve is
|
||||||
|
// called again.
|
||||||
|
//
|
||||||
|
// Example of ChainProvider to be used with an EnvProvider and EC2RoleProvider.
|
||||||
|
// In this example EnvProvider will first check if any credentials are available
|
||||||
|
// via the environment variables. If there are none ChainProvider will check
|
||||||
|
// the next Provider in the list, EC2RoleProvider in this case. If EC2RoleProvider
|
||||||
|
// does not return any credentials ChainProvider will return the error
|
||||||
|
// ErrNoValidProvidersFoundInChain
|
||||||
|
//
|
||||||
|
// creds := credentials.NewChainCredentials(
|
||||||
|
// []credentials.Provider{
|
||||||
|
// &credentials.EnvProvider{},
|
||||||
|
// &ec2rolecreds.EC2RoleProvider{
|
||||||
|
// Client: ec2metadata.New(sess),
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // Usage of ChainCredentials with aws.Config
|
||||||
|
// svc := ec2.New(session.Must(session.NewSession(&aws.Config{
|
||||||
|
// Credentials: creds,
|
||||||
|
// })))
|
||||||
|
//
|
||||||
|
type ChainProvider struct {
|
||||||
|
Providers []Provider
|
||||||
|
curr Provider
|
||||||
|
VerboseErrors bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChainCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping a chain of providers.
|
||||||
|
func NewChainCredentials(providers []Provider) *Credentials {
|
||||||
|
return NewCredentials(&ChainProvider{
|
||||||
|
Providers: append([]Provider{}, providers...),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve returns the credentials value or error if no provider returned
|
||||||
|
// without error.
|
||||||
|
//
|
||||||
|
// If a provider is found it will be cached and any calls to IsExpired()
|
||||||
|
// will return the expired state of the cached provider.
|
||||||
|
func (c *ChainProvider) Retrieve() (Value, error) {
|
||||||
|
var errs []error
|
||||||
|
for _, p := range c.Providers {
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
if err == nil {
|
||||||
|
c.curr = p
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
c.curr = nil
|
||||||
|
|
||||||
|
var err error
|
||||||
|
err = ErrNoValidProvidersFoundInChain
|
||||||
|
if c.VerboseErrors {
|
||||||
|
err = awserr.NewBatchError("NoCredentialProviders", "no valid providers in chain", errs)
|
||||||
|
}
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired will returned the expired state of the currently cached provider
|
||||||
|
// if there is one. If there is no current provider, true will be returned.
|
||||||
|
func (c *ChainProvider) IsExpired() bool {
|
||||||
|
if c.curr != nil {
|
||||||
|
return c.curr.IsExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
292
vendor/github.com/aws/aws-sdk-go/aws/credentials/credentials.go
generated
vendored
Executable file
292
vendor/github.com/aws/aws-sdk-go/aws/credentials/credentials.go
generated
vendored
Executable file
@@ -0,0 +1,292 @@
|
|||||||
|
// Package credentials provides credential retrieval and management
|
||||||
|
//
|
||||||
|
// The Credentials is the primary method of getting access to and managing
|
||||||
|
// credentials Values. Using dependency injection retrieval of the credential
|
||||||
|
// values is handled by a object which satisfies the Provider interface.
|
||||||
|
//
|
||||||
|
// By default the Credentials.Get() will cache the successful result of a
|
||||||
|
// Provider's Retrieve() until Provider.IsExpired() returns true. At which
|
||||||
|
// point Credentials will call Provider's Retrieve() to get new credential Value.
|
||||||
|
//
|
||||||
|
// The Provider is responsible for determining when credentials Value have expired.
|
||||||
|
// It is also important to note that Credentials will always call Retrieve the
|
||||||
|
// first time Credentials.Get() is called.
|
||||||
|
//
|
||||||
|
// Example of using the environment variable credentials.
|
||||||
|
//
|
||||||
|
// creds := credentials.NewEnvCredentials()
|
||||||
|
//
|
||||||
|
// // Retrieve the credentials value
|
||||||
|
// credValue, err := creds.Get()
|
||||||
|
// if err != nil {
|
||||||
|
// // handle error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Example of forcing credentials to expire and be refreshed on the next Get().
|
||||||
|
// This may be helpful to proactively expire credentials and refresh them sooner
|
||||||
|
// than they would naturally expire on their own.
|
||||||
|
//
|
||||||
|
// creds := credentials.NewCredentials(&ec2rolecreds.EC2RoleProvider{})
|
||||||
|
// creds.Expire()
|
||||||
|
// credsValue, err := creds.Get()
|
||||||
|
// // New credentials will be retrieved instead of from cache.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Custom Provider
|
||||||
|
//
|
||||||
|
// Each Provider built into this package also provides a helper method to generate
|
||||||
|
// a Credentials pointer setup with the provider. To use a custom Provider just
|
||||||
|
// create a type which satisfies the Provider interface and pass it to the
|
||||||
|
// NewCredentials method.
|
||||||
|
//
|
||||||
|
// type MyProvider struct{}
|
||||||
|
// func (m *MyProvider) Retrieve() (Value, error) {...}
|
||||||
|
// func (m *MyProvider) IsExpired() bool {...}
|
||||||
|
//
|
||||||
|
// creds := credentials.NewCredentials(&MyProvider{})
|
||||||
|
// credValue, err := creds.Get()
|
||||||
|
//
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnonymousCredentials is an empty Credential object that can be used as
|
||||||
|
// dummy placeholder credentials for requests that do not need signed.
|
||||||
|
//
|
||||||
|
// This Credentials can be used to configure a service to not sign requests
|
||||||
|
// when making service API calls. For example, when accessing public
|
||||||
|
// s3 buckets.
|
||||||
|
//
|
||||||
|
// svc := s3.New(session.Must(session.NewSession(&aws.Config{
|
||||||
|
// Credentials: credentials.AnonymousCredentials,
|
||||||
|
// })))
|
||||||
|
// // Access public S3 buckets.
|
||||||
|
var AnonymousCredentials = NewStaticCredentials("", "", "")
|
||||||
|
|
||||||
|
// A Value is the AWS credentials value for individual credential fields.
|
||||||
|
type Value struct {
|
||||||
|
// AWS Access key ID
|
||||||
|
AccessKeyID string
|
||||||
|
|
||||||
|
// AWS Secret Access Key
|
||||||
|
SecretAccessKey string
|
||||||
|
|
||||||
|
// AWS Session Token
|
||||||
|
SessionToken string
|
||||||
|
|
||||||
|
// Provider used to get credentials
|
||||||
|
ProviderName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Provider is the interface for any component which will provide credentials
|
||||||
|
// Value. A provider is required to manage its own Expired state, and what to
|
||||||
|
// be expired means.
|
||||||
|
//
|
||||||
|
// The Provider should not need to implement its own mutexes, because
|
||||||
|
// that will be managed by Credentials.
|
||||||
|
type Provider interface {
|
||||||
|
// Retrieve returns nil if it successfully retrieved the value.
|
||||||
|
// Error is returned if the value were not obtainable, or empty.
|
||||||
|
Retrieve() (Value, error)
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are no longer valid, and need
|
||||||
|
// to be retrieved.
|
||||||
|
IsExpired() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Expirer is an interface that Providers can implement to expose the expiration
|
||||||
|
// time, if known. If the Provider cannot accurately provide this info,
|
||||||
|
// it should not implement this interface.
|
||||||
|
type Expirer interface {
|
||||||
|
// The time at which the credentials are no longer valid
|
||||||
|
ExpiresAt() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ErrorProvider is a stub credentials provider that always returns an error
|
||||||
|
// this is used by the SDK when construction a known provider is not possible
|
||||||
|
// due to an error.
|
||||||
|
type ErrorProvider struct {
|
||||||
|
// The error to be returned from Retrieve
|
||||||
|
Err error
|
||||||
|
|
||||||
|
// The provider name to set on the Retrieved returned Value
|
||||||
|
ProviderName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve will always return the error that the ErrorProvider was created with.
|
||||||
|
func (p ErrorProvider) Retrieve() (Value, error) {
|
||||||
|
return Value{ProviderName: p.ProviderName}, p.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired will always return not expired.
|
||||||
|
func (p ErrorProvider) IsExpired() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Expiry provides shared expiration logic to be used by credentials
|
||||||
|
// providers to implement expiry functionality.
|
||||||
|
//
|
||||||
|
// The best method to use this struct is as an anonymous field within the
|
||||||
|
// provider's struct.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// type EC2RoleProvider struct {
|
||||||
|
// Expiry
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
type Expiry struct {
|
||||||
|
// The date/time when to expire on
|
||||||
|
expiration time.Time
|
||||||
|
|
||||||
|
// If set will be used by IsExpired to determine the current time.
|
||||||
|
// Defaults to time.Now if CurrentTime is not set. Available for testing
|
||||||
|
// to be able to mock out the current time.
|
||||||
|
CurrentTime func() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExpiration sets the expiration IsExpired will check when called.
|
||||||
|
//
|
||||||
|
// If window is greater than 0 the expiration time will be reduced by the
|
||||||
|
// window value.
|
||||||
|
//
|
||||||
|
// Using a window is helpful to trigger credentials to expire sooner than
|
||||||
|
// the expiration time given to ensure no requests are made with expired
|
||||||
|
// tokens.
|
||||||
|
func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
|
||||||
|
e.expiration = expiration
|
||||||
|
if window > 0 {
|
||||||
|
e.expiration = e.expiration.Add(-window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are expired.
|
||||||
|
func (e *Expiry) IsExpired() bool {
|
||||||
|
curTime := e.CurrentTime
|
||||||
|
if curTime == nil {
|
||||||
|
curTime = time.Now
|
||||||
|
}
|
||||||
|
return e.expiration.Before(curTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpiresAt returns the expiration time of the credential
|
||||||
|
func (e *Expiry) ExpiresAt() time.Time {
|
||||||
|
return e.expiration
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Credentials provides concurrency safe retrieval of AWS credentials Value.
|
||||||
|
// Credentials will cache the credentials value until they expire. Once the value
|
||||||
|
// expires the next Get will attempt to retrieve valid credentials.
|
||||||
|
//
|
||||||
|
// Credentials is safe to use across multiple goroutines and will manage the
|
||||||
|
// synchronous state so the Providers do not need to implement their own
|
||||||
|
// synchronization.
|
||||||
|
//
|
||||||
|
// The first Credentials.Get() will always call Provider.Retrieve() to get the
|
||||||
|
// first instance of the credentials Value. All calls to Get() after that
|
||||||
|
// will return the cached credentials Value until IsExpired() returns true.
|
||||||
|
type Credentials struct {
|
||||||
|
creds Value
|
||||||
|
forceRefresh bool
|
||||||
|
|
||||||
|
m sync.RWMutex
|
||||||
|
|
||||||
|
provider Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentials returns a pointer to a new Credentials with the provider set.
|
||||||
|
func NewCredentials(provider Provider) *Credentials {
|
||||||
|
return &Credentials{
|
||||||
|
provider: provider,
|
||||||
|
forceRefresh: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the credentials value, or error if the credentials Value failed
|
||||||
|
// to be retrieved.
|
||||||
|
//
|
||||||
|
// Will return the cached credentials Value if it has not expired. If the
|
||||||
|
// credentials Value has expired the Provider's Retrieve() will be called
|
||||||
|
// to refresh the credentials.
|
||||||
|
//
|
||||||
|
// If Credentials.Expire() was called the credentials Value will be force
|
||||||
|
// expired, and the next call to Get() will cause them to be refreshed.
|
||||||
|
func (c *Credentials) Get() (Value, error) {
|
||||||
|
// Check the cached credentials first with just the read lock.
|
||||||
|
c.m.RLock()
|
||||||
|
if !c.isExpired() {
|
||||||
|
creds := c.creds
|
||||||
|
c.m.RUnlock()
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
c.m.RUnlock()
|
||||||
|
|
||||||
|
// Credentials are expired need to retrieve the credentials taking the full
|
||||||
|
// lock.
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
if c.isExpired() {
|
||||||
|
creds, err := c.provider.Retrieve()
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
c.creds = creds
|
||||||
|
c.forceRefresh = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire expires the credentials and forces them to be retrieved on the
|
||||||
|
// next call to Get().
|
||||||
|
//
|
||||||
|
// This will override the Provider's expired state, and force Credentials
|
||||||
|
// to call the Provider's Retrieve().
|
||||||
|
func (c *Credentials) Expire() {
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
c.forceRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are no longer valid, and need
|
||||||
|
// to be retrieved.
|
||||||
|
//
|
||||||
|
// If the Credentials were forced to be expired with Expire() this will
|
||||||
|
// reflect that override.
|
||||||
|
func (c *Credentials) IsExpired() bool {
|
||||||
|
c.m.RLock()
|
||||||
|
defer c.m.RUnlock()
|
||||||
|
|
||||||
|
return c.isExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExpired helper method wrapping the definition of expired credentials.
|
||||||
|
func (c *Credentials) isExpired() bool {
|
||||||
|
return c.forceRefresh || c.provider.IsExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpiresAt provides access to the functionality of the Expirer interface of
|
||||||
|
// the underlying Provider, if it supports that interface. Otherwise, it returns
|
||||||
|
// an error.
|
||||||
|
func (c *Credentials) ExpiresAt() (time.Time, error) {
|
||||||
|
c.m.RLock()
|
||||||
|
defer c.m.RUnlock()
|
||||||
|
|
||||||
|
expirer, ok := c.provider.(Expirer)
|
||||||
|
if !ok {
|
||||||
|
return time.Time{}, awserr.New("ProviderNotExpirer",
|
||||||
|
fmt.Sprintf("provider %s does not support ExpiresAt()", c.creds.ProviderName),
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
if c.forceRefresh {
|
||||||
|
// set expiration time to the distant past
|
||||||
|
return time.Time{}, nil
|
||||||
|
}
|
||||||
|
return expirer.ExpiresAt(), nil
|
||||||
|
}
|
||||||
178
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
Executable file
178
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
Executable file
@@ -0,0 +1,178 @@
|
|||||||
|
package ec2rolecreds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProviderName provides a name of EC2Role provider
|
||||||
|
const ProviderName = "EC2RoleProvider"
|
||||||
|
|
||||||
|
// A EC2RoleProvider retrieves credentials from the EC2 service, and keeps track if
|
||||||
|
// those credentials are expired.
|
||||||
|
//
|
||||||
|
// Example how to configure the EC2RoleProvider with custom http Client, Endpoint
|
||||||
|
// or ExpiryWindow
|
||||||
|
//
|
||||||
|
// p := &ec2rolecreds.EC2RoleProvider{
|
||||||
|
// // Pass in a custom timeout to be used when requesting
|
||||||
|
// // IAM EC2 Role credentials.
|
||||||
|
// Client: ec2metadata.New(sess, aws.Config{
|
||||||
|
// HTTPClient: &http.Client{Timeout: 10 * time.Second},
|
||||||
|
// }),
|
||||||
|
//
|
||||||
|
// // Do not use early expiry of credentials. If a non zero value is
|
||||||
|
// // specified the credentials will be expired early
|
||||||
|
// ExpiryWindow: 0,
|
||||||
|
// }
|
||||||
|
type EC2RoleProvider struct {
|
||||||
|
credentials.Expiry
|
||||||
|
|
||||||
|
// Required EC2Metadata client to use when connecting to EC2 metadata service.
|
||||||
|
Client *ec2metadata.EC2Metadata
|
||||||
|
|
||||||
|
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||||
|
// the credentials actually expiring. This is beneficial so race conditions
|
||||||
|
// with expiring credentials do not cause request to fail unexpectedly
|
||||||
|
// due to ExpiredTokenException exceptions.
|
||||||
|
//
|
||||||
|
// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||||
|
// 10 seconds before the credentials are actually expired.
|
||||||
|
//
|
||||||
|
// If ExpiryWindow is 0 or less it will be ignored.
|
||||||
|
ExpiryWindow time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentials returns a pointer to a new Credentials object wrapping
|
||||||
|
// the EC2RoleProvider. Takes a ConfigProvider to create a EC2Metadata client.
|
||||||
|
// The ConfigProvider is satisfied by the session.Session type.
|
||||||
|
func NewCredentials(c client.ConfigProvider, options ...func(*EC2RoleProvider)) *credentials.Credentials {
|
||||||
|
p := &EC2RoleProvider{
|
||||||
|
Client: ec2metadata.New(c),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsWithClient returns a pointer to a new Credentials object wrapping
|
||||||
|
// the EC2RoleProvider. Takes a EC2Metadata client to use when connecting to EC2
|
||||||
|
// metadata service.
|
||||||
|
func NewCredentialsWithClient(client *ec2metadata.EC2Metadata, options ...func(*EC2RoleProvider)) *credentials.Credentials {
|
||||||
|
p := &EC2RoleProvider{
|
||||||
|
Client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve retrieves credentials from the EC2 service.
|
||||||
|
// Error will be returned if the request fails, or unable to extract
|
||||||
|
// the desired credentials.
|
||||||
|
func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
|
||||||
|
credsList, err := requestCredList(m.Client)
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(credsList) == 0 {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, awserr.New("EmptyEC2RoleList", "empty EC2 Role list", nil)
|
||||||
|
}
|
||||||
|
credsName := credsList[0]
|
||||||
|
|
||||||
|
roleCreds, err := requestCred(m.Client, credsName)
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetExpiration(roleCreds.Expiration, m.ExpiryWindow)
|
||||||
|
|
||||||
|
return credentials.Value{
|
||||||
|
AccessKeyID: roleCreds.AccessKeyID,
|
||||||
|
SecretAccessKey: roleCreds.SecretAccessKey,
|
||||||
|
SessionToken: roleCreds.Token,
|
||||||
|
ProviderName: ProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ec2RoleCredRespBody provides the shape for unmarshaling credential
|
||||||
|
// request responses.
|
||||||
|
type ec2RoleCredRespBody struct {
|
||||||
|
// Success State
|
||||||
|
Expiration time.Time
|
||||||
|
AccessKeyID string
|
||||||
|
SecretAccessKey string
|
||||||
|
Token string
|
||||||
|
|
||||||
|
// Error state
|
||||||
|
Code string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
const iamSecurityCredsPath = "iam/security-credentials/"
|
||||||
|
|
||||||
|
// requestCredList requests a list of credentials from the EC2 service.
|
||||||
|
// If there are no credentials, or there is an error making or receiving the request
|
||||||
|
func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
|
||||||
|
resp, err := client.GetMetadata(iamSecurityCredsPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, awserr.New("EC2RoleRequestError", "no EC2 instance role found", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
credsList := []string{}
|
||||||
|
s := bufio.NewScanner(strings.NewReader(resp))
|
||||||
|
for s.Scan() {
|
||||||
|
credsList = append(credsList, s.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, awserr.New("SerializationError", "failed to read EC2 instance role from metadata service", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credsList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestCred requests the credentials for a specific credentials from the EC2 service.
|
||||||
|
//
|
||||||
|
// If the credentials cannot be found, or there is an error reading the response
|
||||||
|
// and error will be returned.
|
||||||
|
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
|
||||||
|
resp, err := client.GetMetadata(sdkuri.PathJoin(iamSecurityCredsPath, credsName))
|
||||||
|
if err != nil {
|
||||||
|
return ec2RoleCredRespBody{},
|
||||||
|
awserr.New("EC2RoleRequestError",
|
||||||
|
fmt.Sprintf("failed to get %s EC2 instance role credentials", credsName),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
respCreds := ec2RoleCredRespBody{}
|
||||||
|
if err := json.NewDecoder(strings.NewReader(resp)).Decode(&respCreds); err != nil {
|
||||||
|
return ec2RoleCredRespBody{},
|
||||||
|
awserr.New("SerializationError",
|
||||||
|
fmt.Sprintf("failed to decode %s EC2 instance role credentials", credsName),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if respCreds.Code != "Success" {
|
||||||
|
// If an error code was returned something failed requesting the role.
|
||||||
|
return ec2RoleCredRespBody{}, awserr.New(respCreds.Code, respCreds.Message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return respCreds, nil
|
||||||
|
}
|
||||||
198
vendor/github.com/aws/aws-sdk-go/aws/credentials/endpointcreds/provider.go
generated
vendored
Executable file
198
vendor/github.com/aws/aws-sdk-go/aws/credentials/endpointcreds/provider.go
generated
vendored
Executable file
@@ -0,0 +1,198 @@
|
|||||||
|
// Package endpointcreds provides support for retrieving credentials from an
|
||||||
|
// arbitrary HTTP endpoint.
|
||||||
|
//
|
||||||
|
// The credentials endpoint Provider can receive both static and refreshable
|
||||||
|
// credentials that will expire. Credentials are static when an "Expiration"
|
||||||
|
// value is not provided in the endpoint's response.
|
||||||
|
//
|
||||||
|
// Static credentials will never expire once they have been retrieved. The format
|
||||||
|
// of the static credentials response:
|
||||||
|
// {
|
||||||
|
// "AccessKeyId" : "MUA...",
|
||||||
|
// "SecretAccessKey" : "/7PC5om....",
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Refreshable credentials will expire within the "ExpiryWindow" of the Expiration
|
||||||
|
// value in the response. The format of the refreshable credentials response:
|
||||||
|
// {
|
||||||
|
// "AccessKeyId" : "MUA...",
|
||||||
|
// "SecretAccessKey" : "/7PC5om....",
|
||||||
|
// "Token" : "AQoDY....=",
|
||||||
|
// "Expiration" : "2016-02-25T06:03:31Z"
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Errors should be returned in the following format and only returned with 400
|
||||||
|
// or 500 HTTP status codes.
|
||||||
|
// {
|
||||||
|
// "code": "ErrorCode",
|
||||||
|
// "message": "Helpful error message."
|
||||||
|
// }
|
||||||
|
package endpointcreds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProviderName is the name of the credentials provider.
|
||||||
|
const ProviderName = `CredentialsEndpointProvider`
|
||||||
|
|
||||||
|
// Provider satisfies the credentials.Provider interface, and is a client to
|
||||||
|
// retrieve credentials from an arbitrary endpoint.
|
||||||
|
type Provider struct {
|
||||||
|
staticCreds bool
|
||||||
|
credentials.Expiry
|
||||||
|
|
||||||
|
// Requires a AWS Client to make HTTP requests to the endpoint with.
|
||||||
|
// the Endpoint the request will be made to is provided by the aws.Config's
|
||||||
|
// Endpoint value.
|
||||||
|
Client *client.Client
|
||||||
|
|
||||||
|
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||||
|
// the credentials actually expiring. This is beneficial so race conditions
|
||||||
|
// with expiring credentials do not cause request to fail unexpectedly
|
||||||
|
// due to ExpiredTokenException exceptions.
|
||||||
|
//
|
||||||
|
// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||||
|
// 10 seconds before the credentials are actually expired.
|
||||||
|
//
|
||||||
|
// If ExpiryWindow is 0 or less it will be ignored.
|
||||||
|
ExpiryWindow time.Duration
|
||||||
|
|
||||||
|
// Optional authorization token value if set will be used as the value of
|
||||||
|
// the Authorization header of the endpoint credential request.
|
||||||
|
AuthorizationToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProviderClient returns a credentials Provider for retrieving AWS credentials
|
||||||
|
// from arbitrary endpoint.
|
||||||
|
func NewProviderClient(cfg aws.Config, handlers request.Handlers, endpoint string, options ...func(*Provider)) credentials.Provider {
|
||||||
|
p := &Provider{
|
||||||
|
Client: client.New(
|
||||||
|
cfg,
|
||||||
|
metadata.ClientInfo{
|
||||||
|
ServiceName: "CredentialsEndpoint",
|
||||||
|
Endpoint: endpoint,
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Client.Handlers.Unmarshal.PushBack(unmarshalHandler)
|
||||||
|
p.Client.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
p.Client.Handlers.Validate.Clear()
|
||||||
|
p.Client.Handlers.Validate.PushBack(validateEndpointHandler)
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsClient returns a Credentials wrapper for retrieving credentials
|
||||||
|
// from an arbitrary endpoint concurrently. The client will request the
|
||||||
|
func NewCredentialsClient(cfg aws.Config, handlers request.Handlers, endpoint string, options ...func(*Provider)) *credentials.Credentials {
|
||||||
|
return credentials.NewCredentials(NewProviderClient(cfg, handlers, endpoint, options...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns true if the credentials retrieved are expired, or not yet
|
||||||
|
// retrieved.
|
||||||
|
func (p *Provider) IsExpired() bool {
|
||||||
|
if p.staticCreds {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.Expiry.IsExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve will attempt to request the credentials from the endpoint the Provider
|
||||||
|
// was configured for. And error will be returned if the retrieval fails.
|
||||||
|
func (p *Provider) Retrieve() (credentials.Value, error) {
|
||||||
|
resp, err := p.getCredentials()
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName},
|
||||||
|
awserr.New("CredentialsEndpointError", "failed to load credentials", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Expiration != nil {
|
||||||
|
p.SetExpiration(*resp.Expiration, p.ExpiryWindow)
|
||||||
|
} else {
|
||||||
|
p.staticCreds = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.Value{
|
||||||
|
AccessKeyID: resp.AccessKeyID,
|
||||||
|
SecretAccessKey: resp.SecretAccessKey,
|
||||||
|
SessionToken: resp.Token,
|
||||||
|
ProviderName: ProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type getCredentialsOutput struct {
|
||||||
|
Expiration *time.Time
|
||||||
|
AccessKeyID string
|
||||||
|
SecretAccessKey string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorOutput struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) getCredentials() (*getCredentialsOutput, error) {
|
||||||
|
op := &request.Operation{
|
||||||
|
Name: "GetCredentials",
|
||||||
|
HTTPMethod: "GET",
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &getCredentialsOutput{}
|
||||||
|
req := p.Client.NewRequest(op, nil, out)
|
||||||
|
req.HTTPRequest.Header.Set("Accept", "application/json")
|
||||||
|
if authToken := p.AuthorizationToken; len(authToken) != 0 {
|
||||||
|
req.HTTPRequest.Header.Set("Authorization", authToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, req.Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateEndpointHandler(r *request.Request) {
|
||||||
|
if len(r.ClientInfo.Endpoint) == 0 {
|
||||||
|
r.Error = aws.ErrMissingEndpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalHandler(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
|
||||||
|
out := r.Data.(*getCredentialsOutput)
|
||||||
|
if err := json.NewDecoder(r.HTTPResponse.Body).Decode(&out); err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError",
|
||||||
|
"failed to decode endpoint credentials",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalError(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
|
||||||
|
var errOut errorOutput
|
||||||
|
if err := json.NewDecoder(r.HTTPResponse.Body).Decode(&errOut); err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError",
|
||||||
|
"failed to decode endpoint credentials",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response body format is not consistent between metadata endpoints.
|
||||||
|
// Grab the error message as a string and include that as the source error
|
||||||
|
r.Error = awserr.New(errOut.Code, errOut.Message, nil)
|
||||||
|
}
|
||||||
74
vendor/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go
generated
vendored
Executable file
74
vendor/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go
generated
vendored
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnvProviderName provides a name of Env provider
|
||||||
|
const EnvProviderName = "EnvProvider"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrAccessKeyIDNotFound is returned when the AWS Access Key ID can't be
|
||||||
|
// found in the process's environment.
|
||||||
|
ErrAccessKeyIDNotFound = awserr.New("EnvAccessKeyNotFound", "AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment", nil)
|
||||||
|
|
||||||
|
// ErrSecretAccessKeyNotFound is returned when the AWS Secret Access Key
|
||||||
|
// can't be found in the process's environment.
|
||||||
|
ErrSecretAccessKeyNotFound = awserr.New("EnvSecretNotFound", "AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A EnvProvider retrieves credentials from the environment variables of the
|
||||||
|
// running process. Environment credentials never expire.
|
||||||
|
//
|
||||||
|
// Environment variables used:
|
||||||
|
//
|
||||||
|
// * Access Key ID: AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY
|
||||||
|
//
|
||||||
|
// * Secret Access Key: AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY
|
||||||
|
type EnvProvider struct {
|
||||||
|
retrieved bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnvCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping the environment variable provider.
|
||||||
|
func NewEnvCredentials() *Credentials {
|
||||||
|
return NewCredentials(&EnvProvider{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve retrieves the keys from the environment.
|
||||||
|
func (e *EnvProvider) Retrieve() (Value, error) {
|
||||||
|
e.retrieved = false
|
||||||
|
|
||||||
|
id := os.Getenv("AWS_ACCESS_KEY_ID")
|
||||||
|
if id == "" {
|
||||||
|
id = os.Getenv("AWS_ACCESS_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||||
|
if secret == "" {
|
||||||
|
secret = os.Getenv("AWS_SECRET_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
if id == "" {
|
||||||
|
return Value{ProviderName: EnvProviderName}, ErrAccessKeyIDNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret == "" {
|
||||||
|
return Value{ProviderName: EnvProviderName}, ErrSecretAccessKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
e.retrieved = true
|
||||||
|
return Value{
|
||||||
|
AccessKeyID: id,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
SessionToken: os.Getenv("AWS_SESSION_TOKEN"),
|
||||||
|
ProviderName: EnvProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials have been retrieved.
|
||||||
|
func (e *EnvProvider) IsExpired() bool {
|
||||||
|
return !e.retrieved
|
||||||
|
}
|
||||||
12
vendor/github.com/aws/aws-sdk-go/aws/credentials/example.ini
generated
vendored
Executable file
12
vendor/github.com/aws/aws-sdk-go/aws/credentials/example.ini
generated
vendored
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
[default]
|
||||||
|
aws_access_key_id = accessKey
|
||||||
|
aws_secret_access_key = secret
|
||||||
|
aws_session_token = token
|
||||||
|
|
||||||
|
[no_token]
|
||||||
|
aws_access_key_id = accessKey
|
||||||
|
aws_secret_access_key = secret
|
||||||
|
|
||||||
|
[with_colon]
|
||||||
|
aws_access_key_id: accessKey
|
||||||
|
aws_secret_access_key: secret
|
||||||
425
vendor/github.com/aws/aws-sdk-go/aws/credentials/processcreds/provider.go
generated
vendored
Executable file
425
vendor/github.com/aws/aws-sdk-go/aws/credentials/processcreds/provider.go
generated
vendored
Executable file
@@ -0,0 +1,425 @@
|
|||||||
|
/*
|
||||||
|
Package processcreds is a credential Provider to retrieve `credential_process`
|
||||||
|
credentials.
|
||||||
|
|
||||||
|
WARNING: The following describes a method of sourcing credentials from an external
|
||||||
|
process. This can potentially be dangerous, so proceed with caution. Other
|
||||||
|
credential providers should be preferred if at all possible. If using this
|
||||||
|
option, you should make sure that the config file is as locked down as possible
|
||||||
|
using security best practices for your operating system.
|
||||||
|
|
||||||
|
You can use credentials from a `credential_process` in a variety of ways.
|
||||||
|
|
||||||
|
One way is to setup your shared config file, located in the default
|
||||||
|
location, with the `credential_process` key and the command you want to be
|
||||||
|
called. You also need to set the AWS_SDK_LOAD_CONFIG environment variable
|
||||||
|
(e.g., `export AWS_SDK_LOAD_CONFIG=1`) to use the shared config file.
|
||||||
|
|
||||||
|
[default]
|
||||||
|
credential_process = /command/to/call
|
||||||
|
|
||||||
|
Creating a new session will use the credential process to retrieve credentials.
|
||||||
|
NOTE: If there are credentials in the profile you are using, the credential
|
||||||
|
process will not be used.
|
||||||
|
|
||||||
|
// Initialize a session to load credentials.
|
||||||
|
sess, _ := session.NewSession(&aws.Config{
|
||||||
|
Region: aws.String("us-east-1")},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create S3 service client to use the credentials.
|
||||||
|
svc := s3.New(sess)
|
||||||
|
|
||||||
|
Another way to use the `credential_process` method is by using
|
||||||
|
`credentials.NewCredentials()` and providing a command to be executed to
|
||||||
|
retrieve credentials:
|
||||||
|
|
||||||
|
// Create credentials using the ProcessProvider.
|
||||||
|
creds := processcreds.NewCredentials("/path/to/command")
|
||||||
|
|
||||||
|
// Create service client value configured for credentials.
|
||||||
|
svc := s3.New(sess, &aws.Config{Credentials: creds})
|
||||||
|
|
||||||
|
You can set a non-default timeout for the `credential_process` with another
|
||||||
|
constructor, `credentials.NewCredentialsTimeout()`, providing the timeout. To
|
||||||
|
set a one minute timeout:
|
||||||
|
|
||||||
|
// Create credentials using the ProcessProvider.
|
||||||
|
creds := processcreds.NewCredentialsTimeout(
|
||||||
|
"/path/to/command",
|
||||||
|
time.Duration(500) * time.Millisecond)
|
||||||
|
|
||||||
|
If you need more control, you can set any configurable options in the
|
||||||
|
credentials using one or more option functions. For example, you can set a two
|
||||||
|
minute timeout, a credential duration of 60 minutes, and a maximum stdout
|
||||||
|
buffer size of 2k.
|
||||||
|
|
||||||
|
creds := processcreds.NewCredentials(
|
||||||
|
"/path/to/command",
|
||||||
|
func(opt *ProcessProvider) {
|
||||||
|
opt.Timeout = time.Duration(2) * time.Minute
|
||||||
|
opt.Duration = time.Duration(60) * time.Minute
|
||||||
|
opt.MaxBufSize = 2048
|
||||||
|
})
|
||||||
|
|
||||||
|
You can also use your own `exec.Cmd`:
|
||||||
|
|
||||||
|
// Create an exec.Cmd
|
||||||
|
myCommand := exec.Command("/path/to/command")
|
||||||
|
|
||||||
|
// Create credentials using your exec.Cmd and custom timeout
|
||||||
|
creds := processcreds.NewCredentialsCommand(
|
||||||
|
myCommand,
|
||||||
|
func(opt *processcreds.ProcessProvider) {
|
||||||
|
opt.Timeout = time.Duration(1) * time.Second
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
package processcreds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ProviderName is the name this credentials provider will label any
|
||||||
|
// returned credentials Value with.
|
||||||
|
ProviderName = `ProcessProvider`
|
||||||
|
|
||||||
|
// ErrCodeProcessProviderParse error parsing process output
|
||||||
|
ErrCodeProcessProviderParse = "ProcessProviderParseError"
|
||||||
|
|
||||||
|
// ErrCodeProcessProviderVersion version error in output
|
||||||
|
ErrCodeProcessProviderVersion = "ProcessProviderVersionError"
|
||||||
|
|
||||||
|
// ErrCodeProcessProviderRequired required attribute missing in output
|
||||||
|
ErrCodeProcessProviderRequired = "ProcessProviderRequiredError"
|
||||||
|
|
||||||
|
// ErrCodeProcessProviderExecution execution of command failed
|
||||||
|
ErrCodeProcessProviderExecution = "ProcessProviderExecutionError"
|
||||||
|
|
||||||
|
// errMsgProcessProviderTimeout process took longer than allowed
|
||||||
|
errMsgProcessProviderTimeout = "credential process timed out"
|
||||||
|
|
||||||
|
// errMsgProcessProviderProcess process error
|
||||||
|
errMsgProcessProviderProcess = "error in credential_process"
|
||||||
|
|
||||||
|
// errMsgProcessProviderParse problem parsing output
|
||||||
|
errMsgProcessProviderParse = "parse failed of credential_process output"
|
||||||
|
|
||||||
|
// errMsgProcessProviderVersion version error in output
|
||||||
|
errMsgProcessProviderVersion = "wrong version in process output (not 1)"
|
||||||
|
|
||||||
|
// errMsgProcessProviderMissKey missing access key id in output
|
||||||
|
errMsgProcessProviderMissKey = "missing AccessKeyId in process output"
|
||||||
|
|
||||||
|
// errMsgProcessProviderMissSecret missing secret acess key in output
|
||||||
|
errMsgProcessProviderMissSecret = "missing SecretAccessKey in process output"
|
||||||
|
|
||||||
|
// errMsgProcessProviderPrepareCmd prepare of command failed
|
||||||
|
errMsgProcessProviderPrepareCmd = "failed to prepare command"
|
||||||
|
|
||||||
|
// errMsgProcessProviderEmptyCmd command must not be empty
|
||||||
|
errMsgProcessProviderEmptyCmd = "command must not be empty"
|
||||||
|
|
||||||
|
// errMsgProcessProviderPipe failed to initialize pipe
|
||||||
|
errMsgProcessProviderPipe = "failed to initialize pipe"
|
||||||
|
|
||||||
|
// DefaultDuration is the default amount of time in minutes that the
|
||||||
|
// credentials will be valid for.
|
||||||
|
DefaultDuration = time.Duration(15) * time.Minute
|
||||||
|
|
||||||
|
// DefaultBufSize limits buffer size from growing to an enormous
|
||||||
|
// amount due to a faulty process.
|
||||||
|
DefaultBufSize = 1024
|
||||||
|
|
||||||
|
// DefaultTimeout default limit on time a process can run.
|
||||||
|
DefaultTimeout = time.Duration(1) * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessProvider satisfies the credentials.Provider interface, and is a
|
||||||
|
// client to retrieve credentials from a process.
|
||||||
|
type ProcessProvider struct {
|
||||||
|
staticCreds bool
|
||||||
|
credentials.Expiry
|
||||||
|
originalCommand []string
|
||||||
|
|
||||||
|
// Expiry duration of the credentials. Defaults to 15 minutes if not set.
|
||||||
|
Duration time.Duration
|
||||||
|
|
||||||
|
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||||
|
// the credentials actually expiring. This is beneficial so race conditions
|
||||||
|
// with expiring credentials do not cause request to fail unexpectedly
|
||||||
|
// due to ExpiredTokenException exceptions.
|
||||||
|
//
|
||||||
|
// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||||
|
// 10 seconds before the credentials are actually expired.
|
||||||
|
//
|
||||||
|
// If ExpiryWindow is 0 or less it will be ignored.
|
||||||
|
ExpiryWindow time.Duration
|
||||||
|
|
||||||
|
// A string representing an os command that should return a JSON with
|
||||||
|
// credential information.
|
||||||
|
command *exec.Cmd
|
||||||
|
|
||||||
|
// MaxBufSize limits memory usage from growing to an enormous
|
||||||
|
// amount due to a faulty process.
|
||||||
|
MaxBufSize int
|
||||||
|
|
||||||
|
// Timeout limits the time a process can run.
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentials returns a pointer to a new Credentials object wrapping the
|
||||||
|
// ProcessProvider. The credentials will expire every 15 minutes by default.
|
||||||
|
func NewCredentials(command string, options ...func(*ProcessProvider)) *credentials.Credentials {
|
||||||
|
p := &ProcessProvider{
|
||||||
|
command: exec.Command(command),
|
||||||
|
Duration: DefaultDuration,
|
||||||
|
Timeout: DefaultTimeout,
|
||||||
|
MaxBufSize: DefaultBufSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsTimeout returns a pointer to a new Credentials object with
|
||||||
|
// the specified command and timeout, and default duration and max buffer size.
|
||||||
|
func NewCredentialsTimeout(command string, timeout time.Duration) *credentials.Credentials {
|
||||||
|
p := NewCredentials(command, func(opt *ProcessProvider) {
|
||||||
|
opt.Timeout = timeout
|
||||||
|
})
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsCommand returns a pointer to a new Credentials object with
|
||||||
|
// the specified command, and default timeout, duration and max buffer size.
|
||||||
|
func NewCredentialsCommand(command *exec.Cmd, options ...func(*ProcessProvider)) *credentials.Credentials {
|
||||||
|
p := &ProcessProvider{
|
||||||
|
command: command,
|
||||||
|
Duration: DefaultDuration,
|
||||||
|
Timeout: DefaultTimeout,
|
||||||
|
MaxBufSize: DefaultBufSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
type credentialProcessResponse struct {
|
||||||
|
Version int
|
||||||
|
AccessKeyID string `json:"AccessKeyId"`
|
||||||
|
SecretAccessKey string
|
||||||
|
SessionToken string
|
||||||
|
Expiration *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve executes the 'credential_process' and returns the credentials.
|
||||||
|
func (p *ProcessProvider) Retrieve() (credentials.Value, error) {
|
||||||
|
out, err := p.executeCredentialProcess()
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize and validate response
|
||||||
|
resp := &credentialProcessResponse{}
|
||||||
|
if err = json.Unmarshal(out, resp); err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, awserr.New(
|
||||||
|
ErrCodeProcessProviderParse,
|
||||||
|
fmt.Sprintf("%s: %s", errMsgProcessProviderParse, string(out)),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Version != 1 {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, awserr.New(
|
||||||
|
ErrCodeProcessProviderVersion,
|
||||||
|
errMsgProcessProviderVersion,
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.AccessKeyID) == 0 {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, awserr.New(
|
||||||
|
ErrCodeProcessProviderRequired,
|
||||||
|
errMsgProcessProviderMissKey,
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.SecretAccessKey) == 0 {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, awserr.New(
|
||||||
|
ErrCodeProcessProviderRequired,
|
||||||
|
errMsgProcessProviderMissSecret,
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle expiration
|
||||||
|
p.staticCreds = resp.Expiration == nil
|
||||||
|
if resp.Expiration != nil {
|
||||||
|
p.SetExpiration(*resp.Expiration, p.ExpiryWindow)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.Value{
|
||||||
|
ProviderName: ProviderName,
|
||||||
|
AccessKeyID: resp.AccessKeyID,
|
||||||
|
SecretAccessKey: resp.SecretAccessKey,
|
||||||
|
SessionToken: resp.SessionToken,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns true if the credentials retrieved are expired, or not yet
|
||||||
|
// retrieved.
|
||||||
|
func (p *ProcessProvider) IsExpired() bool {
|
||||||
|
if p.staticCreds {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.Expiry.IsExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareCommand prepares the command to be executed.
|
||||||
|
func (p *ProcessProvider) prepareCommand() error {
|
||||||
|
|
||||||
|
var cmdArgs []string
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cmdArgs = []string{"cmd.exe", "/C"}
|
||||||
|
} else {
|
||||||
|
cmdArgs = []string{"sh", "-c"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.originalCommand) == 0 {
|
||||||
|
p.originalCommand = make([]string, len(p.command.Args))
|
||||||
|
copy(p.originalCommand, p.command.Args)
|
||||||
|
|
||||||
|
// check for empty command because it succeeds
|
||||||
|
if len(strings.TrimSpace(p.originalCommand[0])) < 1 {
|
||||||
|
return awserr.New(
|
||||||
|
ErrCodeProcessProviderExecution,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%s: %s",
|
||||||
|
errMsgProcessProviderPrepareCmd,
|
||||||
|
errMsgProcessProviderEmptyCmd),
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdArgs = append(cmdArgs, p.originalCommand...)
|
||||||
|
p.command = exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||||
|
p.command.Env = os.Environ()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeCredentialProcess starts the credential process on the OS and
|
||||||
|
// returns the results or an error.
|
||||||
|
func (p *ProcessProvider) executeCredentialProcess() ([]byte, error) {
|
||||||
|
|
||||||
|
if err := p.prepareCommand(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the pipes
|
||||||
|
outReadPipe, outWritePipe, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, awserr.New(
|
||||||
|
ErrCodeProcessProviderExecution,
|
||||||
|
errMsgProcessProviderPipe,
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.command.Stderr = os.Stderr // display stderr on console for MFA
|
||||||
|
p.command.Stdout = outWritePipe // get creds json on process's stdout
|
||||||
|
p.command.Stdin = os.Stdin // enable stdin for MFA
|
||||||
|
|
||||||
|
output := bytes.NewBuffer(make([]byte, 0, p.MaxBufSize))
|
||||||
|
|
||||||
|
stdoutCh := make(chan error, 1)
|
||||||
|
go readInput(
|
||||||
|
io.LimitReader(outReadPipe, int64(p.MaxBufSize)),
|
||||||
|
output,
|
||||||
|
stdoutCh)
|
||||||
|
|
||||||
|
execCh := make(chan error, 1)
|
||||||
|
go executeCommand(*p.command, execCh)
|
||||||
|
|
||||||
|
finished := false
|
||||||
|
var errors []error
|
||||||
|
for !finished {
|
||||||
|
select {
|
||||||
|
case readError := <-stdoutCh:
|
||||||
|
errors = appendError(errors, readError)
|
||||||
|
finished = true
|
||||||
|
case execError := <-execCh:
|
||||||
|
err := outWritePipe.Close()
|
||||||
|
errors = appendError(errors, err)
|
||||||
|
errors = appendError(errors, execError)
|
||||||
|
if errors != nil {
|
||||||
|
return output.Bytes(), awserr.NewBatchError(
|
||||||
|
ErrCodeProcessProviderExecution,
|
||||||
|
errMsgProcessProviderProcess,
|
||||||
|
errors)
|
||||||
|
}
|
||||||
|
case <-time.After(p.Timeout):
|
||||||
|
finished = true
|
||||||
|
return output.Bytes(), awserr.NewBatchError(
|
||||||
|
ErrCodeProcessProviderExecution,
|
||||||
|
errMsgProcessProviderTimeout,
|
||||||
|
errors) // errors can be nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out := output.Bytes()
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// windows adds slashes to quotes
|
||||||
|
out = []byte(strings.Replace(string(out), `\"`, `"`, -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendError conveniently checks for nil before appending slice
|
||||||
|
func appendError(errors []error, err error) []error {
|
||||||
|
if err != nil {
|
||||||
|
return append(errors, err)
|
||||||
|
}
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeCommand(cmd exec.Cmd, exec chan error) {
|
||||||
|
// Start the command
|
||||||
|
err := cmd.Start()
|
||||||
|
if err == nil {
|
||||||
|
err = cmd.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
exec <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
func readInput(r io.Reader, w io.Writer, read chan error) {
|
||||||
|
tee := io.TeeReader(r, w)
|
||||||
|
|
||||||
|
_, err := ioutil.ReadAll(tee)
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
read <- err // will only arrive here when write end of pipe is closed
|
||||||
|
}
|
||||||
150
vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
Executable file
150
vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
Executable file
@@ -0,0 +1,150 @@
|
|||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/ini"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/shareddefaults"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SharedCredsProviderName provides a name of SharedCreds provider
|
||||||
|
const SharedCredsProviderName = "SharedCredentialsProvider"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSharedCredentialsHomeNotFound is emitted when the user directory cannot be found.
|
||||||
|
ErrSharedCredentialsHomeNotFound = awserr.New("UserHomeNotFound", "user home directory not found.", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A SharedCredentialsProvider retrieves credentials from the current user's home
|
||||||
|
// directory, and keeps track if those credentials are expired.
|
||||||
|
//
|
||||||
|
// Profile ini file example: $HOME/.aws/credentials
|
||||||
|
type SharedCredentialsProvider struct {
|
||||||
|
// Path to the shared credentials file.
|
||||||
|
//
|
||||||
|
// If empty will look for "AWS_SHARED_CREDENTIALS_FILE" env variable. If the
|
||||||
|
// env value is empty will default to current user's home directory.
|
||||||
|
// Linux/OSX: "$HOME/.aws/credentials"
|
||||||
|
// Windows: "%USERPROFILE%\.aws\credentials"
|
||||||
|
Filename string
|
||||||
|
|
||||||
|
// AWS Profile to extract credentials from the shared credentials file. If empty
|
||||||
|
// will default to environment variable "AWS_PROFILE" or "default" if
|
||||||
|
// environment variable is also not set.
|
||||||
|
Profile string
|
||||||
|
|
||||||
|
// retrieved states if the credentials have been successfully retrieved.
|
||||||
|
retrieved bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSharedCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping the Profile file provider.
|
||||||
|
func NewSharedCredentials(filename, profile string) *Credentials {
|
||||||
|
return NewCredentials(&SharedCredentialsProvider{
|
||||||
|
Filename: filename,
|
||||||
|
Profile: profile,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve reads and extracts the shared credentials from the current
|
||||||
|
// users home directory.
|
||||||
|
func (p *SharedCredentialsProvider) Retrieve() (Value, error) {
|
||||||
|
p.retrieved = false
|
||||||
|
|
||||||
|
filename, err := p.filename()
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, err := loadProfile(filename, p.profile())
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.retrieved = true
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the shared credentials have expired.
|
||||||
|
func (p *SharedCredentialsProvider) IsExpired() bool {
|
||||||
|
return !p.retrieved
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadProfiles loads from the file pointed to by shared credentials filename for profile.
|
||||||
|
// The credentials retrieved from the profile will be returned or error. Error will be
|
||||||
|
// returned if it fails to read from the file, or the data is invalid.
|
||||||
|
func loadProfile(filename, profile string) (Value, error) {
|
||||||
|
config, err := ini.OpenFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsLoad", "failed to load shared credentials file", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
iniProfile, ok := config.GetSection(profile)
|
||||||
|
if !ok {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsLoad", "failed to get profile", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
id := iniProfile.String("aws_access_key_id")
|
||||||
|
if len(id) == 0 {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsAccessKey",
|
||||||
|
fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename),
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := iniProfile.String("aws_secret_access_key")
|
||||||
|
if len(secret) == 0 {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsSecret",
|
||||||
|
fmt.Sprintf("shared credentials %s in %s did not contain aws_secret_access_key", profile, filename),
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to empty string if not found
|
||||||
|
token := iniProfile.String("aws_session_token")
|
||||||
|
|
||||||
|
return Value{
|
||||||
|
AccessKeyID: id,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
SessionToken: token,
|
||||||
|
ProviderName: SharedCredsProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filename returns the filename to use to read AWS shared credentials.
|
||||||
|
//
|
||||||
|
// Will return an error if the user's home directory path cannot be found.
|
||||||
|
func (p *SharedCredentialsProvider) filename() (string, error) {
|
||||||
|
if len(p.Filename) != 0 {
|
||||||
|
return p.Filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Filename = os.Getenv("AWS_SHARED_CREDENTIALS_FILE"); len(p.Filename) != 0 {
|
||||||
|
return p.Filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if home := shareddefaults.UserHomeDir(); len(home) == 0 {
|
||||||
|
// Backwards compatibility of home directly not found error being returned.
|
||||||
|
// This error is too verbose, failure when opening the file would of been
|
||||||
|
// a better error to return.
|
||||||
|
return "", ErrSharedCredentialsHomeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Filename = shareddefaults.SharedCredentialsFilename()
|
||||||
|
|
||||||
|
return p.Filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// profile returns the AWS shared credentials profile. If empty will read
|
||||||
|
// environment variable "AWS_PROFILE". If that is not set profile will
|
||||||
|
// return "default".
|
||||||
|
func (p *SharedCredentialsProvider) profile() string {
|
||||||
|
if p.Profile == "" {
|
||||||
|
p.Profile = os.Getenv("AWS_PROFILE")
|
||||||
|
}
|
||||||
|
if p.Profile == "" {
|
||||||
|
p.Profile = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Profile
|
||||||
|
}
|
||||||
55
vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
Executable file
55
vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StaticProviderName provides a name of Static provider
|
||||||
|
const StaticProviderName = "StaticProvider"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrStaticCredentialsEmpty is emitted when static credentials are empty.
|
||||||
|
ErrStaticCredentialsEmpty = awserr.New("EmptyStaticCreds", "static credentials are empty", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A StaticProvider is a set of credentials which are set programmatically,
|
||||||
|
// and will never expire.
|
||||||
|
type StaticProvider struct {
|
||||||
|
Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStaticCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping a static credentials value provider.
|
||||||
|
func NewStaticCredentials(id, secret, token string) *Credentials {
|
||||||
|
return NewCredentials(&StaticProvider{Value: Value{
|
||||||
|
AccessKeyID: id,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
SessionToken: token,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStaticCredentialsFromCreds returns a pointer to a new Credentials object
|
||||||
|
// wrapping the static credentials value provide. Same as NewStaticCredentials
|
||||||
|
// but takes the creds Value instead of individual fields
|
||||||
|
func NewStaticCredentialsFromCreds(creds Value) *Credentials {
|
||||||
|
return NewCredentials(&StaticProvider{Value: creds})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve returns the credentials or error if the credentials are invalid.
|
||||||
|
func (s *StaticProvider) Retrieve() (Value, error) {
|
||||||
|
if s.AccessKeyID == "" || s.SecretAccessKey == "" {
|
||||||
|
return Value{ProviderName: StaticProviderName}, ErrStaticCredentialsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.Value.ProviderName) == 0 {
|
||||||
|
s.Value.ProviderName = StaticProviderName
|
||||||
|
}
|
||||||
|
return s.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are expired.
|
||||||
|
//
|
||||||
|
// For StaticProvider, the credentials never expired.
|
||||||
|
func (s *StaticProvider) IsExpired() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
313
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
Executable file
313
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
Executable file
@@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
Package stscreds are credential Providers to retrieve STS AWS credentials.
|
||||||
|
|
||||||
|
STS provides multiple ways to retrieve credentials which can be used when making
|
||||||
|
future AWS service API operation calls.
|
||||||
|
|
||||||
|
The SDK will ensure that per instance of credentials.Credentials all requests
|
||||||
|
to refresh the credentials will be synchronized. But, the SDK is unable to
|
||||||
|
ensure synchronous usage of the AssumeRoleProvider if the value is shared
|
||||||
|
between multiple Credentials, Sessions or service clients.
|
||||||
|
|
||||||
|
Assume Role
|
||||||
|
|
||||||
|
To assume an IAM role using STS with the SDK you can create a new Credentials
|
||||||
|
with the SDKs's stscreds package.
|
||||||
|
|
||||||
|
// Initial credentials loaded from SDK's default credential chain. Such as
|
||||||
|
// the environment, shared credentials (~/.aws/credentials), or EC2 Instance
|
||||||
|
// Role. These credentials will be used to to make the STS Assume Role API.
|
||||||
|
sess := session.Must(session.NewSession())
|
||||||
|
|
||||||
|
// Create the credentials from AssumeRoleProvider to assume the role
|
||||||
|
// referenced by the "myRoleARN" ARN.
|
||||||
|
creds := stscreds.NewCredentials(sess, "myRoleArn")
|
||||||
|
|
||||||
|
// Create service client value configured for credentials
|
||||||
|
// from assumed role.
|
||||||
|
svc := s3.New(sess, &aws.Config{Credentials: creds})
|
||||||
|
|
||||||
|
Assume Role with static MFA Token
|
||||||
|
|
||||||
|
To assume an IAM role with a MFA token you can either specify a MFA token code
|
||||||
|
directly or provide a function to prompt the user each time the credentials
|
||||||
|
need to refresh the role's credentials. Specifying the TokenCode should be used
|
||||||
|
for short lived operations that will not need to be refreshed, and when you do
|
||||||
|
not want to have direct control over the user provides their MFA token.
|
||||||
|
|
||||||
|
With TokenCode the AssumeRoleProvider will be not be able to refresh the role's
|
||||||
|
credentials.
|
||||||
|
|
||||||
|
// Create the credentials from AssumeRoleProvider to assume the role
|
||||||
|
// referenced by the "myRoleARN" ARN using the MFA token code provided.
|
||||||
|
creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
|
||||||
|
p.SerialNumber = aws.String("myTokenSerialNumber")
|
||||||
|
p.TokenCode = aws.String("00000000")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create service client value configured for credentials
|
||||||
|
// from assumed role.
|
||||||
|
svc := s3.New(sess, &aws.Config{Credentials: creds})
|
||||||
|
|
||||||
|
Assume Role with MFA Token Provider
|
||||||
|
|
||||||
|
To assume an IAM role with MFA for longer running tasks where the credentials
|
||||||
|
may need to be refreshed setting the TokenProvider field of AssumeRoleProvider
|
||||||
|
will allow the credential provider to prompt for new MFA token code when the
|
||||||
|
role's credentials need to be refreshed.
|
||||||
|
|
||||||
|
The StdinTokenProvider function is available to prompt on stdin to retrieve
|
||||||
|
the MFA token code from the user. You can also implement custom prompts by
|
||||||
|
satisfing the TokenProvider function signature.
|
||||||
|
|
||||||
|
Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
|
||||||
|
have undesirable results as the StdinTokenProvider will not be synchronized. A
|
||||||
|
single Credentials with an AssumeRoleProvider can be shared safely.
|
||||||
|
|
||||||
|
// Create the credentials from AssumeRoleProvider to assume the role
|
||||||
|
// referenced by the "myRoleARN" ARN. Prompting for MFA token from stdin.
|
||||||
|
creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
|
||||||
|
p.SerialNumber = aws.String("myTokenSerialNumber")
|
||||||
|
p.TokenProvider = stscreds.StdinTokenProvider
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create service client value configured for credentials
|
||||||
|
// from assumed role.
|
||||||
|
svc := s3.New(sess, &aws.Config{Credentials: creds})
|
||||||
|
|
||||||
|
*/
|
||||||
|
package stscreds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/sdkrand"
|
||||||
|
"github.com/aws/aws-sdk-go/service/sts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StdinTokenProvider will prompt on stderr and read from stdin for a string value.
|
||||||
|
// An error is returned if reading from stdin fails.
|
||||||
|
//
|
||||||
|
// Use this function go read MFA tokens from stdin. The function makes no attempt
|
||||||
|
// to make atomic prompts from stdin across multiple gorouties.
|
||||||
|
//
|
||||||
|
// Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
|
||||||
|
// have undesirable results as the StdinTokenProvider will not be synchronized. A
|
||||||
|
// single Credentials with an AssumeRoleProvider can be shared safely
|
||||||
|
//
|
||||||
|
// Will wait forever until something is provided on the stdin.
|
||||||
|
func StdinTokenProvider() (string, error) {
|
||||||
|
var v string
|
||||||
|
fmt.Fprintf(os.Stderr, "Assume Role MFA token code: ")
|
||||||
|
_, err := fmt.Scanln(&v)
|
||||||
|
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProviderName provides a name of AssumeRole provider
|
||||||
|
const ProviderName = "AssumeRoleProvider"
|
||||||
|
|
||||||
|
// AssumeRoler represents the minimal subset of the STS client API used by this provider.
|
||||||
|
type AssumeRoler interface {
|
||||||
|
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDuration is the default amount of time in minutes that the credentials
|
||||||
|
// will be valid for.
|
||||||
|
var DefaultDuration = time.Duration(15) * time.Minute
|
||||||
|
|
||||||
|
// AssumeRoleProvider retrieves temporary credentials from the STS service, and
|
||||||
|
// keeps track of their expiration time.
|
||||||
|
//
|
||||||
|
// This credential provider will be used by the SDKs default credential change
|
||||||
|
// when shared configuration is enabled, and the shared config or shared credentials
|
||||||
|
// file configure assume role. See Session docs for how to do this.
|
||||||
|
//
|
||||||
|
// AssumeRoleProvider does not provide any synchronization and it is not safe
|
||||||
|
// to share this value across multiple Credentials, Sessions, or service clients
|
||||||
|
// without also sharing the same Credentials instance.
|
||||||
|
type AssumeRoleProvider struct {
|
||||||
|
credentials.Expiry
|
||||||
|
|
||||||
|
// STS client to make assume role request with.
|
||||||
|
Client AssumeRoler
|
||||||
|
|
||||||
|
// Role to be assumed.
|
||||||
|
RoleARN string
|
||||||
|
|
||||||
|
// Session name, if you wish to reuse the credentials elsewhere.
|
||||||
|
RoleSessionName string
|
||||||
|
|
||||||
|
// Expiry duration of the STS credentials. Defaults to 15 minutes if not set.
|
||||||
|
Duration time.Duration
|
||||||
|
|
||||||
|
// Optional ExternalID to pass along, defaults to nil if not set.
|
||||||
|
ExternalID *string
|
||||||
|
|
||||||
|
// The policy plain text must be 2048 bytes or shorter. However, an internal
|
||||||
|
// conversion compresses it into a packed binary format with a separate limit.
|
||||||
|
// The PackedPolicySize response element indicates by percentage how close to
|
||||||
|
// the upper size limit the policy is, with 100% equaling the maximum allowed
|
||||||
|
// size.
|
||||||
|
Policy *string
|
||||||
|
|
||||||
|
// The identification number of the MFA device that is associated with the user
|
||||||
|
// who is making the AssumeRole call. Specify this value if the trust policy
|
||||||
|
// of the role being assumed includes a condition that requires MFA authentication.
|
||||||
|
// The value is either the serial number for a hardware device (such as GAHT12345678)
|
||||||
|
// or an Amazon Resource Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user).
|
||||||
|
SerialNumber *string
|
||||||
|
|
||||||
|
// The value provided by the MFA device, if the trust policy of the role being
|
||||||
|
// assumed requires MFA (that is, if the policy includes a condition that tests
|
||||||
|
// for MFA). If the role being assumed requires MFA and if the TokenCode value
|
||||||
|
// is missing or expired, the AssumeRole call returns an "access denied" error.
|
||||||
|
//
|
||||||
|
// If SerialNumber is set and neither TokenCode nor TokenProvider are also
|
||||||
|
// set an error will be returned.
|
||||||
|
TokenCode *string
|
||||||
|
|
||||||
|
// Async method of providing MFA token code for assuming an IAM role with MFA.
|
||||||
|
// The value returned by the function will be used as the TokenCode in the Retrieve
|
||||||
|
// call. See StdinTokenProvider for a provider that prompts and reads from stdin.
|
||||||
|
//
|
||||||
|
// This token provider will be called when ever the assumed role's
|
||||||
|
// credentials need to be refreshed when SerialNumber is also set and
|
||||||
|
// TokenCode is not set.
|
||||||
|
//
|
||||||
|
// If both TokenCode and TokenProvider is set, TokenProvider will be used and
|
||||||
|
// TokenCode is ignored.
|
||||||
|
TokenProvider func() (string, error)
|
||||||
|
|
||||||
|
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||||
|
// the credentials actually expiring. This is beneficial so race conditions
|
||||||
|
// with expiring credentials do not cause request to fail unexpectedly
|
||||||
|
// due to ExpiredTokenException exceptions.
|
||||||
|
//
|
||||||
|
// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||||
|
// 10 seconds before the credentials are actually expired.
|
||||||
|
//
|
||||||
|
// If ExpiryWindow is 0 or less it will be ignored.
|
||||||
|
ExpiryWindow time.Duration
|
||||||
|
|
||||||
|
// MaxJitterFrac reduces the effective Duration of each credential requested
|
||||||
|
// by a random percentage between 0 and MaxJitterFraction. MaxJitterFrac must
|
||||||
|
// have a value between 0 and 1. Any other value may lead to expected behavior.
|
||||||
|
// With a MaxJitterFrac value of 0, default) will no jitter will be used.
|
||||||
|
//
|
||||||
|
// For example, with a Duration of 30m and a MaxJitterFrac of 0.1, the
|
||||||
|
// AssumeRole call will be made with an arbitrary Duration between 27m and
|
||||||
|
// 30m.
|
||||||
|
//
|
||||||
|
// MaxJitterFrac should not be negative.
|
||||||
|
MaxJitterFrac float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentials returns a pointer to a new Credentials object wrapping the
|
||||||
|
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
|
||||||
|
// role will be named after a nanosecond timestamp of this operation.
|
||||||
|
//
|
||||||
|
// Takes a Config provider to create the STS client. The ConfigProvider is
|
||||||
|
// satisfied by the session.Session type.
|
||||||
|
//
|
||||||
|
// It is safe to share the returned Credentials with multiple Sessions and
|
||||||
|
// service clients. All access to the credentials and refreshing them
|
||||||
|
// will be synchronized.
|
||||||
|
func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
|
||||||
|
p := &AssumeRoleProvider{
|
||||||
|
Client: sts.New(c),
|
||||||
|
RoleARN: roleARN,
|
||||||
|
Duration: DefaultDuration,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsWithClient returns a pointer to a new Credentials object wrapping the
|
||||||
|
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
|
||||||
|
// role will be named after a nanosecond timestamp of this operation.
|
||||||
|
//
|
||||||
|
// Takes an AssumeRoler which can be satisfied by the STS client.
|
||||||
|
//
|
||||||
|
// It is safe to share the returned Credentials with multiple Sessions and
|
||||||
|
// service clients. All access to the credentials and refreshing them
|
||||||
|
// will be synchronized.
|
||||||
|
func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
|
||||||
|
p := &AssumeRoleProvider{
|
||||||
|
Client: svc,
|
||||||
|
RoleARN: roleARN,
|
||||||
|
Duration: DefaultDuration,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve generates a new set of temporary credentials using STS.
|
||||||
|
func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
|
||||||
|
|
||||||
|
// Apply defaults where parameters are not set.
|
||||||
|
if p.RoleSessionName == "" {
|
||||||
|
// Try to work out a role name that will hopefully end up unique.
|
||||||
|
p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano())
|
||||||
|
}
|
||||||
|
if p.Duration == 0 {
|
||||||
|
// Expire as often as AWS permits.
|
||||||
|
p.Duration = DefaultDuration
|
||||||
|
}
|
||||||
|
jitter := time.Duration(sdkrand.SeededRand.Float64() * p.MaxJitterFrac * float64(p.Duration))
|
||||||
|
input := &sts.AssumeRoleInput{
|
||||||
|
DurationSeconds: aws.Int64(int64((p.Duration - jitter) / time.Second)),
|
||||||
|
RoleArn: aws.String(p.RoleARN),
|
||||||
|
RoleSessionName: aws.String(p.RoleSessionName),
|
||||||
|
ExternalId: p.ExternalID,
|
||||||
|
}
|
||||||
|
if p.Policy != nil {
|
||||||
|
input.Policy = p.Policy
|
||||||
|
}
|
||||||
|
if p.SerialNumber != nil {
|
||||||
|
if p.TokenCode != nil {
|
||||||
|
input.SerialNumber = p.SerialNumber
|
||||||
|
input.TokenCode = p.TokenCode
|
||||||
|
} else if p.TokenProvider != nil {
|
||||||
|
input.SerialNumber = p.SerialNumber
|
||||||
|
code, err := p.TokenProvider()
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, err
|
||||||
|
}
|
||||||
|
input.TokenCode = aws.String(code)
|
||||||
|
} else {
|
||||||
|
return credentials.Value{ProviderName: ProviderName},
|
||||||
|
awserr.New("AssumeRoleTokenNotAvailable",
|
||||||
|
"assume role with MFA enabled, but neither TokenCode nor TokenProvider are set", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
roleOutput, err := p.Client.AssumeRole(input)
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will proactively generate new credentials before they expire.
|
||||||
|
p.SetExpiration(*roleOutput.Credentials.Expiration, p.ExpiryWindow)
|
||||||
|
|
||||||
|
return credentials.Value{
|
||||||
|
AccessKeyID: *roleOutput.Credentials.AccessKeyId,
|
||||||
|
SecretAccessKey: *roleOutput.Credentials.SecretAccessKey,
|
||||||
|
SessionToken: *roleOutput.Credentials.SessionToken,
|
||||||
|
ProviderName: ProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
46
vendor/github.com/aws/aws-sdk-go/aws/csm/doc.go
generated
vendored
Executable file
46
vendor/github.com/aws/aws-sdk-go/aws/csm/doc.go
generated
vendored
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
// Package csm provides Client Side Monitoring (CSM) which enables sending metrics
|
||||||
|
// via UDP connection. Using the Start function will enable the reporting of
|
||||||
|
// metrics on a given port. If Start is called, with different parameters, again,
|
||||||
|
// a panic will occur.
|
||||||
|
//
|
||||||
|
// Pause can be called to pause any metrics publishing on a given port. Sessions
|
||||||
|
// that have had their handlers modified via InjectHandlers may still be used.
|
||||||
|
// However, the handlers will act as a no-op meaning no metrics will be published.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// r, err := csm.Start("clientID", ":31000")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(fmt.Errorf("failed starting CSM: %v", err))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// sess, err := session.NewSession(&aws.Config{})
|
||||||
|
// if err != nil {
|
||||||
|
// panic(fmt.Errorf("failed loading session: %v", err))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// r.InjectHandlers(&sess.Handlers)
|
||||||
|
//
|
||||||
|
// client := s3.New(sess)
|
||||||
|
// resp, err := client.GetObject(&s3.GetObjectInput{
|
||||||
|
// Bucket: aws.String("bucket"),
|
||||||
|
// Key: aws.String("key"),
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // Will pause monitoring
|
||||||
|
// r.Pause()
|
||||||
|
// resp, err = client.GetObject(&s3.GetObjectInput{
|
||||||
|
// Bucket: aws.String("bucket"),
|
||||||
|
// Key: aws.String("key"),
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // Resume monitoring
|
||||||
|
// r.Continue()
|
||||||
|
//
|
||||||
|
// Start returns a Reporter that is used to enable or disable monitoring. If
|
||||||
|
// access to the Reporter is required later, calling Get will return the Reporter
|
||||||
|
// singleton.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// r := csm.Get()
|
||||||
|
// r.Continue()
|
||||||
|
package csm
|
||||||
67
vendor/github.com/aws/aws-sdk-go/aws/csm/enable.go
generated
vendored
Executable file
67
vendor/github.com/aws/aws-sdk-go/aws/csm/enable.go
generated
vendored
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
package csm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
lock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client side metric handler names
|
||||||
|
const (
|
||||||
|
APICallMetricHandlerName = "awscsm.SendAPICallMetric"
|
||||||
|
APICallAttemptMetricHandlerName = "awscsm.SendAPICallAttemptMetric"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start will start the a long running go routine to capture
|
||||||
|
// client side metrics. Calling start multiple time will only
|
||||||
|
// start the metric listener once and will panic if a different
|
||||||
|
// client ID or port is passed in.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// r, err := csm.Start("clientID", "127.0.0.1:8094")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(fmt.Errorf("expected no error, but received %v", err))
|
||||||
|
// }
|
||||||
|
// sess := session.NewSession()
|
||||||
|
// r.InjectHandlers(sess.Handlers)
|
||||||
|
//
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
// out, err := svc.GetObject(&s3.GetObjectInput{
|
||||||
|
// Bucket: aws.String("bucket"),
|
||||||
|
// Key: aws.String("key"),
|
||||||
|
// })
|
||||||
|
func Start(clientID string, url string) (*Reporter, error) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
if sender == nil {
|
||||||
|
sender = newReporter(clientID, url)
|
||||||
|
} else {
|
||||||
|
if sender.clientID != clientID {
|
||||||
|
panic(fmt.Errorf("inconsistent client IDs. %q was expected, but received %q", sender.clientID, clientID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if sender.url != url {
|
||||||
|
panic(fmt.Errorf("inconsistent URLs. %q was expected, but received %q", sender.url, url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := connect(url); err != nil {
|
||||||
|
sender = nil
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sender, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will return a reporter if one exists, if one does not exist, nil will
|
||||||
|
// be returned.
|
||||||
|
func Get() *Reporter {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
return sender
|
||||||
|
}
|
||||||
109
vendor/github.com/aws/aws-sdk-go/aws/csm/metric.go
generated
vendored
Executable file
109
vendor/github.com/aws/aws-sdk-go/aws/csm/metric.go
generated
vendored
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
package csm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metricTime time.Time
|
||||||
|
|
||||||
|
func (t metricTime) MarshalJSON() ([]byte, error) {
|
||||||
|
ns := time.Duration(time.Time(t).UnixNano())
|
||||||
|
return []byte(strconv.FormatInt(int64(ns/time.Millisecond), 10)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type metric struct {
|
||||||
|
ClientID *string `json:"ClientId,omitempty"`
|
||||||
|
API *string `json:"Api,omitempty"`
|
||||||
|
Service *string `json:"Service,omitempty"`
|
||||||
|
Timestamp *metricTime `json:"Timestamp,omitempty"`
|
||||||
|
Type *string `json:"Type,omitempty"`
|
||||||
|
Version *int `json:"Version,omitempty"`
|
||||||
|
|
||||||
|
AttemptCount *int `json:"AttemptCount,omitempty"`
|
||||||
|
Latency *int `json:"Latency,omitempty"`
|
||||||
|
|
||||||
|
Fqdn *string `json:"Fqdn,omitempty"`
|
||||||
|
UserAgent *string `json:"UserAgent,omitempty"`
|
||||||
|
AttemptLatency *int `json:"AttemptLatency,omitempty"`
|
||||||
|
|
||||||
|
SessionToken *string `json:"SessionToken,omitempty"`
|
||||||
|
Region *string `json:"Region,omitempty"`
|
||||||
|
AccessKey *string `json:"AccessKey,omitempty"`
|
||||||
|
HTTPStatusCode *int `json:"HttpStatusCode,omitempty"`
|
||||||
|
XAmzID2 *string `json:"XAmzId2,omitempty"`
|
||||||
|
XAmzRequestID *string `json:"XAmznRequestId,omitempty"`
|
||||||
|
|
||||||
|
AWSException *string `json:"AwsException,omitempty"`
|
||||||
|
AWSExceptionMessage *string `json:"AwsExceptionMessage,omitempty"`
|
||||||
|
SDKException *string `json:"SdkException,omitempty"`
|
||||||
|
SDKExceptionMessage *string `json:"SdkExceptionMessage,omitempty"`
|
||||||
|
|
||||||
|
FinalHTTPStatusCode *int `json:"FinalHttpStatusCode,omitempty"`
|
||||||
|
FinalAWSException *string `json:"FinalAwsException,omitempty"`
|
||||||
|
FinalAWSExceptionMessage *string `json:"FinalAwsExceptionMessage,omitempty"`
|
||||||
|
FinalSDKException *string `json:"FinalSdkException,omitempty"`
|
||||||
|
FinalSDKExceptionMessage *string `json:"FinalSdkExceptionMessage,omitempty"`
|
||||||
|
|
||||||
|
DestinationIP *string `json:"DestinationIp,omitempty"`
|
||||||
|
ConnectionReused *int `json:"ConnectionReused,omitempty"`
|
||||||
|
|
||||||
|
AcquireConnectionLatency *int `json:"AcquireConnectionLatency,omitempty"`
|
||||||
|
ConnectLatency *int `json:"ConnectLatency,omitempty"`
|
||||||
|
RequestLatency *int `json:"RequestLatency,omitempty"`
|
||||||
|
DNSLatency *int `json:"DnsLatency,omitempty"`
|
||||||
|
TCPLatency *int `json:"TcpLatency,omitempty"`
|
||||||
|
SSLLatency *int `json:"SslLatency,omitempty"`
|
||||||
|
|
||||||
|
MaxRetriesExceeded *int `json:"MaxRetriesExceeded,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metric) TruncateFields() {
|
||||||
|
m.ClientID = truncateString(m.ClientID, 255)
|
||||||
|
m.UserAgent = truncateString(m.UserAgent, 256)
|
||||||
|
|
||||||
|
m.AWSException = truncateString(m.AWSException, 128)
|
||||||
|
m.AWSExceptionMessage = truncateString(m.AWSExceptionMessage, 512)
|
||||||
|
|
||||||
|
m.SDKException = truncateString(m.SDKException, 128)
|
||||||
|
m.SDKExceptionMessage = truncateString(m.SDKExceptionMessage, 512)
|
||||||
|
|
||||||
|
m.FinalAWSException = truncateString(m.FinalAWSException, 128)
|
||||||
|
m.FinalAWSExceptionMessage = truncateString(m.FinalAWSExceptionMessage, 512)
|
||||||
|
|
||||||
|
m.FinalSDKException = truncateString(m.FinalSDKException, 128)
|
||||||
|
m.FinalSDKExceptionMessage = truncateString(m.FinalSDKExceptionMessage, 512)
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncateString(v *string, l int) *string {
|
||||||
|
if v != nil && len(*v) > l {
|
||||||
|
nv := (*v)[:l]
|
||||||
|
return &nv
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metric) SetException(e metricException) {
|
||||||
|
switch te := e.(type) {
|
||||||
|
case awsException:
|
||||||
|
m.AWSException = aws.String(te.exception)
|
||||||
|
m.AWSExceptionMessage = aws.String(te.message)
|
||||||
|
case sdkException:
|
||||||
|
m.SDKException = aws.String(te.exception)
|
||||||
|
m.SDKExceptionMessage = aws.String(te.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metric) SetFinalException(e metricException) {
|
||||||
|
switch te := e.(type) {
|
||||||
|
case awsException:
|
||||||
|
m.FinalAWSException = aws.String(te.exception)
|
||||||
|
m.FinalAWSExceptionMessage = aws.String(te.message)
|
||||||
|
case sdkException:
|
||||||
|
m.FinalSDKException = aws.String(te.exception)
|
||||||
|
m.FinalSDKExceptionMessage = aws.String(te.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
54
vendor/github.com/aws/aws-sdk-go/aws/csm/metric_chan.go
generated
vendored
Executable file
54
vendor/github.com/aws/aws-sdk-go/aws/csm/metric_chan.go
generated
vendored
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
package csm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
runningEnum = iota
|
||||||
|
pausedEnum
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// MetricsChannelSize of metrics to hold in the channel
|
||||||
|
MetricsChannelSize = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
type metricChan struct {
|
||||||
|
ch chan metric
|
||||||
|
paused int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMetricChan(size int) metricChan {
|
||||||
|
return metricChan{
|
||||||
|
ch: make(chan metric, size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *metricChan) Pause() {
|
||||||
|
atomic.StoreInt64(&ch.paused, pausedEnum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *metricChan) Continue() {
|
||||||
|
atomic.StoreInt64(&ch.paused, runningEnum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *metricChan) IsPaused() bool {
|
||||||
|
v := atomic.LoadInt64(&ch.paused)
|
||||||
|
return v == pausedEnum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push will push metrics to the metric channel if the channel
|
||||||
|
// is not paused
|
||||||
|
func (ch *metricChan) Push(m metric) bool {
|
||||||
|
if ch.IsPaused() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case ch.ch <- m:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
26
vendor/github.com/aws/aws-sdk-go/aws/csm/metric_exception.go
generated
vendored
Executable file
26
vendor/github.com/aws/aws-sdk-go/aws/csm/metric_exception.go
generated
vendored
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
package csm
|
||||||
|
|
||||||
|
type metricException interface {
|
||||||
|
Exception() string
|
||||||
|
Message() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestException struct {
|
||||||
|
exception string
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e requestException) Exception() string {
|
||||||
|
return e.exception
|
||||||
|
}
|
||||||
|
func (e requestException) Message() string {
|
||||||
|
return e.message
|
||||||
|
}
|
||||||
|
|
||||||
|
type awsException struct {
|
||||||
|
requestException
|
||||||
|
}
|
||||||
|
|
||||||
|
type sdkException struct {
|
||||||
|
requestException
|
||||||
|
}
|
||||||
260
vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go
generated
vendored
Executable file
260
vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go
generated
vendored
Executable file
@@ -0,0 +1,260 @@
|
|||||||
|
package csm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultPort is used when no port is specified
|
||||||
|
DefaultPort = "31000"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reporter will gather metrics of API requests made and
|
||||||
|
// send those metrics to the CSM endpoint.
|
||||||
|
type Reporter struct {
|
||||||
|
clientID string
|
||||||
|
url string
|
||||||
|
conn net.Conn
|
||||||
|
metricsCh metricChan
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
sender *Reporter
|
||||||
|
)
|
||||||
|
|
||||||
|
func connect(url string) error {
|
||||||
|
const network = "udp"
|
||||||
|
if err := sender.connect(network, url); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sender.done == nil {
|
||||||
|
sender.done = make(chan struct{})
|
||||||
|
go sender.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReporter(clientID, url string) *Reporter {
|
||||||
|
return &Reporter{
|
||||||
|
clientID: clientID,
|
||||||
|
url: url,
|
||||||
|
metricsCh: newMetricChan(MetricsChannelSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rep *Reporter) sendAPICallAttemptMetric(r *request.Request) {
|
||||||
|
if rep == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
creds, _ := r.Config.Credentials.Get()
|
||||||
|
|
||||||
|
m := metric{
|
||||||
|
ClientID: aws.String(rep.clientID),
|
||||||
|
API: aws.String(r.Operation.Name),
|
||||||
|
Service: aws.String(r.ClientInfo.ServiceID),
|
||||||
|
Timestamp: (*metricTime)(&now),
|
||||||
|
UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")),
|
||||||
|
Region: r.Config.Region,
|
||||||
|
Type: aws.String("ApiCallAttempt"),
|
||||||
|
Version: aws.Int(1),
|
||||||
|
|
||||||
|
XAmzRequestID: aws.String(r.RequestID),
|
||||||
|
|
||||||
|
AttemptCount: aws.Int(r.RetryCount + 1),
|
||||||
|
AttemptLatency: aws.Int(int(now.Sub(r.AttemptTime).Nanoseconds() / int64(time.Millisecond))),
|
||||||
|
AccessKey: aws.String(creds.AccessKeyID),
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.HTTPResponse != nil {
|
||||||
|
m.HTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Error != nil {
|
||||||
|
if awserr, ok := r.Error.(awserr.Error); ok {
|
||||||
|
m.SetException(getMetricException(awserr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.TruncateFields()
|
||||||
|
rep.metricsCh.Push(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMetricException(err awserr.Error) metricException {
|
||||||
|
msg := err.Error()
|
||||||
|
code := err.Code()
|
||||||
|
|
||||||
|
switch code {
|
||||||
|
case "RequestError",
|
||||||
|
"SerializationError",
|
||||||
|
request.CanceledErrorCode:
|
||||||
|
return sdkException{
|
||||||
|
requestException{exception: code, message: msg},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return awsException{
|
||||||
|
requestException{exception: code, message: msg},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rep *Reporter) sendAPICallMetric(r *request.Request) {
|
||||||
|
if rep == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
m := metric{
|
||||||
|
ClientID: aws.String(rep.clientID),
|
||||||
|
API: aws.String(r.Operation.Name),
|
||||||
|
Service: aws.String(r.ClientInfo.ServiceID),
|
||||||
|
Timestamp: (*metricTime)(&now),
|
||||||
|
UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")),
|
||||||
|
Type: aws.String("ApiCall"),
|
||||||
|
AttemptCount: aws.Int(r.RetryCount + 1),
|
||||||
|
Region: r.Config.Region,
|
||||||
|
Latency: aws.Int(int(time.Now().Sub(r.Time) / time.Millisecond)),
|
||||||
|
XAmzRequestID: aws.String(r.RequestID),
|
||||||
|
MaxRetriesExceeded: aws.Int(boolIntValue(r.RetryCount >= r.MaxRetries())),
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.HTTPResponse != nil {
|
||||||
|
m.FinalHTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Error != nil {
|
||||||
|
if awserr, ok := r.Error.(awserr.Error); ok {
|
||||||
|
m.SetFinalException(getMetricException(awserr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.TruncateFields()
|
||||||
|
|
||||||
|
// TODO: Probably want to figure something out for logging dropped
|
||||||
|
// metrics
|
||||||
|
rep.metricsCh.Push(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rep *Reporter) connect(network, url string) error {
|
||||||
|
if rep.conn != nil {
|
||||||
|
rep.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.Dial(network, url)
|
||||||
|
if err != nil {
|
||||||
|
return awserr.New("UDPError", "Could not connect", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rep.conn = conn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rep *Reporter) close() {
|
||||||
|
if rep.done != nil {
|
||||||
|
close(rep.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
rep.metricsCh.Pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rep *Reporter) start() {
|
||||||
|
defer func() {
|
||||||
|
rep.metricsCh.Pause()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-rep.done:
|
||||||
|
rep.done = nil
|
||||||
|
return
|
||||||
|
case m := <-rep.metricsCh.ch:
|
||||||
|
// TODO: What to do with this error? Probably should just log
|
||||||
|
b, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rep.conn.Write(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause will pause the metric channel preventing any new metrics from
|
||||||
|
// being added.
|
||||||
|
func (rep *Reporter) Pause() {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
if rep == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rep.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue will reopen the metric channel and allow for monitoring
|
||||||
|
// to be resumed.
|
||||||
|
func (rep *Reporter) Continue() {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
if rep == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rep.metricsCh.IsPaused() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rep.metricsCh.Continue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectHandlers will will enable client side metrics and inject the proper
|
||||||
|
// handlers to handle how metrics are sent.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Start must be called in order to inject the correct handlers
|
||||||
|
// r, err := csm.Start("clientID", "127.0.0.1:8094")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(fmt.Errorf("expected no error, but received %v", err))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// sess := session.NewSession()
|
||||||
|
// r.InjectHandlers(&sess.Handlers)
|
||||||
|
//
|
||||||
|
// // create a new service client with our client side metric session
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
func (rep *Reporter) InjectHandlers(handlers *request.Handlers) {
|
||||||
|
if rep == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers.Complete.PushFrontNamed(request.NamedHandler{
|
||||||
|
Name: APICallMetricHandlerName,
|
||||||
|
Fn: rep.sendAPICallMetric,
|
||||||
|
})
|
||||||
|
|
||||||
|
handlers.CompleteAttempt.PushFrontNamed(request.NamedHandler{
|
||||||
|
Name: APICallAttemptMetricHandlerName,
|
||||||
|
Fn: rep.sendAPICallAttemptMetric,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// boolIntValue return 1 for true and 0 for false.
|
||||||
|
func boolIntValue(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
207
vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go
generated
vendored
Executable file
207
vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go
generated
vendored
Executable file
@@ -0,0 +1,207 @@
|
|||||||
|
// Package defaults is a collection of helpers to retrieve the SDK's default
|
||||||
|
// configuration and handlers.
|
||||||
|
//
|
||||||
|
// Generally this package shouldn't be used directly, but session.Session
|
||||||
|
// instead. This package is useful when you need to reset the defaults
|
||||||
|
// of a session or service client to the SDK defaults before setting
|
||||||
|
// additional parameters.
|
||||||
|
package defaults
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials/endpointcreds"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/shareddefaults"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Defaults provides a collection of default values for SDK clients.
|
||||||
|
type Defaults struct {
|
||||||
|
Config *aws.Config
|
||||||
|
Handlers request.Handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the SDK's default values with Config and handlers pre-configured.
|
||||||
|
func Get() Defaults {
|
||||||
|
cfg := Config()
|
||||||
|
handlers := Handlers()
|
||||||
|
cfg.Credentials = CredChain(cfg, handlers)
|
||||||
|
|
||||||
|
return Defaults{
|
||||||
|
Config: cfg,
|
||||||
|
Handlers: handlers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config returns the default configuration without credentials.
|
||||||
|
// To retrieve a config with credentials also included use
|
||||||
|
// `defaults.Get().Config` instead.
|
||||||
|
//
|
||||||
|
// Generally you shouldn't need to use this method directly, but
|
||||||
|
// is available if you need to reset the configuration of an
|
||||||
|
// existing service client or session.
|
||||||
|
func Config() *aws.Config {
|
||||||
|
return aws.NewConfig().
|
||||||
|
WithCredentials(credentials.AnonymousCredentials).
|
||||||
|
WithRegion(os.Getenv("AWS_REGION")).
|
||||||
|
WithHTTPClient(http.DefaultClient).
|
||||||
|
WithMaxRetries(aws.UseServiceDefaultRetries).
|
||||||
|
WithLogger(aws.NewDefaultLogger()).
|
||||||
|
WithLogLevel(aws.LogOff).
|
||||||
|
WithEndpointResolver(endpoints.DefaultResolver())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handlers returns the default request handlers.
|
||||||
|
//
|
||||||
|
// Generally you shouldn't need to use this method directly, but
|
||||||
|
// is available if you need to reset the request handlers of an
|
||||||
|
// existing service client or session.
|
||||||
|
func Handlers() request.Handlers {
|
||||||
|
var handlers request.Handlers
|
||||||
|
|
||||||
|
handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
|
||||||
|
handlers.Validate.AfterEachFn = request.HandlerListStopOnError
|
||||||
|
handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
|
||||||
|
handlers.Build.PushBackNamed(corehandlers.AddHostExecEnvUserAgentHander)
|
||||||
|
handlers.Build.AfterEachFn = request.HandlerListStopOnError
|
||||||
|
handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
|
||||||
|
handlers.Send.PushBackNamed(corehandlers.ValidateReqSigHandler)
|
||||||
|
handlers.Send.PushBackNamed(corehandlers.SendHandler)
|
||||||
|
handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
|
||||||
|
handlers.ValidateResponse.PushBackNamed(corehandlers.ValidateResponseHandler)
|
||||||
|
|
||||||
|
return handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredChain returns the default credential chain.
|
||||||
|
//
|
||||||
|
// Generally you shouldn't need to use this method directly, but
|
||||||
|
// is available if you need to reset the credentials of an
|
||||||
|
// existing service client or session's Config.
|
||||||
|
func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
|
||||||
|
return credentials.NewCredentials(&credentials.ChainProvider{
|
||||||
|
VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
|
||||||
|
Providers: CredProviders(cfg, handlers),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredProviders returns the slice of providers used in
|
||||||
|
// the default credential chain.
|
||||||
|
//
|
||||||
|
// For applications that need to use some other provider (for example use
|
||||||
|
// different environment variables for legacy reasons) but still fall back
|
||||||
|
// on the default chain of providers. This allows that default chaint to be
|
||||||
|
// automatically updated
|
||||||
|
func CredProviders(cfg *aws.Config, handlers request.Handlers) []credentials.Provider {
|
||||||
|
return []credentials.Provider{
|
||||||
|
&credentials.EnvProvider{},
|
||||||
|
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
|
||||||
|
RemoteCredProvider(*cfg, handlers),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
httpProviderAuthorizationEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
|
||||||
|
httpProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RemoteCredProvider returns a credentials provider for the default remote
|
||||||
|
// endpoints such as EC2 or ECS Roles.
|
||||||
|
func RemoteCredProvider(cfg aws.Config, handlers request.Handlers) credentials.Provider {
|
||||||
|
if u := os.Getenv(httpProviderEnvVar); len(u) > 0 {
|
||||||
|
return localHTTPCredProvider(cfg, handlers, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
if uri := os.Getenv(shareddefaults.ECSCredsProviderEnvVar); len(uri) > 0 {
|
||||||
|
u := fmt.Sprintf("%s%s", shareddefaults.ECSContainerCredentialsURI, uri)
|
||||||
|
return httpCredProvider(cfg, handlers, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ec2RoleProvider(cfg, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
var lookupHostFn = net.LookupHost
|
||||||
|
|
||||||
|
func isLoopbackHost(host string) (bool, error) {
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip != nil {
|
||||||
|
return ip.IsLoopback(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host is not an ip, perform lookup
|
||||||
|
addrs, err := lookupHostFn(host)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if !net.ParseIP(addr).IsLoopback() {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func localHTTPCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
|
||||||
|
var errMsg string
|
||||||
|
|
||||||
|
parsed, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
errMsg = fmt.Sprintf("invalid URL, %v", err)
|
||||||
|
} else {
|
||||||
|
host := aws.URLHostname(parsed)
|
||||||
|
if len(host) == 0 {
|
||||||
|
errMsg = "unable to parse host from local HTTP cred provider URL"
|
||||||
|
} else if isLoopback, loopbackErr := isLoopbackHost(host); loopbackErr != nil {
|
||||||
|
errMsg = fmt.Sprintf("failed to resolve host %q, %v", host, loopbackErr)
|
||||||
|
} else if !isLoopback {
|
||||||
|
errMsg = fmt.Sprintf("invalid endpoint host, %q, only loopback hosts are allowed.", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errMsg) > 0 {
|
||||||
|
if cfg.Logger != nil {
|
||||||
|
cfg.Logger.Log("Ignoring, HTTP credential provider", errMsg, err)
|
||||||
|
}
|
||||||
|
return credentials.ErrorProvider{
|
||||||
|
Err: awserr.New("CredentialsEndpointError", errMsg, err),
|
||||||
|
ProviderName: endpointcreds.ProviderName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpCredProvider(cfg, handlers, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
|
||||||
|
return endpointcreds.NewProviderClient(cfg, handlers, u,
|
||||||
|
func(p *endpointcreds.Provider) {
|
||||||
|
p.ExpiryWindow = 5 * time.Minute
|
||||||
|
p.AuthorizationToken = os.Getenv(httpProviderAuthorizationEnvVar)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ec2RoleProvider(cfg aws.Config, handlers request.Handlers) credentials.Provider {
|
||||||
|
resolver := cfg.EndpointResolver
|
||||||
|
if resolver == nil {
|
||||||
|
resolver = endpoints.DefaultResolver()
|
||||||
|
}
|
||||||
|
|
||||||
|
e, _ := resolver.EndpointFor(endpoints.Ec2metadataServiceID, "")
|
||||||
|
return &ec2rolecreds.EC2RoleProvider{
|
||||||
|
Client: ec2metadata.NewClient(cfg, handlers, e.URL, e.SigningRegion),
|
||||||
|
ExpiryWindow: 5 * time.Minute,
|
||||||
|
}
|
||||||
|
}
|
||||||
27
vendor/github.com/aws/aws-sdk-go/aws/defaults/shared_config.go
generated
vendored
Executable file
27
vendor/github.com/aws/aws-sdk-go/aws/defaults/shared_config.go
generated
vendored
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
package defaults
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/internal/shareddefaults"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SharedCredentialsFilename returns the SDK's default file path
|
||||||
|
// for the shared credentials file.
|
||||||
|
//
|
||||||
|
// Builds the shared config file path based on the OS's platform.
|
||||||
|
//
|
||||||
|
// - Linux/Unix: $HOME/.aws/credentials
|
||||||
|
// - Windows: %USERPROFILE%\.aws\credentials
|
||||||
|
func SharedCredentialsFilename() string {
|
||||||
|
return shareddefaults.SharedCredentialsFilename()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SharedConfigFilename returns the SDK's default file path for
|
||||||
|
// the shared config file.
|
||||||
|
//
|
||||||
|
// Builds the shared config file path based on the OS's platform.
|
||||||
|
//
|
||||||
|
// - Linux/Unix: $HOME/.aws/config
|
||||||
|
// - Windows: %USERPROFILE%\.aws\config
|
||||||
|
func SharedConfigFilename() string {
|
||||||
|
return shareddefaults.SharedConfigFilename()
|
||||||
|
}
|
||||||
56
vendor/github.com/aws/aws-sdk-go/aws/doc.go
generated
vendored
Executable file
56
vendor/github.com/aws/aws-sdk-go/aws/doc.go
generated
vendored
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
// Package aws provides the core SDK's utilities and shared types. Use this package's
|
||||||
|
// utilities to simplify setting and reading API operations parameters.
|
||||||
|
//
|
||||||
|
// Value and Pointer Conversion Utilities
|
||||||
|
//
|
||||||
|
// This package includes a helper conversion utility for each scalar type the SDK's
|
||||||
|
// API use. These utilities make getting a pointer of the scalar, and dereferencing
|
||||||
|
// a pointer easier.
|
||||||
|
//
|
||||||
|
// Each conversion utility comes in two forms. Value to Pointer and Pointer to Value.
|
||||||
|
// The Pointer to value will safely dereference the pointer and return its value.
|
||||||
|
// If the pointer was nil, the scalar's zero value will be returned.
|
||||||
|
//
|
||||||
|
// The value to pointer functions will be named after the scalar type. So get a
|
||||||
|
// *string from a string value use the "String" function. This makes it easy to
|
||||||
|
// to get pointer of a literal string value, because getting the address of a
|
||||||
|
// literal requires assigning the value to a variable first.
|
||||||
|
//
|
||||||
|
// var strPtr *string
|
||||||
|
//
|
||||||
|
// // Without the SDK's conversion functions
|
||||||
|
// str := "my string"
|
||||||
|
// strPtr = &str
|
||||||
|
//
|
||||||
|
// // With the SDK's conversion functions
|
||||||
|
// strPtr = aws.String("my string")
|
||||||
|
//
|
||||||
|
// // Convert *string to string value
|
||||||
|
// str = aws.StringValue(strPtr)
|
||||||
|
//
|
||||||
|
// In addition to scalars the aws package also includes conversion utilities for
|
||||||
|
// map and slice for commonly types used in API parameters. The map and slice
|
||||||
|
// conversion functions use similar naming pattern as the scalar conversion
|
||||||
|
// functions.
|
||||||
|
//
|
||||||
|
// var strPtrs []*string
|
||||||
|
// var strs []string = []string{"Go", "Gophers", "Go"}
|
||||||
|
//
|
||||||
|
// // Convert []string to []*string
|
||||||
|
// strPtrs = aws.StringSlice(strs)
|
||||||
|
//
|
||||||
|
// // Convert []*string to []string
|
||||||
|
// strs = aws.StringValueSlice(strPtrs)
|
||||||
|
//
|
||||||
|
// SDK Default HTTP Client
|
||||||
|
//
|
||||||
|
// The SDK will use the http.DefaultClient if a HTTP client is not provided to
|
||||||
|
// the SDK's Session, or service client constructor. This means that if the
|
||||||
|
// http.DefaultClient is modified by other components of your application the
|
||||||
|
// modifications will be picked up by the SDK as well.
|
||||||
|
//
|
||||||
|
// In some cases this might be intended, but it is a better practice to create
|
||||||
|
// a custom HTTP Client to share explicitly through your application. You can
|
||||||
|
// configure the SDK to use the custom HTTP Client by setting the HTTPClient
|
||||||
|
// value of the SDK's Config type when creating a Session or service client.
|
||||||
|
package aws
|
||||||
169
vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go
generated
vendored
Executable file
169
vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go
generated
vendored
Executable file
@@ -0,0 +1,169 @@
|
|||||||
|
package ec2metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMetadata uses the path provided to request information from the EC2
|
||||||
|
// instance metdata service. The content will be returned as a string, or
|
||||||
|
// error if the request failed.
|
||||||
|
func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
||||||
|
op := &request.Operation{
|
||||||
|
Name: "GetMetadata",
|
||||||
|
HTTPMethod: "GET",
|
||||||
|
HTTPPath: sdkuri.PathJoin("/meta-data", p),
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &metadataOutput{}
|
||||||
|
req := c.NewRequest(op, nil, output)
|
||||||
|
err := req.Send()
|
||||||
|
|
||||||
|
return output.Content, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserData returns the userdata that was configured for the service. If
|
||||||
|
// there is no user-data setup for the EC2 instance a "NotFoundError" error
|
||||||
|
// code will be returned.
|
||||||
|
func (c *EC2Metadata) GetUserData() (string, error) {
|
||||||
|
op := &request.Operation{
|
||||||
|
Name: "GetUserData",
|
||||||
|
HTTPMethod: "GET",
|
||||||
|
HTTPPath: "/user-data",
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &metadataOutput{}
|
||||||
|
req := c.NewRequest(op, nil, output)
|
||||||
|
req.Handlers.UnmarshalError.PushBack(func(r *request.Request) {
|
||||||
|
if r.HTTPResponse.StatusCode == http.StatusNotFound {
|
||||||
|
r.Error = awserr.New("NotFoundError", "user-data not found", r.Error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
err := req.Send()
|
||||||
|
|
||||||
|
return output.Content, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDynamicData uses the path provided to request information from the EC2
|
||||||
|
// instance metadata service for dynamic data. The content will be returned
|
||||||
|
// as a string, or error if the request failed.
|
||||||
|
func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
||||||
|
op := &request.Operation{
|
||||||
|
Name: "GetDynamicData",
|
||||||
|
HTTPMethod: "GET",
|
||||||
|
HTTPPath: sdkuri.PathJoin("/dynamic", p),
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &metadataOutput{}
|
||||||
|
req := c.NewRequest(op, nil, output)
|
||||||
|
err := req.Send()
|
||||||
|
|
||||||
|
return output.Content, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstanceIdentityDocument retrieves an identity document describing an
|
||||||
|
// instance. Error is returned if the request fails or is unable to parse
|
||||||
|
// the response.
|
||||||
|
func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) {
|
||||||
|
resp, err := c.GetDynamicData("instance-identity/document")
|
||||||
|
if err != nil {
|
||||||
|
return EC2InstanceIdentityDocument{},
|
||||||
|
awserr.New("EC2MetadataRequestError",
|
||||||
|
"failed to get EC2 instance identity document", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
doc := EC2InstanceIdentityDocument{}
|
||||||
|
if err := json.NewDecoder(strings.NewReader(resp)).Decode(&doc); err != nil {
|
||||||
|
return EC2InstanceIdentityDocument{},
|
||||||
|
awserr.New("SerializationError",
|
||||||
|
"failed to decode EC2 instance identity document", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IAMInfo retrieves IAM info from the metadata API
|
||||||
|
func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
|
||||||
|
resp, err := c.GetMetadata("iam/info")
|
||||||
|
if err != nil {
|
||||||
|
return EC2IAMInfo{},
|
||||||
|
awserr.New("EC2MetadataRequestError",
|
||||||
|
"failed to get EC2 IAM info", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info := EC2IAMInfo{}
|
||||||
|
if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil {
|
||||||
|
return EC2IAMInfo{},
|
||||||
|
awserr.New("SerializationError",
|
||||||
|
"failed to decode EC2 IAM info", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Code != "Success" {
|
||||||
|
errMsg := fmt.Sprintf("failed to get EC2 IAM Info (%s)", info.Code)
|
||||||
|
return EC2IAMInfo{},
|
||||||
|
awserr.New("EC2MetadataError", errMsg, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Region returns the region the instance is running in.
|
||||||
|
func (c *EC2Metadata) Region() (string, error) {
|
||||||
|
resp, err := c.GetMetadata("placement/availability-zone")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp) == 0 {
|
||||||
|
return "", awserr.New("EC2MetadataError", "invalid Region response", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns region without the suffix. Eg: us-west-2a becomes us-west-2
|
||||||
|
return resp[:len(resp)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Available returns if the application has access to the EC2 Metadata service.
|
||||||
|
// Can be used to determine if application is running within an EC2 Instance and
|
||||||
|
// the metadata service is available.
|
||||||
|
func (c *EC2Metadata) Available() bool {
|
||||||
|
if _, err := c.GetMetadata("instance-id"); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// An EC2IAMInfo provides the shape for unmarshaling
|
||||||
|
// an IAM info from the metadata API
|
||||||
|
type EC2IAMInfo struct {
|
||||||
|
Code string
|
||||||
|
LastUpdated time.Time
|
||||||
|
InstanceProfileArn string
|
||||||
|
InstanceProfileID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// An EC2InstanceIdentityDocument provides the shape for unmarshaling
|
||||||
|
// an instance identity document
|
||||||
|
type EC2InstanceIdentityDocument struct {
|
||||||
|
DevpayProductCodes []string `json:"devpayProductCodes"`
|
||||||
|
AvailabilityZone string `json:"availabilityZone"`
|
||||||
|
PrivateIP string `json:"privateIp"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
InstanceID string `json:"instanceId"`
|
||||||
|
BillingProducts []string `json:"billingProducts"`
|
||||||
|
InstanceType string `json:"instanceType"`
|
||||||
|
AccountID string `json:"accountId"`
|
||||||
|
PendingTime time.Time `json:"pendingTime"`
|
||||||
|
ImageID string `json:"imageId"`
|
||||||
|
KernelID string `json:"kernelId"`
|
||||||
|
RamdiskID string `json:"ramdiskId"`
|
||||||
|
Architecture string `json:"architecture"`
|
||||||
|
}
|
||||||
152
vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go
generated
vendored
Executable file
152
vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go
generated
vendored
Executable file
@@ -0,0 +1,152 @@
|
|||||||
|
// Package ec2metadata provides the client for making API calls to the
|
||||||
|
// EC2 Metadata service.
|
||||||
|
//
|
||||||
|
// This package's client can be disabled completely by setting the environment
|
||||||
|
// variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to
|
||||||
|
// true instructs the SDK to disable the EC2 Metadata client. The client cannot
|
||||||
|
// be used while the environment variable is set to true, (case insensitive).
|
||||||
|
package ec2metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceName is the name of the service.
|
||||||
|
const ServiceName = "ec2metadata"
|
||||||
|
const disableServiceEnvVar = "AWS_EC2_METADATA_DISABLED"
|
||||||
|
|
||||||
|
// A EC2Metadata is an EC2 Metadata service Client.
|
||||||
|
type EC2Metadata struct {
|
||||||
|
*client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new instance of the EC2Metadata client with a session.
|
||||||
|
// This client is safe to use across multiple goroutines.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Create a EC2Metadata client from just a session.
|
||||||
|
// svc := ec2metadata.New(mySession)
|
||||||
|
//
|
||||||
|
// // Create a EC2Metadata client with additional configuration
|
||||||
|
// svc := ec2metadata.New(mySession, aws.NewConfig().WithLogLevel(aws.LogDebugHTTPBody))
|
||||||
|
func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
|
||||||
|
c := p.ClientConfig(ServiceName, cfgs...)
|
||||||
|
return NewClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new EC2Metadata client. Should be used to create
|
||||||
|
// a client when not using a session. Generally using just New with a session
|
||||||
|
// is preferred.
|
||||||
|
//
|
||||||
|
// If an unmodified HTTP client is provided from the stdlib default, or no client
|
||||||
|
// the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened.
|
||||||
|
// To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default.
|
||||||
|
func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string, opts ...func(*client.Client)) *EC2Metadata {
|
||||||
|
if !aws.BoolValue(cfg.EC2MetadataDisableTimeoutOverride) && httpClientZero(cfg.HTTPClient) {
|
||||||
|
// If the http client is unmodified and this feature is not disabled
|
||||||
|
// set custom timeouts for EC2Metadata requests.
|
||||||
|
cfg.HTTPClient = &http.Client{
|
||||||
|
// use a shorter timeout than default because the metadata
|
||||||
|
// service is local if it is running, and to fail faster
|
||||||
|
// if not running on an ec2 instance.
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := &EC2Metadata{
|
||||||
|
Client: client.New(
|
||||||
|
cfg,
|
||||||
|
metadata.ClientInfo{
|
||||||
|
ServiceName: ServiceName,
|
||||||
|
ServiceID: ServiceName,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
APIVersion: "latest",
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.Handlers.Unmarshal.PushBack(unmarshalHandler)
|
||||||
|
svc.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
svc.Handlers.Validate.Clear()
|
||||||
|
svc.Handlers.Validate.PushBack(validateEndpointHandler)
|
||||||
|
|
||||||
|
// Disable the EC2 Metadata service if the environment variable is set.
|
||||||
|
// This shortcirctes the service's functionality to always fail to send
|
||||||
|
// requests.
|
||||||
|
if strings.ToLower(os.Getenv(disableServiceEnvVar)) == "true" {
|
||||||
|
svc.Handlers.Send.SwapNamed(request.NamedHandler{
|
||||||
|
Name: corehandlers.SendHandler.Name,
|
||||||
|
Fn: func(r *request.Request) {
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
Header: http.Header{},
|
||||||
|
}
|
||||||
|
r.Error = awserr.New(
|
||||||
|
request.CanceledErrorCode,
|
||||||
|
"EC2 IMDS access disabled via "+disableServiceEnvVar+" env var",
|
||||||
|
nil)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add additional options to the service config
|
||||||
|
for _, option := range opts {
|
||||||
|
option(svc.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpClientZero(c *http.Client) bool {
|
||||||
|
return c == nil || (c.Transport == nil && c.CheckRedirect == nil && c.Jar == nil && c.Timeout == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type metadataOutput struct {
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalHandler(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata respose", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if data, ok := r.Data.(*metadataOutput); ok {
|
||||||
|
data.Content = b.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalError(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata error respose", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response body format is not consistent between metadata endpoints.
|
||||||
|
// Grab the error message as a string and include that as the source error
|
||||||
|
r.Error = awserr.New("EC2MetadataError", "failed to make EC2Metadata request", errors.New(b.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateEndpointHandler(r *request.Request) {
|
||||||
|
if r.ClientInfo.Endpoint == "" {
|
||||||
|
r.Error = aws.ErrMissingEndpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
188
vendor/github.com/aws/aws-sdk-go/aws/endpoints/decode.go
generated
vendored
Executable file
188
vendor/github.com/aws/aws-sdk-go/aws/endpoints/decode.go
generated
vendored
Executable file
@@ -0,0 +1,188 @@
|
|||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type modelDefinition map[string]json.RawMessage
|
||||||
|
|
||||||
|
// A DecodeModelOptions are the options for how the endpoints model definition
|
||||||
|
// are decoded.
|
||||||
|
type DecodeModelOptions struct {
|
||||||
|
SkipCustomizations bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set combines all of the option functions together.
|
||||||
|
func (d *DecodeModelOptions) Set(optFns ...func(*DecodeModelOptions)) {
|
||||||
|
for _, fn := range optFns {
|
||||||
|
fn(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeModel unmarshals a Regions and Endpoint model definition file into
|
||||||
|
// a endpoint Resolver. If the file format is not supported, or an error occurs
|
||||||
|
// when unmarshaling the model an error will be returned.
|
||||||
|
//
|
||||||
|
// Casting the return value of this func to a EnumPartitions will
|
||||||
|
// allow you to get a list of the partitions in the order the endpoints
|
||||||
|
// will be resolved in.
|
||||||
|
//
|
||||||
|
// resolver, err := endpoints.DecodeModel(reader)
|
||||||
|
//
|
||||||
|
// partitions := resolver.(endpoints.EnumPartitions).Partitions()
|
||||||
|
// for _, p := range partitions {
|
||||||
|
// // ... inspect partitions
|
||||||
|
// }
|
||||||
|
func DecodeModel(r io.Reader, optFns ...func(*DecodeModelOptions)) (Resolver, error) {
|
||||||
|
var opts DecodeModelOptions
|
||||||
|
opts.Set(optFns...)
|
||||||
|
|
||||||
|
// Get the version of the partition file to determine what
|
||||||
|
// unmarshaling model to use.
|
||||||
|
modelDef := modelDefinition{}
|
||||||
|
if err := json.NewDecoder(r).Decode(&modelDef); err != nil {
|
||||||
|
return nil, newDecodeModelError("failed to decode endpoints model", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var version string
|
||||||
|
if b, ok := modelDef["version"]; ok {
|
||||||
|
version = string(b)
|
||||||
|
} else {
|
||||||
|
return nil, newDecodeModelError("endpoints version not found in model", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if version == "3" {
|
||||||
|
return decodeV3Endpoints(modelDef, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, newDecodeModelError(
|
||||||
|
fmt.Sprintf("endpoints version %s, not supported", version), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resolver, error) {
|
||||||
|
b, ok := modelDef["partitions"]
|
||||||
|
if !ok {
|
||||||
|
return nil, newDecodeModelError("endpoints model missing partitions", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
ps := partitions{}
|
||||||
|
if err := json.Unmarshal(b, &ps); err != nil {
|
||||||
|
return nil, newDecodeModelError("failed to decode endpoints model", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.SkipCustomizations {
|
||||||
|
return ps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customization
|
||||||
|
for i := 0; i < len(ps); i++ {
|
||||||
|
p := &ps[i]
|
||||||
|
custAddEC2Metadata(p)
|
||||||
|
custAddS3DualStack(p)
|
||||||
|
custRmIotDataService(p)
|
||||||
|
custFixAppAutoscalingChina(p)
|
||||||
|
custFixAppAutoscalingUsGov(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func custAddS3DualStack(p *partition) {
|
||||||
|
if p.ID != "aws" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
custAddDualstack(p, "s3")
|
||||||
|
custAddDualstack(p, "s3-control")
|
||||||
|
}
|
||||||
|
|
||||||
|
func custAddDualstack(p *partition, svcName string) {
|
||||||
|
s, ok := p.Services[svcName]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Defaults.HasDualStack = boxedTrue
|
||||||
|
s.Defaults.DualStackHostname = "{service}.dualstack.{region}.{dnsSuffix}"
|
||||||
|
|
||||||
|
p.Services[svcName] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
func custAddEC2Metadata(p *partition) {
|
||||||
|
p.Services["ec2metadata"] = service{
|
||||||
|
IsRegionalized: boxedFalse,
|
||||||
|
PartitionEndpoint: "aws-global",
|
||||||
|
Endpoints: endpoints{
|
||||||
|
"aws-global": endpoint{
|
||||||
|
Hostname: "169.254.169.254/latest",
|
||||||
|
Protocols: []string{"http"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func custRmIotDataService(p *partition) {
|
||||||
|
delete(p.Services, "data.iot")
|
||||||
|
}
|
||||||
|
|
||||||
|
func custFixAppAutoscalingChina(p *partition) {
|
||||||
|
if p.ID != "aws-cn" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceName = "application-autoscaling"
|
||||||
|
s, ok := p.Services[serviceName]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectHostname = `autoscaling.{region}.amazonaws.com`
|
||||||
|
if e, a := s.Defaults.Hostname, expectHostname; e != a {
|
||||||
|
fmt.Printf("custFixAppAutoscalingChina: ignoring customization, expected %s, got %s\n", e, a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Defaults.Hostname = expectHostname + ".cn"
|
||||||
|
p.Services[serviceName] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
func custFixAppAutoscalingUsGov(p *partition) {
|
||||||
|
if p.ID != "aws-us-gov" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceName = "application-autoscaling"
|
||||||
|
s, ok := p.Services[serviceName]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if a := s.Defaults.CredentialScope.Service; a != "" {
|
||||||
|
fmt.Printf("custFixAppAutoscalingUsGov: ignoring customization, expected empty credential scope service, got %s\n", a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if a := s.Defaults.Hostname; a != "" {
|
||||||
|
fmt.Printf("custFixAppAutoscalingUsGov: ignoring customization, expected empty hostname, got %s\n", a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Defaults.CredentialScope.Service = "application-autoscaling"
|
||||||
|
s.Defaults.Hostname = "autoscaling.{region}.amazonaws.com"
|
||||||
|
|
||||||
|
p.Services[serviceName] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
type decodeModelError struct {
|
||||||
|
awsError
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDecodeModelError(msg string, err error) decodeModelError {
|
||||||
|
return decodeModelError{
|
||||||
|
awsError: awserr.New("DecodeEndpointsModelError", msg, err),
|
||||||
|
}
|
||||||
|
}
|
||||||
4278
vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go
generated
vendored
Executable file
4278
vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
141
vendor/github.com/aws/aws-sdk-go/aws/endpoints/dep_service_ids.go
generated
vendored
Executable file
141
vendor/github.com/aws/aws-sdk-go/aws/endpoints/dep_service_ids.go
generated
vendored
Executable file
@@ -0,0 +1,141 @@
|
|||||||
|
package endpoints
|
||||||
|
|
||||||
|
// Service identifiers
|
||||||
|
//
|
||||||
|
// Deprecated: Use client package's EndpointID value instead of these
|
||||||
|
// ServiceIDs. These IDs are not maintained, and are out of date.
|
||||||
|
const (
|
||||||
|
A4bServiceID = "a4b" // A4b.
|
||||||
|
AcmServiceID = "acm" // Acm.
|
||||||
|
AcmPcaServiceID = "acm-pca" // AcmPca.
|
||||||
|
ApiMediatailorServiceID = "api.mediatailor" // ApiMediatailor.
|
||||||
|
ApiPricingServiceID = "api.pricing" // ApiPricing.
|
||||||
|
ApiSagemakerServiceID = "api.sagemaker" // ApiSagemaker.
|
||||||
|
ApigatewayServiceID = "apigateway" // Apigateway.
|
||||||
|
ApplicationAutoscalingServiceID = "application-autoscaling" // ApplicationAutoscaling.
|
||||||
|
Appstream2ServiceID = "appstream2" // Appstream2.
|
||||||
|
AppsyncServiceID = "appsync" // Appsync.
|
||||||
|
AthenaServiceID = "athena" // Athena.
|
||||||
|
AutoscalingServiceID = "autoscaling" // Autoscaling.
|
||||||
|
AutoscalingPlansServiceID = "autoscaling-plans" // AutoscalingPlans.
|
||||||
|
BatchServiceID = "batch" // Batch.
|
||||||
|
BudgetsServiceID = "budgets" // Budgets.
|
||||||
|
CeServiceID = "ce" // Ce.
|
||||||
|
ChimeServiceID = "chime" // Chime.
|
||||||
|
Cloud9ServiceID = "cloud9" // Cloud9.
|
||||||
|
ClouddirectoryServiceID = "clouddirectory" // Clouddirectory.
|
||||||
|
CloudformationServiceID = "cloudformation" // Cloudformation.
|
||||||
|
CloudfrontServiceID = "cloudfront" // Cloudfront.
|
||||||
|
CloudhsmServiceID = "cloudhsm" // Cloudhsm.
|
||||||
|
Cloudhsmv2ServiceID = "cloudhsmv2" // Cloudhsmv2.
|
||||||
|
CloudsearchServiceID = "cloudsearch" // Cloudsearch.
|
||||||
|
CloudtrailServiceID = "cloudtrail" // Cloudtrail.
|
||||||
|
CodebuildServiceID = "codebuild" // Codebuild.
|
||||||
|
CodecommitServiceID = "codecommit" // Codecommit.
|
||||||
|
CodedeployServiceID = "codedeploy" // Codedeploy.
|
||||||
|
CodepipelineServiceID = "codepipeline" // Codepipeline.
|
||||||
|
CodestarServiceID = "codestar" // Codestar.
|
||||||
|
CognitoIdentityServiceID = "cognito-identity" // CognitoIdentity.
|
||||||
|
CognitoIdpServiceID = "cognito-idp" // CognitoIdp.
|
||||||
|
CognitoSyncServiceID = "cognito-sync" // CognitoSync.
|
||||||
|
ComprehendServiceID = "comprehend" // Comprehend.
|
||||||
|
ConfigServiceID = "config" // Config.
|
||||||
|
CurServiceID = "cur" // Cur.
|
||||||
|
DatapipelineServiceID = "datapipeline" // Datapipeline.
|
||||||
|
DaxServiceID = "dax" // Dax.
|
||||||
|
DevicefarmServiceID = "devicefarm" // Devicefarm.
|
||||||
|
DirectconnectServiceID = "directconnect" // Directconnect.
|
||||||
|
DiscoveryServiceID = "discovery" // Discovery.
|
||||||
|
DmsServiceID = "dms" // Dms.
|
||||||
|
DsServiceID = "ds" // Ds.
|
||||||
|
DynamodbServiceID = "dynamodb" // Dynamodb.
|
||||||
|
Ec2ServiceID = "ec2" // Ec2.
|
||||||
|
Ec2metadataServiceID = "ec2metadata" // Ec2metadata.
|
||||||
|
EcrServiceID = "ecr" // Ecr.
|
||||||
|
EcsServiceID = "ecs" // Ecs.
|
||||||
|
ElasticacheServiceID = "elasticache" // Elasticache.
|
||||||
|
ElasticbeanstalkServiceID = "elasticbeanstalk" // Elasticbeanstalk.
|
||||||
|
ElasticfilesystemServiceID = "elasticfilesystem" // Elasticfilesystem.
|
||||||
|
ElasticloadbalancingServiceID = "elasticloadbalancing" // Elasticloadbalancing.
|
||||||
|
ElasticmapreduceServiceID = "elasticmapreduce" // Elasticmapreduce.
|
||||||
|
ElastictranscoderServiceID = "elastictranscoder" // Elastictranscoder.
|
||||||
|
EmailServiceID = "email" // Email.
|
||||||
|
EntitlementMarketplaceServiceID = "entitlement.marketplace" // EntitlementMarketplace.
|
||||||
|
EsServiceID = "es" // Es.
|
||||||
|
EventsServiceID = "events" // Events.
|
||||||
|
FirehoseServiceID = "firehose" // Firehose.
|
||||||
|
FmsServiceID = "fms" // Fms.
|
||||||
|
GameliftServiceID = "gamelift" // Gamelift.
|
||||||
|
GlacierServiceID = "glacier" // Glacier.
|
||||||
|
GlueServiceID = "glue" // Glue.
|
||||||
|
GreengrassServiceID = "greengrass" // Greengrass.
|
||||||
|
GuarddutyServiceID = "guardduty" // Guardduty.
|
||||||
|
HealthServiceID = "health" // Health.
|
||||||
|
IamServiceID = "iam" // Iam.
|
||||||
|
ImportexportServiceID = "importexport" // Importexport.
|
||||||
|
InspectorServiceID = "inspector" // Inspector.
|
||||||
|
IotServiceID = "iot" // Iot.
|
||||||
|
IotanalyticsServiceID = "iotanalytics" // Iotanalytics.
|
||||||
|
KinesisServiceID = "kinesis" // Kinesis.
|
||||||
|
KinesisanalyticsServiceID = "kinesisanalytics" // Kinesisanalytics.
|
||||||
|
KinesisvideoServiceID = "kinesisvideo" // Kinesisvideo.
|
||||||
|
KmsServiceID = "kms" // Kms.
|
||||||
|
LambdaServiceID = "lambda" // Lambda.
|
||||||
|
LightsailServiceID = "lightsail" // Lightsail.
|
||||||
|
LogsServiceID = "logs" // Logs.
|
||||||
|
MachinelearningServiceID = "machinelearning" // Machinelearning.
|
||||||
|
MarketplacecommerceanalyticsServiceID = "marketplacecommerceanalytics" // Marketplacecommerceanalytics.
|
||||||
|
MediaconvertServiceID = "mediaconvert" // Mediaconvert.
|
||||||
|
MedialiveServiceID = "medialive" // Medialive.
|
||||||
|
MediapackageServiceID = "mediapackage" // Mediapackage.
|
||||||
|
MediastoreServiceID = "mediastore" // Mediastore.
|
||||||
|
MeteringMarketplaceServiceID = "metering.marketplace" // MeteringMarketplace.
|
||||||
|
MghServiceID = "mgh" // Mgh.
|
||||||
|
MobileanalyticsServiceID = "mobileanalytics" // Mobileanalytics.
|
||||||
|
ModelsLexServiceID = "models.lex" // ModelsLex.
|
||||||
|
MonitoringServiceID = "monitoring" // Monitoring.
|
||||||
|
MturkRequesterServiceID = "mturk-requester" // MturkRequester.
|
||||||
|
NeptuneServiceID = "neptune" // Neptune.
|
||||||
|
OpsworksServiceID = "opsworks" // Opsworks.
|
||||||
|
OpsworksCmServiceID = "opsworks-cm" // OpsworksCm.
|
||||||
|
OrganizationsServiceID = "organizations" // Organizations.
|
||||||
|
PinpointServiceID = "pinpoint" // Pinpoint.
|
||||||
|
PollyServiceID = "polly" // Polly.
|
||||||
|
RdsServiceID = "rds" // Rds.
|
||||||
|
RedshiftServiceID = "redshift" // Redshift.
|
||||||
|
RekognitionServiceID = "rekognition" // Rekognition.
|
||||||
|
ResourceGroupsServiceID = "resource-groups" // ResourceGroups.
|
||||||
|
Route53ServiceID = "route53" // Route53.
|
||||||
|
Route53domainsServiceID = "route53domains" // Route53domains.
|
||||||
|
RuntimeLexServiceID = "runtime.lex" // RuntimeLex.
|
||||||
|
RuntimeSagemakerServiceID = "runtime.sagemaker" // RuntimeSagemaker.
|
||||||
|
S3ServiceID = "s3" // S3.
|
||||||
|
S3ControlServiceID = "s3-control" // S3Control.
|
||||||
|
SagemakerServiceID = "api.sagemaker" // Sagemaker.
|
||||||
|
SdbServiceID = "sdb" // Sdb.
|
||||||
|
SecretsmanagerServiceID = "secretsmanager" // Secretsmanager.
|
||||||
|
ServerlessrepoServiceID = "serverlessrepo" // Serverlessrepo.
|
||||||
|
ServicecatalogServiceID = "servicecatalog" // Servicecatalog.
|
||||||
|
ServicediscoveryServiceID = "servicediscovery" // Servicediscovery.
|
||||||
|
ShieldServiceID = "shield" // Shield.
|
||||||
|
SmsServiceID = "sms" // Sms.
|
||||||
|
SnowballServiceID = "snowball" // Snowball.
|
||||||
|
SnsServiceID = "sns" // Sns.
|
||||||
|
SqsServiceID = "sqs" // Sqs.
|
||||||
|
SsmServiceID = "ssm" // Ssm.
|
||||||
|
StatesServiceID = "states" // States.
|
||||||
|
StoragegatewayServiceID = "storagegateway" // Storagegateway.
|
||||||
|
StreamsDynamodbServiceID = "streams.dynamodb" // StreamsDynamodb.
|
||||||
|
StsServiceID = "sts" // Sts.
|
||||||
|
SupportServiceID = "support" // Support.
|
||||||
|
SwfServiceID = "swf" // Swf.
|
||||||
|
TaggingServiceID = "tagging" // Tagging.
|
||||||
|
TransferServiceID = "transfer" // Transfer.
|
||||||
|
TranslateServiceID = "translate" // Translate.
|
||||||
|
WafServiceID = "waf" // Waf.
|
||||||
|
WafRegionalServiceID = "waf-regional" // WafRegional.
|
||||||
|
WorkdocsServiceID = "workdocs" // Workdocs.
|
||||||
|
WorkmailServiceID = "workmail" // Workmail.
|
||||||
|
WorkspacesServiceID = "workspaces" // Workspaces.
|
||||||
|
XrayServiceID = "xray" // Xray.
|
||||||
|
)
|
||||||
66
vendor/github.com/aws/aws-sdk-go/aws/endpoints/doc.go
generated
vendored
Executable file
66
vendor/github.com/aws/aws-sdk-go/aws/endpoints/doc.go
generated
vendored
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
// Package endpoints provides the types and functionality for defining regions
|
||||||
|
// and endpoints, as well as querying those definitions.
|
||||||
|
//
|
||||||
|
// The SDK's Regions and Endpoints metadata is code generated into the endpoints
|
||||||
|
// package, and is accessible via the DefaultResolver function. This function
|
||||||
|
// returns a endpoint Resolver will search the metadata and build an associated
|
||||||
|
// endpoint if one is found. The default resolver will search all partitions
|
||||||
|
// known by the SDK. e.g AWS Standard (aws), AWS China (aws-cn), and
|
||||||
|
// AWS GovCloud (US) (aws-us-gov).
|
||||||
|
// .
|
||||||
|
//
|
||||||
|
// Enumerating Regions and Endpoint Metadata
|
||||||
|
//
|
||||||
|
// Casting the Resolver returned by DefaultResolver to a EnumPartitions interface
|
||||||
|
// will allow you to get access to the list of underlying Partitions with the
|
||||||
|
// Partitions method. This is helpful if you want to limit the SDK's endpoint
|
||||||
|
// resolving to a single partition, or enumerate regions, services, and endpoints
|
||||||
|
// in the partition.
|
||||||
|
//
|
||||||
|
// resolver := endpoints.DefaultResolver()
|
||||||
|
// partitions := resolver.(endpoints.EnumPartitions).Partitions()
|
||||||
|
//
|
||||||
|
// for _, p := range partitions {
|
||||||
|
// fmt.Println("Regions for", p.ID())
|
||||||
|
// for id, _ := range p.Regions() {
|
||||||
|
// fmt.Println("*", id)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Println("Services for", p.ID())
|
||||||
|
// for id, _ := range p.Services() {
|
||||||
|
// fmt.Println("*", id)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Using Custom Endpoints
|
||||||
|
//
|
||||||
|
// The endpoints package also gives you the ability to use your own logic how
|
||||||
|
// endpoints are resolved. This is a great way to define a custom endpoint
|
||||||
|
// for select services, without passing that logic down through your code.
|
||||||
|
//
|
||||||
|
// If a type implements the Resolver interface it can be used to resolve
|
||||||
|
// endpoints. To use this with the SDK's Session and Config set the value
|
||||||
|
// of the type to the EndpointsResolver field of aws.Config when initializing
|
||||||
|
// the session, or service client.
|
||||||
|
//
|
||||||
|
// In addition the ResolverFunc is a wrapper for a func matching the signature
|
||||||
|
// of Resolver.EndpointFor, converting it to a type that satisfies the
|
||||||
|
// Resolver interface.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// myCustomResolver := func(service, region string, optFns ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
|
||||||
|
// if service == endpoints.S3ServiceID {
|
||||||
|
// return endpoints.ResolvedEndpoint{
|
||||||
|
// URL: "s3.custom.endpoint.com",
|
||||||
|
// SigningRegion: "custom-signing-region",
|
||||||
|
// }, nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return endpoints.DefaultResolver().EndpointFor(service, region, optFns...)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// sess := session.Must(session.NewSession(&aws.Config{
|
||||||
|
// Region: aws.String("us-west-2"),
|
||||||
|
// EndpointResolver: endpoints.ResolverFunc(myCustomResolver),
|
||||||
|
// }))
|
||||||
|
package endpoints
|
||||||
449
vendor/github.com/aws/aws-sdk-go/aws/endpoints/endpoints.go
generated
vendored
Executable file
449
vendor/github.com/aws/aws-sdk-go/aws/endpoints/endpoints.go
generated
vendored
Executable file
@@ -0,0 +1,449 @@
|
|||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options provide the configuration needed to direct how the
|
||||||
|
// endpoints will be resolved.
|
||||||
|
type Options struct {
|
||||||
|
// DisableSSL forces the endpoint to be resolved as HTTP.
|
||||||
|
// instead of HTTPS if the service supports it.
|
||||||
|
DisableSSL bool
|
||||||
|
|
||||||
|
// Sets the resolver to resolve the endpoint as a dualstack endpoint
|
||||||
|
// for the service. If dualstack support for a service is not known and
|
||||||
|
// StrictMatching is not enabled a dualstack endpoint for the service will
|
||||||
|
// be returned. This endpoint may not be valid. If StrictMatching is
|
||||||
|
// enabled only services that are known to support dualstack will return
|
||||||
|
// dualstack endpoints.
|
||||||
|
UseDualStack bool
|
||||||
|
|
||||||
|
// Enables strict matching of services and regions resolved endpoints.
|
||||||
|
// If the partition doesn't enumerate the exact service and region an
|
||||||
|
// error will be returned. This option will prevent returning endpoints
|
||||||
|
// that look valid, but may not resolve to any real endpoint.
|
||||||
|
StrictMatching bool
|
||||||
|
|
||||||
|
// Enables resolving a service endpoint based on the region provided if the
|
||||||
|
// service does not exist. The service endpoint ID will be used as the service
|
||||||
|
// domain name prefix. By default the endpoint resolver requires the service
|
||||||
|
// to be known when resolving endpoints.
|
||||||
|
//
|
||||||
|
// If resolving an endpoint on the partition list the provided region will
|
||||||
|
// be used to determine which partition's domain name pattern to the service
|
||||||
|
// endpoint ID with. If both the service and region are unknown and resolving
|
||||||
|
// the endpoint on partition list an UnknownEndpointError error will be returned.
|
||||||
|
//
|
||||||
|
// If resolving and endpoint on a partition specific resolver that partition's
|
||||||
|
// domain name pattern will be used with the service endpoint ID. If both
|
||||||
|
// region and service do not exist when resolving an endpoint on a specific
|
||||||
|
// partition the partition's domain pattern will be used to combine the
|
||||||
|
// endpoint and region together.
|
||||||
|
//
|
||||||
|
// This option is ignored if StrictMatching is enabled.
|
||||||
|
ResolveUnknownService bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set combines all of the option functions together.
|
||||||
|
func (o *Options) Set(optFns ...func(*Options)) {
|
||||||
|
for _, fn := range optFns {
|
||||||
|
fn(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableSSLOption sets the DisableSSL options. Can be used as a functional
|
||||||
|
// option when resolving endpoints.
|
||||||
|
func DisableSSLOption(o *Options) {
|
||||||
|
o.DisableSSL = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseDualStackOption sets the UseDualStack option. Can be used as a functional
|
||||||
|
// option when resolving endpoints.
|
||||||
|
func UseDualStackOption(o *Options) {
|
||||||
|
o.UseDualStack = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrictMatchingOption sets the StrictMatching option. Can be used as a functional
|
||||||
|
// option when resolving endpoints.
|
||||||
|
func StrictMatchingOption(o *Options) {
|
||||||
|
o.StrictMatching = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveUnknownServiceOption sets the ResolveUnknownService option. Can be used
|
||||||
|
// as a functional option when resolving endpoints.
|
||||||
|
func ResolveUnknownServiceOption(o *Options) {
|
||||||
|
o.ResolveUnknownService = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Resolver provides the interface for functionality to resolve endpoints.
|
||||||
|
// The build in Partition and DefaultResolver return value satisfy this interface.
|
||||||
|
type Resolver interface {
|
||||||
|
EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolverFunc is a helper utility that wraps a function so it satisfies the
|
||||||
|
// Resolver interface. This is useful when you want to add additional endpoint
|
||||||
|
// resolving logic, or stub out specific endpoints with custom values.
|
||||||
|
type ResolverFunc func(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error)
|
||||||
|
|
||||||
|
// EndpointFor wraps the ResolverFunc function to satisfy the Resolver interface.
|
||||||
|
func (fn ResolverFunc) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
|
||||||
|
return fn(service, region, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var schemeRE = regexp.MustCompile("^([^:]+)://")
|
||||||
|
|
||||||
|
// AddScheme adds the HTTP or HTTPS schemes to a endpoint URL if there is no
|
||||||
|
// scheme. If disableSSL is true HTTP will set HTTP instead of the default HTTPS.
|
||||||
|
//
|
||||||
|
// If disableSSL is set, it will only set the URL's scheme if the URL does not
|
||||||
|
// contain a scheme.
|
||||||
|
func AddScheme(endpoint string, disableSSL bool) string {
|
||||||
|
if !schemeRE.MatchString(endpoint) {
|
||||||
|
scheme := "https"
|
||||||
|
if disableSSL {
|
||||||
|
scheme = "http"
|
||||||
|
}
|
||||||
|
endpoint = fmt.Sprintf("%s://%s", scheme, endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumPartitions a provides a way to retrieve the underlying partitions that
|
||||||
|
// make up the SDK's default Resolver, or any resolver decoded from a model
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// Use this interface with DefaultResolver and DecodeModels to get the list of
|
||||||
|
// Partitions.
|
||||||
|
type EnumPartitions interface {
|
||||||
|
Partitions() []Partition
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegionsForService returns a map of regions for the partition and service.
|
||||||
|
// If either the partition or service does not exist false will be returned
|
||||||
|
// as the second parameter.
|
||||||
|
//
|
||||||
|
// This example shows how to get the regions for DynamoDB in the AWS partition.
|
||||||
|
// rs, exists := endpoints.RegionsForService(endpoints.DefaultPartitions(), endpoints.AwsPartitionID, endpoints.DynamodbServiceID)
|
||||||
|
//
|
||||||
|
// This is equivalent to using the partition directly.
|
||||||
|
// rs := endpoints.AwsPartition().Services()[endpoints.DynamodbServiceID].Regions()
|
||||||
|
func RegionsForService(ps []Partition, partitionID, serviceID string) (map[string]Region, bool) {
|
||||||
|
for _, p := range ps {
|
||||||
|
if p.ID() != partitionID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := p.p.Services[serviceID]; !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s := Service{
|
||||||
|
id: serviceID,
|
||||||
|
p: p.p,
|
||||||
|
}
|
||||||
|
return s.Regions(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]Region{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PartitionForRegion returns the first partition which includes the region
|
||||||
|
// passed in. This includes both known regions and regions which match
|
||||||
|
// a pattern supported by the partition which may include regions that are
|
||||||
|
// not explicitly known by the partition. Use the Regions method of the
|
||||||
|
// returned Partition if explicit support is needed.
|
||||||
|
func PartitionForRegion(ps []Partition, regionID string) (Partition, bool) {
|
||||||
|
for _, p := range ps {
|
||||||
|
if _, ok := p.p.Regions[regionID]; ok || p.p.RegionRegex.MatchString(regionID) {
|
||||||
|
return p, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Partition{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Partition provides the ability to enumerate the partition's regions
|
||||||
|
// and services.
|
||||||
|
type Partition struct {
|
||||||
|
id string
|
||||||
|
p *partition
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the identifier of the partition.
|
||||||
|
func (p Partition) ID() string { return p.id }
|
||||||
|
|
||||||
|
// EndpointFor attempts to resolve the endpoint based on service and region.
|
||||||
|
// See Options for information on configuring how the endpoint is resolved.
|
||||||
|
//
|
||||||
|
// If the service cannot be found in the metadata the UnknownServiceError
|
||||||
|
// error will be returned. This validation will occur regardless if
|
||||||
|
// StrictMatching is enabled. To enable resolving unknown services set the
|
||||||
|
// "ResolveUnknownService" option to true. When StrictMatching is disabled
|
||||||
|
// this option allows the partition resolver to resolve a endpoint based on
|
||||||
|
// the service endpoint ID provided.
|
||||||
|
//
|
||||||
|
// When resolving endpoints you can choose to enable StrictMatching. This will
|
||||||
|
// require the provided service and region to be known by the partition.
|
||||||
|
// If the endpoint cannot be strictly resolved an error will be returned. This
|
||||||
|
// mode is useful to ensure the endpoint resolved is valid. Without
|
||||||
|
// StrictMatching enabled the endpoint returned my look valid but may not work.
|
||||||
|
// StrictMatching requires the SDK to be updated if you want to take advantage
|
||||||
|
// of new regions and services expansions.
|
||||||
|
//
|
||||||
|
// Errors that can be returned.
|
||||||
|
// * UnknownServiceError
|
||||||
|
// * UnknownEndpointError
|
||||||
|
func (p Partition) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
|
||||||
|
return p.p.EndpointFor(service, region, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regions returns a map of Regions indexed by their ID. This is useful for
|
||||||
|
// enumerating over the regions in a partition.
|
||||||
|
func (p Partition) Regions() map[string]Region {
|
||||||
|
rs := map[string]Region{}
|
||||||
|
for id, r := range p.p.Regions {
|
||||||
|
rs[id] = Region{
|
||||||
|
id: id,
|
||||||
|
desc: r.Description,
|
||||||
|
p: p.p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Services returns a map of Service indexed by their ID. This is useful for
|
||||||
|
// enumerating over the services in a partition.
|
||||||
|
func (p Partition) Services() map[string]Service {
|
||||||
|
ss := map[string]Service{}
|
||||||
|
for id := range p.p.Services {
|
||||||
|
ss[id] = Service{
|
||||||
|
id: id,
|
||||||
|
p: p.p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Region provides information about a region, and ability to resolve an
|
||||||
|
// endpoint from the context of a region, given a service.
|
||||||
|
type Region struct {
|
||||||
|
id, desc string
|
||||||
|
p *partition
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the region's identifier.
|
||||||
|
func (r Region) ID() string { return r.id }
|
||||||
|
|
||||||
|
// Description returns the region's description. The region description
|
||||||
|
// is free text, it can be empty, and it may change between SDK releases.
|
||||||
|
func (r Region) Description() string { return r.desc }
|
||||||
|
|
||||||
|
// ResolveEndpoint resolves an endpoint from the context of the region given
|
||||||
|
// a service. See Partition.EndpointFor for usage and errors that can be returned.
|
||||||
|
func (r Region) ResolveEndpoint(service string, opts ...func(*Options)) (ResolvedEndpoint, error) {
|
||||||
|
return r.p.EndpointFor(service, r.id, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Services returns a list of all services that are known to be in this region.
|
||||||
|
func (r Region) Services() map[string]Service {
|
||||||
|
ss := map[string]Service{}
|
||||||
|
for id, s := range r.p.Services {
|
||||||
|
if _, ok := s.Endpoints[r.id]; ok {
|
||||||
|
ss[id] = Service{
|
||||||
|
id: id,
|
||||||
|
p: r.p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Service provides information about a service, and ability to resolve an
|
||||||
|
// endpoint from the context of a service, given a region.
|
||||||
|
type Service struct {
|
||||||
|
id string
|
||||||
|
p *partition
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the identifier for the service.
|
||||||
|
func (s Service) ID() string { return s.id }
|
||||||
|
|
||||||
|
// ResolveEndpoint resolves an endpoint from the context of a service given
|
||||||
|
// a region. See Partition.EndpointFor for usage and errors that can be returned.
|
||||||
|
func (s Service) ResolveEndpoint(region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
|
||||||
|
return s.p.EndpointFor(s.id, region, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regions returns a map of Regions that the service is present in.
|
||||||
|
//
|
||||||
|
// A region is the AWS region the service exists in. Whereas a Endpoint is
|
||||||
|
// an URL that can be resolved to a instance of a service.
|
||||||
|
func (s Service) Regions() map[string]Region {
|
||||||
|
rs := map[string]Region{}
|
||||||
|
for id := range s.p.Services[s.id].Endpoints {
|
||||||
|
if r, ok := s.p.Regions[id]; ok {
|
||||||
|
rs[id] = Region{
|
||||||
|
id: id,
|
||||||
|
desc: r.Description,
|
||||||
|
p: s.p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoints returns a map of Endpoints indexed by their ID for all known
|
||||||
|
// endpoints for a service.
|
||||||
|
//
|
||||||
|
// A region is the AWS region the service exists in. Whereas a Endpoint is
|
||||||
|
// an URL that can be resolved to a instance of a service.
|
||||||
|
func (s Service) Endpoints() map[string]Endpoint {
|
||||||
|
es := map[string]Endpoint{}
|
||||||
|
for id := range s.p.Services[s.id].Endpoints {
|
||||||
|
es[id] = Endpoint{
|
||||||
|
id: id,
|
||||||
|
serviceID: s.id,
|
||||||
|
p: s.p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return es
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Endpoint provides information about endpoints, and provides the ability
|
||||||
|
// to resolve that endpoint for the service, and the region the endpoint
|
||||||
|
// represents.
|
||||||
|
type Endpoint struct {
|
||||||
|
id string
|
||||||
|
serviceID string
|
||||||
|
p *partition
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the identifier for an endpoint.
|
||||||
|
func (e Endpoint) ID() string { return e.id }
|
||||||
|
|
||||||
|
// ServiceID returns the identifier the endpoint belongs to.
|
||||||
|
func (e Endpoint) ServiceID() string { return e.serviceID }
|
||||||
|
|
||||||
|
// ResolveEndpoint resolves an endpoint from the context of a service and
|
||||||
|
// region the endpoint represents. See Partition.EndpointFor for usage and
|
||||||
|
// errors that can be returned.
|
||||||
|
func (e Endpoint) ResolveEndpoint(opts ...func(*Options)) (ResolvedEndpoint, error) {
|
||||||
|
return e.p.EndpointFor(e.serviceID, e.id, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ResolvedEndpoint is an endpoint that has been resolved based on a partition
|
||||||
|
// service, and region.
|
||||||
|
type ResolvedEndpoint struct {
|
||||||
|
// The endpoint URL
|
||||||
|
URL string
|
||||||
|
|
||||||
|
// The region that should be used for signing requests.
|
||||||
|
SigningRegion string
|
||||||
|
|
||||||
|
// The service name that should be used for signing requests.
|
||||||
|
SigningName string
|
||||||
|
|
||||||
|
// States that the signing name for this endpoint was derived from metadata
|
||||||
|
// passed in, but was not explicitly modeled.
|
||||||
|
SigningNameDerived bool
|
||||||
|
|
||||||
|
// The signing method that should be used for signing requests.
|
||||||
|
SigningMethod string
|
||||||
|
}
|
||||||
|
|
||||||
|
// So that the Error interface type can be included as an anonymous field
|
||||||
|
// in the requestError struct and not conflict with the error.Error() method.
|
||||||
|
type awsError awserr.Error
|
||||||
|
|
||||||
|
// A EndpointNotFoundError is returned when in StrictMatching mode, and the
|
||||||
|
// endpoint for the service and region cannot be found in any of the partitions.
|
||||||
|
type EndpointNotFoundError struct {
|
||||||
|
awsError
|
||||||
|
Partition string
|
||||||
|
Service string
|
||||||
|
Region string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A UnknownServiceError is returned when the service does not resolve to an
|
||||||
|
// endpoint. Includes a list of all known services for the partition. Returned
|
||||||
|
// when a partition does not support the service.
|
||||||
|
type UnknownServiceError struct {
|
||||||
|
awsError
|
||||||
|
Partition string
|
||||||
|
Service string
|
||||||
|
Known []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnknownServiceError builds and returns UnknownServiceError.
|
||||||
|
func NewUnknownServiceError(p, s string, known []string) UnknownServiceError {
|
||||||
|
return UnknownServiceError{
|
||||||
|
awsError: awserr.New("UnknownServiceError",
|
||||||
|
"could not resolve endpoint for unknown service", nil),
|
||||||
|
Partition: p,
|
||||||
|
Service: s,
|
||||||
|
Known: known,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
func (e UnknownServiceError) Error() string {
|
||||||
|
extra := fmt.Sprintf("partition: %q, service: %q",
|
||||||
|
e.Partition, e.Service)
|
||||||
|
if len(e.Known) > 0 {
|
||||||
|
extra += fmt.Sprintf(", known: %v", e.Known)
|
||||||
|
}
|
||||||
|
return awserr.SprintError(e.Code(), e.Message(), extra, e.OrigErr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
func (e UnknownServiceError) String() string {
|
||||||
|
return e.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A UnknownEndpointError is returned when in StrictMatching mode and the
|
||||||
|
// service is valid, but the region does not resolve to an endpoint. Includes
|
||||||
|
// a list of all known endpoints for the service.
|
||||||
|
type UnknownEndpointError struct {
|
||||||
|
awsError
|
||||||
|
Partition string
|
||||||
|
Service string
|
||||||
|
Region string
|
||||||
|
Known []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnknownEndpointError builds and returns UnknownEndpointError.
|
||||||
|
func NewUnknownEndpointError(p, s, r string, known []string) UnknownEndpointError {
|
||||||
|
return UnknownEndpointError{
|
||||||
|
awsError: awserr.New("UnknownEndpointError",
|
||||||
|
"could not resolve endpoint", nil),
|
||||||
|
Partition: p,
|
||||||
|
Service: s,
|
||||||
|
Region: r,
|
||||||
|
Known: known,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
func (e UnknownEndpointError) Error() string {
|
||||||
|
extra := fmt.Sprintf("partition: %q, service: %q, region: %q",
|
||||||
|
e.Partition, e.Service, e.Region)
|
||||||
|
if len(e.Known) > 0 {
|
||||||
|
extra += fmt.Sprintf(", known: %v", e.Known)
|
||||||
|
}
|
||||||
|
return awserr.SprintError(e.Code(), e.Message(), extra, e.OrigErr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
func (e UnknownEndpointError) String() string {
|
||||||
|
return e.Error()
|
||||||
|
}
|
||||||
307
vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model.go
generated
vendored
Executable file
307
vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model.go
generated
vendored
Executable file
@@ -0,0 +1,307 @@
|
|||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type partitions []partition
|
||||||
|
|
||||||
|
func (ps partitions) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
|
||||||
|
var opt Options
|
||||||
|
opt.Set(opts...)
|
||||||
|
|
||||||
|
for i := 0; i < len(ps); i++ {
|
||||||
|
if !ps[i].canResolveEndpoint(service, region, opt.StrictMatching) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps[i].EndpointFor(service, region, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If loose matching fallback to first partition format to use
|
||||||
|
// when resolving the endpoint.
|
||||||
|
if !opt.StrictMatching && len(ps) > 0 {
|
||||||
|
return ps[0].EndpointFor(service, region, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolvedEndpoint{}, NewUnknownEndpointError("all partitions", service, region, []string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partitions satisfies the EnumPartitions interface and returns a list
|
||||||
|
// of Partitions representing each partition represented in the SDK's
|
||||||
|
// endpoints model.
|
||||||
|
func (ps partitions) Partitions() []Partition {
|
||||||
|
parts := make([]Partition, 0, len(ps))
|
||||||
|
for i := 0; i < len(ps); i++ {
|
||||||
|
parts = append(parts, ps[i].Partition())
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
|
type partition struct {
|
||||||
|
ID string `json:"partition"`
|
||||||
|
Name string `json:"partitionName"`
|
||||||
|
DNSSuffix string `json:"dnsSuffix"`
|
||||||
|
RegionRegex regionRegex `json:"regionRegex"`
|
||||||
|
Defaults endpoint `json:"defaults"`
|
||||||
|
Regions regions `json:"regions"`
|
||||||
|
Services services `json:"services"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p partition) Partition() Partition {
|
||||||
|
return Partition{
|
||||||
|
id: p.ID,
|
||||||
|
p: &p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p partition) canResolveEndpoint(service, region string, strictMatch bool) bool {
|
||||||
|
s, hasService := p.Services[service]
|
||||||
|
_, hasEndpoint := s.Endpoints[region]
|
||||||
|
|
||||||
|
if hasEndpoint && hasService {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if strictMatch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.RegionRegex.MatchString(region)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p partition) EndpointFor(service, region string, opts ...func(*Options)) (resolved ResolvedEndpoint, err error) {
|
||||||
|
var opt Options
|
||||||
|
opt.Set(opts...)
|
||||||
|
|
||||||
|
s, hasService := p.Services[service]
|
||||||
|
if !(hasService || opt.ResolveUnknownService) {
|
||||||
|
// Only return error if the resolver will not fallback to creating
|
||||||
|
// endpoint based on service endpoint ID passed in.
|
||||||
|
return resolved, NewUnknownServiceError(p.ID, service, serviceList(p.Services))
|
||||||
|
}
|
||||||
|
|
||||||
|
e, hasEndpoint := s.endpointForRegion(region)
|
||||||
|
if !hasEndpoint && opt.StrictMatching {
|
||||||
|
return resolved, NewUnknownEndpointError(p.ID, service, region, endpointList(s.Endpoints))
|
||||||
|
}
|
||||||
|
|
||||||
|
defs := []endpoint{p.Defaults, s.Defaults}
|
||||||
|
return e.resolve(service, region, p.DNSSuffix, defs, opt), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func serviceList(ss services) []string {
|
||||||
|
list := make([]string, 0, len(ss))
|
||||||
|
for k := range ss {
|
||||||
|
list = append(list, k)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
func endpointList(es endpoints) []string {
|
||||||
|
list := make([]string, 0, len(es))
|
||||||
|
for k := range es {
|
||||||
|
list = append(list, k)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
type regionRegex struct {
|
||||||
|
*regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *regionRegex) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
// Strip leading and trailing quotes
|
||||||
|
regex, err := strconv.Unquote(string(b))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to strip quotes from regex, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rr.Regexp, err = regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to unmarshal region regex, %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type regions map[string]region
|
||||||
|
|
||||||
|
type region struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type services map[string]service
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
PartitionEndpoint string `json:"partitionEndpoint"`
|
||||||
|
IsRegionalized boxedBool `json:"isRegionalized,omitempty"`
|
||||||
|
Defaults endpoint `json:"defaults"`
|
||||||
|
Endpoints endpoints `json:"endpoints"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) endpointForRegion(region string) (endpoint, bool) {
|
||||||
|
if s.IsRegionalized == boxedFalse {
|
||||||
|
return s.Endpoints[s.PartitionEndpoint], region == s.PartitionEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, ok := s.Endpoints[region]; ok {
|
||||||
|
return e, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unable to find any matching endpoint, return
|
||||||
|
// blank that will be used for generic endpoint creation.
|
||||||
|
return endpoint{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
type endpoints map[string]endpoint
|
||||||
|
|
||||||
|
type endpoint struct {
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Protocols []string `json:"protocols"`
|
||||||
|
CredentialScope credentialScope `json:"credentialScope"`
|
||||||
|
|
||||||
|
// Custom fields not modeled
|
||||||
|
HasDualStack boxedBool `json:"-"`
|
||||||
|
DualStackHostname string `json:"-"`
|
||||||
|
|
||||||
|
// Signature Version not used
|
||||||
|
SignatureVersions []string `json:"signatureVersions"`
|
||||||
|
|
||||||
|
// SSLCommonName not used.
|
||||||
|
SSLCommonName string `json:"sslCommonName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultProtocol = "https"
|
||||||
|
defaultSigner = "v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
protocolPriority = []string{"https", "http"}
|
||||||
|
signerPriority = []string{"v4", "v2"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getByPriority(s []string, p []string, def string) string {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(p); i++ {
|
||||||
|
for j := 0; j < len(s); j++ {
|
||||||
|
if s[j] == p[i] {
|
||||||
|
return s[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e endpoint) resolve(service, region, dnsSuffix string, defs []endpoint, opts Options) ResolvedEndpoint {
|
||||||
|
var merged endpoint
|
||||||
|
for _, def := range defs {
|
||||||
|
merged.mergeIn(def)
|
||||||
|
}
|
||||||
|
merged.mergeIn(e)
|
||||||
|
e = merged
|
||||||
|
|
||||||
|
hostname := e.Hostname
|
||||||
|
|
||||||
|
// Offset the hostname for dualstack if enabled
|
||||||
|
if opts.UseDualStack && e.HasDualStack == boxedTrue {
|
||||||
|
hostname = e.DualStackHostname
|
||||||
|
}
|
||||||
|
|
||||||
|
u := strings.Replace(hostname, "{service}", service, 1)
|
||||||
|
u = strings.Replace(u, "{region}", region, 1)
|
||||||
|
u = strings.Replace(u, "{dnsSuffix}", dnsSuffix, 1)
|
||||||
|
|
||||||
|
scheme := getEndpointScheme(e.Protocols, opts.DisableSSL)
|
||||||
|
u = fmt.Sprintf("%s://%s", scheme, u)
|
||||||
|
|
||||||
|
signingRegion := e.CredentialScope.Region
|
||||||
|
if len(signingRegion) == 0 {
|
||||||
|
signingRegion = region
|
||||||
|
}
|
||||||
|
|
||||||
|
signingName := e.CredentialScope.Service
|
||||||
|
var signingNameDerived bool
|
||||||
|
if len(signingName) == 0 {
|
||||||
|
signingName = service
|
||||||
|
signingNameDerived = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolvedEndpoint{
|
||||||
|
URL: u,
|
||||||
|
SigningRegion: signingRegion,
|
||||||
|
SigningName: signingName,
|
||||||
|
SigningNameDerived: signingNameDerived,
|
||||||
|
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEndpointScheme(protocols []string, disableSSL bool) string {
|
||||||
|
if disableSSL {
|
||||||
|
return "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
return getByPriority(protocols, protocolPriority, defaultProtocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *endpoint) mergeIn(other endpoint) {
|
||||||
|
if len(other.Hostname) > 0 {
|
||||||
|
e.Hostname = other.Hostname
|
||||||
|
}
|
||||||
|
if len(other.Protocols) > 0 {
|
||||||
|
e.Protocols = other.Protocols
|
||||||
|
}
|
||||||
|
if len(other.SignatureVersions) > 0 {
|
||||||
|
e.SignatureVersions = other.SignatureVersions
|
||||||
|
}
|
||||||
|
if len(other.CredentialScope.Region) > 0 {
|
||||||
|
e.CredentialScope.Region = other.CredentialScope.Region
|
||||||
|
}
|
||||||
|
if len(other.CredentialScope.Service) > 0 {
|
||||||
|
e.CredentialScope.Service = other.CredentialScope.Service
|
||||||
|
}
|
||||||
|
if len(other.SSLCommonName) > 0 {
|
||||||
|
e.SSLCommonName = other.SSLCommonName
|
||||||
|
}
|
||||||
|
if other.HasDualStack != boxedBoolUnset {
|
||||||
|
e.HasDualStack = other.HasDualStack
|
||||||
|
}
|
||||||
|
if len(other.DualStackHostname) > 0 {
|
||||||
|
e.DualStackHostname = other.DualStackHostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type credentialScope struct {
|
||||||
|
Region string `json:"region"`
|
||||||
|
Service string `json:"service"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type boxedBool int
|
||||||
|
|
||||||
|
func (b *boxedBool) UnmarshalJSON(buf []byte) error {
|
||||||
|
v, err := strconv.ParseBool(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if v {
|
||||||
|
*b = boxedTrue
|
||||||
|
} else {
|
||||||
|
*b = boxedFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
boxedBoolUnset boxedBool = iota
|
||||||
|
boxedFalse
|
||||||
|
boxedTrue
|
||||||
|
)
|
||||||
351
vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model_codegen.go
generated
vendored
Executable file
351
vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model_codegen.go
generated
vendored
Executable file
@@ -0,0 +1,351 @@
|
|||||||
|
// +build codegen
|
||||||
|
|
||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A CodeGenOptions are the options for code generating the endpoints into
|
||||||
|
// Go code from the endpoints model definition.
|
||||||
|
type CodeGenOptions struct {
|
||||||
|
// Options for how the model will be decoded.
|
||||||
|
DecodeModelOptions DecodeModelOptions
|
||||||
|
|
||||||
|
// Disables code generation of the service endpoint prefix IDs defined in
|
||||||
|
// the model.
|
||||||
|
DisableGenerateServiceIDs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set combines all of the option functions together
|
||||||
|
func (d *CodeGenOptions) Set(optFns ...func(*CodeGenOptions)) {
|
||||||
|
for _, fn := range optFns {
|
||||||
|
fn(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeGenModel given a endpoints model file will decode it and attempt to
|
||||||
|
// generate Go code from the model definition. Error will be returned if
|
||||||
|
// the code is unable to be generated, or decoded.
|
||||||
|
func CodeGenModel(modelFile io.Reader, outFile io.Writer, optFns ...func(*CodeGenOptions)) error {
|
||||||
|
var opts CodeGenOptions
|
||||||
|
opts.Set(optFns...)
|
||||||
|
|
||||||
|
resolver, err := DecodeModel(modelFile, func(d *DecodeModelOptions) {
|
||||||
|
*d = opts.DecodeModelOptions
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := struct {
|
||||||
|
Resolver
|
||||||
|
CodeGenOptions
|
||||||
|
}{
|
||||||
|
Resolver: resolver,
|
||||||
|
CodeGenOptions: opts,
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := template.Must(template.New("tmpl").Funcs(funcMap).Parse(v3Tmpl))
|
||||||
|
if err := tmpl.ExecuteTemplate(outFile, "defaults", v); err != nil {
|
||||||
|
return fmt.Errorf("failed to execute template, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSymbol(v string) string {
|
||||||
|
out := []rune{}
|
||||||
|
for _, c := range strings.Title(v) {
|
||||||
|
if !(unicode.IsNumber(c) || unicode.IsLetter(c)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func quoteString(v string) string {
|
||||||
|
return fmt.Sprintf("%q", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regionConstName(p, r string) string {
|
||||||
|
return toSymbol(p) + toSymbol(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func partitionGetter(id string) string {
|
||||||
|
return fmt.Sprintf("%sPartition", toSymbol(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func partitionVarName(id string) string {
|
||||||
|
return fmt.Sprintf("%sPartition", strings.ToLower(toSymbol(id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func listPartitionNames(ps partitions) string {
|
||||||
|
names := []string{}
|
||||||
|
switch len(ps) {
|
||||||
|
case 1:
|
||||||
|
return ps[0].Name
|
||||||
|
case 2:
|
||||||
|
return fmt.Sprintf("%s and %s", ps[0].Name, ps[1].Name)
|
||||||
|
default:
|
||||||
|
for i, p := range ps {
|
||||||
|
if i == len(ps)-1 {
|
||||||
|
names = append(names, "and "+p.Name)
|
||||||
|
} else {
|
||||||
|
names = append(names, p.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(names, ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func boxedBoolIfSet(msg string, v boxedBool) string {
|
||||||
|
switch v {
|
||||||
|
case boxedTrue:
|
||||||
|
return fmt.Sprintf(msg, "boxedTrue")
|
||||||
|
case boxedFalse:
|
||||||
|
return fmt.Sprintf(msg, "boxedFalse")
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringIfSet(msg, v string) string {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(msg, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringSliceIfSet(msg string, vs []string) string {
|
||||||
|
if len(vs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
names := []string{}
|
||||||
|
for _, v := range vs {
|
||||||
|
names = append(names, `"`+v+`"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(msg, strings.Join(names, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
func endpointIsSet(v endpoint) bool {
|
||||||
|
return !reflect.DeepEqual(v, endpoint{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func serviceSet(ps partitions) map[string]struct{} {
|
||||||
|
set := map[string]struct{}{}
|
||||||
|
for _, p := range ps {
|
||||||
|
for id := range p.Services {
|
||||||
|
set[id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
var funcMap = template.FuncMap{
|
||||||
|
"ToSymbol": toSymbol,
|
||||||
|
"QuoteString": quoteString,
|
||||||
|
"RegionConst": regionConstName,
|
||||||
|
"PartitionGetter": partitionGetter,
|
||||||
|
"PartitionVarName": partitionVarName,
|
||||||
|
"ListPartitionNames": listPartitionNames,
|
||||||
|
"BoxedBoolIfSet": boxedBoolIfSet,
|
||||||
|
"StringIfSet": stringIfSet,
|
||||||
|
"StringSliceIfSet": stringSliceIfSet,
|
||||||
|
"EndpointIsSet": endpointIsSet,
|
||||||
|
"ServicesSet": serviceSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
const v3Tmpl = `
|
||||||
|
{{ define "defaults" -}}
|
||||||
|
// Code generated by aws/endpoints/v3model_codegen.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
{{ template "partition consts" $.Resolver }}
|
||||||
|
|
||||||
|
{{ range $_, $partition := $.Resolver }}
|
||||||
|
{{ template "partition region consts" $partition }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if not $.DisableGenerateServiceIDs -}}
|
||||||
|
{{ template "service consts" $.Resolver }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ template "endpoint resolvers" $.Resolver }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "partition consts" }}
|
||||||
|
// Partition identifiers
|
||||||
|
const (
|
||||||
|
{{ range $_, $p := . -}}
|
||||||
|
{{ ToSymbol $p.ID }}PartitionID = {{ QuoteString $p.ID }} // {{ $p.Name }} partition.
|
||||||
|
{{ end -}}
|
||||||
|
)
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "partition region consts" }}
|
||||||
|
// {{ .Name }} partition's regions.
|
||||||
|
const (
|
||||||
|
{{ range $id, $region := .Regions -}}
|
||||||
|
{{ ToSymbol $id }}RegionID = {{ QuoteString $id }} // {{ $region.Description }}.
|
||||||
|
{{ end -}}
|
||||||
|
)
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "service consts" }}
|
||||||
|
// Service identifiers
|
||||||
|
const (
|
||||||
|
{{ $serviceSet := ServicesSet . -}}
|
||||||
|
{{ range $id, $_ := $serviceSet -}}
|
||||||
|
{{ ToSymbol $id }}ServiceID = {{ QuoteString $id }} // {{ ToSymbol $id }}.
|
||||||
|
{{ end -}}
|
||||||
|
)
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "endpoint resolvers" }}
|
||||||
|
// DefaultResolver returns an Endpoint resolver that will be able
|
||||||
|
// to resolve endpoints for: {{ ListPartitionNames . }}.
|
||||||
|
//
|
||||||
|
// Use DefaultPartitions() to get the list of the default partitions.
|
||||||
|
func DefaultResolver() Resolver {
|
||||||
|
return defaultPartitions
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultPartitions returns a list of the partitions the SDK is bundled
|
||||||
|
// with. The available partitions are: {{ ListPartitionNames . }}.
|
||||||
|
//
|
||||||
|
// partitions := endpoints.DefaultPartitions
|
||||||
|
// for _, p := range partitions {
|
||||||
|
// // ... inspect partitions
|
||||||
|
// }
|
||||||
|
func DefaultPartitions() []Partition {
|
||||||
|
return defaultPartitions.Partitions()
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultPartitions = partitions{
|
||||||
|
{{ range $_, $partition := . -}}
|
||||||
|
{{ PartitionVarName $partition.ID }},
|
||||||
|
{{ end }}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ range $_, $partition := . -}}
|
||||||
|
{{ $name := PartitionGetter $partition.ID -}}
|
||||||
|
// {{ $name }} returns the Resolver for {{ $partition.Name }}.
|
||||||
|
func {{ $name }}() Partition {
|
||||||
|
return {{ PartitionVarName $partition.ID }}.Partition()
|
||||||
|
}
|
||||||
|
var {{ PartitionVarName $partition.ID }} = {{ template "gocode Partition" $partition }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "default partitions" }}
|
||||||
|
func DefaultPartitions() []Partition {
|
||||||
|
return []partition{
|
||||||
|
{{ range $_, $partition := . -}}
|
||||||
|
// {{ ToSymbol $partition.ID}}Partition(),
|
||||||
|
{{ end }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "gocode Partition" -}}
|
||||||
|
partition{
|
||||||
|
{{ StringIfSet "ID: %q,\n" .ID -}}
|
||||||
|
{{ StringIfSet "Name: %q,\n" .Name -}}
|
||||||
|
{{ StringIfSet "DNSSuffix: %q,\n" .DNSSuffix -}}
|
||||||
|
RegionRegex: {{ template "gocode RegionRegex" .RegionRegex }},
|
||||||
|
{{ if EndpointIsSet .Defaults -}}
|
||||||
|
Defaults: {{ template "gocode Endpoint" .Defaults }},
|
||||||
|
{{- end }}
|
||||||
|
Regions: {{ template "gocode Regions" .Regions }},
|
||||||
|
Services: {{ template "gocode Services" .Services }},
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "gocode RegionRegex" -}}
|
||||||
|
regionRegex{
|
||||||
|
Regexp: func() *regexp.Regexp{
|
||||||
|
reg, _ := regexp.Compile({{ QuoteString .Regexp.String }})
|
||||||
|
return reg
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "gocode Regions" -}}
|
||||||
|
regions{
|
||||||
|
{{ range $id, $region := . -}}
|
||||||
|
"{{ $id }}": {{ template "gocode Region" $region }},
|
||||||
|
{{ end -}}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "gocode Region" -}}
|
||||||
|
region{
|
||||||
|
{{ StringIfSet "Description: %q,\n" .Description -}}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "gocode Services" -}}
|
||||||
|
services{
|
||||||
|
{{ range $id, $service := . -}}
|
||||||
|
"{{ $id }}": {{ template "gocode Service" $service }},
|
||||||
|
{{ end }}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "gocode Service" -}}
|
||||||
|
service{
|
||||||
|
{{ StringIfSet "PartitionEndpoint: %q,\n" .PartitionEndpoint -}}
|
||||||
|
{{ BoxedBoolIfSet "IsRegionalized: %s,\n" .IsRegionalized -}}
|
||||||
|
{{ if EndpointIsSet .Defaults -}}
|
||||||
|
Defaults: {{ template "gocode Endpoint" .Defaults -}},
|
||||||
|
{{- end }}
|
||||||
|
{{ if .Endpoints -}}
|
||||||
|
Endpoints: {{ template "gocode Endpoints" .Endpoints }},
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "gocode Endpoints" -}}
|
||||||
|
endpoints{
|
||||||
|
{{ range $id, $endpoint := . -}}
|
||||||
|
"{{ $id }}": {{ template "gocode Endpoint" $endpoint }},
|
||||||
|
{{ end }}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "gocode Endpoint" -}}
|
||||||
|
endpoint{
|
||||||
|
{{ StringIfSet "Hostname: %q,\n" .Hostname -}}
|
||||||
|
{{ StringIfSet "SSLCommonName: %q,\n" .SSLCommonName -}}
|
||||||
|
{{ StringSliceIfSet "Protocols: []string{%s},\n" .Protocols -}}
|
||||||
|
{{ StringSliceIfSet "SignatureVersions: []string{%s},\n" .SignatureVersions -}}
|
||||||
|
{{ if or .CredentialScope.Region .CredentialScope.Service -}}
|
||||||
|
CredentialScope: credentialScope{
|
||||||
|
{{ StringIfSet "Region: %q,\n" .CredentialScope.Region -}}
|
||||||
|
{{ StringIfSet "Service: %q,\n" .CredentialScope.Service -}}
|
||||||
|
},
|
||||||
|
{{- end }}
|
||||||
|
{{ BoxedBoolIfSet "HasDualStack: %s,\n" .HasDualStack -}}
|
||||||
|
{{ StringIfSet "DualStackHostname: %q,\n" .DualStackHostname -}}
|
||||||
|
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
`
|
||||||
13
vendor/github.com/aws/aws-sdk-go/aws/errors.go
generated
vendored
Executable file
13
vendor/github.com/aws/aws-sdk-go/aws/errors.go
generated
vendored
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
import "github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrMissingRegion is an error that is returned if region configuration is
|
||||||
|
// not found.
|
||||||
|
ErrMissingRegion = awserr.New("MissingRegion", "could not find region configuration", nil)
|
||||||
|
|
||||||
|
// ErrMissingEndpoint is an error that is returned if an endpoint cannot be
|
||||||
|
// resolved for a service.
|
||||||
|
ErrMissingEndpoint = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil)
|
||||||
|
)
|
||||||
12
vendor/github.com/aws/aws-sdk-go/aws/jsonvalue.go
generated
vendored
Executable file
12
vendor/github.com/aws/aws-sdk-go/aws/jsonvalue.go
generated
vendored
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
// JSONValue is a representation of a grab bag type that will be marshaled
|
||||||
|
// into a json string. This type can be used just like any other map.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// values := aws.JSONValue{
|
||||||
|
// "Foo": "Bar",
|
||||||
|
// }
|
||||||
|
// values["Baz"] = "Qux"
|
||||||
|
type JSONValue map[string]interface{}
|
||||||
118
vendor/github.com/aws/aws-sdk-go/aws/logger.go
generated
vendored
Executable file
118
vendor/github.com/aws/aws-sdk-go/aws/logger.go
generated
vendored
Executable file
@@ -0,0 +1,118 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A LogLevelType defines the level logging should be performed at. Used to instruct
|
||||||
|
// the SDK which statements should be logged.
|
||||||
|
type LogLevelType uint
|
||||||
|
|
||||||
|
// LogLevel returns the pointer to a LogLevel. Should be used to workaround
|
||||||
|
// not being able to take the address of a non-composite literal.
|
||||||
|
func LogLevel(l LogLevelType) *LogLevelType {
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the LogLevel value or the default value LogOff if the LogLevel
|
||||||
|
// is nil. Safe to use on nil value LogLevelTypes.
|
||||||
|
func (l *LogLevelType) Value() LogLevelType {
|
||||||
|
if l != nil {
|
||||||
|
return *l
|
||||||
|
}
|
||||||
|
return LogOff
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches returns true if the v LogLevel is enabled by this LogLevel. Should be
|
||||||
|
// used with logging sub levels. Is safe to use on nil value LogLevelTypes. If
|
||||||
|
// LogLevel is nil, will default to LogOff comparison.
|
||||||
|
func (l *LogLevelType) Matches(v LogLevelType) bool {
|
||||||
|
c := l.Value()
|
||||||
|
return c&v == v
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtLeast returns true if this LogLevel is at least high enough to satisfies v.
|
||||||
|
// Is safe to use on nil value LogLevelTypes. If LogLevel is nil, will default
|
||||||
|
// to LogOff comparison.
|
||||||
|
func (l *LogLevelType) AtLeast(v LogLevelType) bool {
|
||||||
|
c := l.Value()
|
||||||
|
return c >= v
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LogOff states that no logging should be performed by the SDK. This is the
|
||||||
|
// default state of the SDK, and should be use to disable all logging.
|
||||||
|
LogOff LogLevelType = iota * 0x1000
|
||||||
|
|
||||||
|
// LogDebug state that debug output should be logged by the SDK. This should
|
||||||
|
// be used to inspect request made and responses received.
|
||||||
|
LogDebug
|
||||||
|
)
|
||||||
|
|
||||||
|
// Debug Logging Sub Levels
|
||||||
|
const (
|
||||||
|
// LogDebugWithSigning states that the SDK should log request signing and
|
||||||
|
// presigning events. This should be used to log the signing details of
|
||||||
|
// requests for debugging. Will also enable LogDebug.
|
||||||
|
LogDebugWithSigning LogLevelType = LogDebug | (1 << iota)
|
||||||
|
|
||||||
|
// LogDebugWithHTTPBody states the SDK should log HTTP request and response
|
||||||
|
// HTTP bodys in addition to the headers and path. This should be used to
|
||||||
|
// see the body content of requests and responses made while using the SDK
|
||||||
|
// Will also enable LogDebug.
|
||||||
|
LogDebugWithHTTPBody
|
||||||
|
|
||||||
|
// LogDebugWithRequestRetries states the SDK should log when service requests will
|
||||||
|
// be retried. This should be used to log when you want to log when service
|
||||||
|
// requests are being retried. Will also enable LogDebug.
|
||||||
|
LogDebugWithRequestRetries
|
||||||
|
|
||||||
|
// LogDebugWithRequestErrors states the SDK should log when service requests fail
|
||||||
|
// to build, send, validate, or unmarshal.
|
||||||
|
LogDebugWithRequestErrors
|
||||||
|
|
||||||
|
// LogDebugWithEventStreamBody states the SDK should log EventStream
|
||||||
|
// request and response bodys. This should be used to log the EventStream
|
||||||
|
// wire unmarshaled message content of requests and responses made while
|
||||||
|
// using the SDK Will also enable LogDebug.
|
||||||
|
LogDebugWithEventStreamBody
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Logger is a minimalistic interface for the SDK to log messages to. Should
|
||||||
|
// be used to provide custom logging writers for the SDK to use.
|
||||||
|
type Logger interface {
|
||||||
|
Log(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LoggerFunc is a convenience type to convert a function taking a variadic
|
||||||
|
// list of arguments and wrap it so the Logger interface can be used.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// s3.New(sess, &aws.Config{Logger: aws.LoggerFunc(func(args ...interface{}) {
|
||||||
|
// fmt.Fprintln(os.Stdout, args...)
|
||||||
|
// })})
|
||||||
|
type LoggerFunc func(...interface{})
|
||||||
|
|
||||||
|
// Log calls the wrapped function with the arguments provided
|
||||||
|
func (f LoggerFunc) Log(args ...interface{}) {
|
||||||
|
f(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultLogger returns a Logger which will write log messages to stdout, and
|
||||||
|
// use same formatting runes as the stdlib log.Logger
|
||||||
|
func NewDefaultLogger() Logger {
|
||||||
|
return &defaultLogger{
|
||||||
|
logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A defaultLogger provides a minimalistic logger satisfying the Logger interface.
|
||||||
|
type defaultLogger struct {
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log logs the parameters to the stdlib logger. See log.Println.
|
||||||
|
func (l defaultLogger) Log(args ...interface{}) {
|
||||||
|
l.logger.Println(args...)
|
||||||
|
}
|
||||||
19
vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error.go
generated
vendored
Executable file
19
vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error.go
generated
vendored
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
// +build !appengine,!plan9
|
||||||
|
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isErrConnectionReset(err error) bool {
|
||||||
|
if opErr, ok := err.(*net.OpError); ok {
|
||||||
|
if sysErr, ok := opErr.Err.(*os.SyscallError); ok {
|
||||||
|
return sysErr.Err == syscall.ECONNRESET
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
11
vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error_other.go
generated
vendored
Executable file
11
vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error_other.go
generated
vendored
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build appengine plan9
|
||||||
|
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isErrConnectionReset(err error) bool {
|
||||||
|
return strings.Contains(err.Error(), "connection reset")
|
||||||
|
}
|
||||||
277
vendor/github.com/aws/aws-sdk-go/aws/request/handlers.go
generated
vendored
Executable file
277
vendor/github.com/aws/aws-sdk-go/aws/request/handlers.go
generated
vendored
Executable file
@@ -0,0 +1,277 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Handlers provides a collection of request handlers for various
|
||||||
|
// stages of handling requests.
|
||||||
|
type Handlers struct {
|
||||||
|
Validate HandlerList
|
||||||
|
Build HandlerList
|
||||||
|
Sign HandlerList
|
||||||
|
Send HandlerList
|
||||||
|
ValidateResponse HandlerList
|
||||||
|
Unmarshal HandlerList
|
||||||
|
UnmarshalStream HandlerList
|
||||||
|
UnmarshalMeta HandlerList
|
||||||
|
UnmarshalError HandlerList
|
||||||
|
Retry HandlerList
|
||||||
|
AfterRetry HandlerList
|
||||||
|
CompleteAttempt HandlerList
|
||||||
|
Complete HandlerList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns of this handler's lists.
|
||||||
|
func (h *Handlers) Copy() Handlers {
|
||||||
|
return Handlers{
|
||||||
|
Validate: h.Validate.copy(),
|
||||||
|
Build: h.Build.copy(),
|
||||||
|
Sign: h.Sign.copy(),
|
||||||
|
Send: h.Send.copy(),
|
||||||
|
ValidateResponse: h.ValidateResponse.copy(),
|
||||||
|
Unmarshal: h.Unmarshal.copy(),
|
||||||
|
UnmarshalStream: h.UnmarshalStream.copy(),
|
||||||
|
UnmarshalError: h.UnmarshalError.copy(),
|
||||||
|
UnmarshalMeta: h.UnmarshalMeta.copy(),
|
||||||
|
Retry: h.Retry.copy(),
|
||||||
|
AfterRetry: h.AfterRetry.copy(),
|
||||||
|
CompleteAttempt: h.CompleteAttempt.copy(),
|
||||||
|
Complete: h.Complete.copy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes callback functions for all handlers
|
||||||
|
func (h *Handlers) Clear() {
|
||||||
|
h.Validate.Clear()
|
||||||
|
h.Build.Clear()
|
||||||
|
h.Send.Clear()
|
||||||
|
h.Sign.Clear()
|
||||||
|
h.Unmarshal.Clear()
|
||||||
|
h.UnmarshalStream.Clear()
|
||||||
|
h.UnmarshalMeta.Clear()
|
||||||
|
h.UnmarshalError.Clear()
|
||||||
|
h.ValidateResponse.Clear()
|
||||||
|
h.Retry.Clear()
|
||||||
|
h.AfterRetry.Clear()
|
||||||
|
h.CompleteAttempt.Clear()
|
||||||
|
h.Complete.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A HandlerListRunItem represents an entry in the HandlerList which
|
||||||
|
// is being run.
|
||||||
|
type HandlerListRunItem struct {
|
||||||
|
Index int
|
||||||
|
Handler NamedHandler
|
||||||
|
Request *Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// A HandlerList manages zero or more handlers in a list.
|
||||||
|
type HandlerList struct {
|
||||||
|
list []NamedHandler
|
||||||
|
|
||||||
|
// Called after each request handler in the list is called. If set
|
||||||
|
// and the func returns true the HandlerList will continue to iterate
|
||||||
|
// over the request handlers. If false is returned the HandlerList
|
||||||
|
// will stop iterating.
|
||||||
|
//
|
||||||
|
// Should be used if extra logic to be performed between each handler
|
||||||
|
// in the list. This can be used to terminate a list's iteration
|
||||||
|
// based on a condition such as error like, HandlerListStopOnError.
|
||||||
|
// Or for logging like HandlerListLogItem.
|
||||||
|
AfterEachFn func(item HandlerListRunItem) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// A NamedHandler is a struct that contains a name and function callback.
|
||||||
|
type NamedHandler struct {
|
||||||
|
Name string
|
||||||
|
Fn func(*Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy creates a copy of the handler list.
|
||||||
|
func (l *HandlerList) copy() HandlerList {
|
||||||
|
n := HandlerList{
|
||||||
|
AfterEachFn: l.AfterEachFn,
|
||||||
|
}
|
||||||
|
if len(l.list) == 0 {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
n.list = append(make([]NamedHandler, 0, len(l.list)), l.list...)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the handler list.
|
||||||
|
func (l *HandlerList) Clear() {
|
||||||
|
l.list = l.list[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of handlers in the list.
|
||||||
|
func (l *HandlerList) Len() int {
|
||||||
|
return len(l.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack pushes handler f to the back of the handler list.
|
||||||
|
func (l *HandlerList) PushBack(f func(*Request)) {
|
||||||
|
l.PushBackNamed(NamedHandler{"__anonymous", f})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBackNamed pushes named handler f to the back of the handler list.
|
||||||
|
func (l *HandlerList) PushBackNamed(n NamedHandler) {
|
||||||
|
if cap(l.list) == 0 {
|
||||||
|
l.list = make([]NamedHandler, 0, 5)
|
||||||
|
}
|
||||||
|
l.list = append(l.list, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFront pushes handler f to the front of the handler list.
|
||||||
|
func (l *HandlerList) PushFront(f func(*Request)) {
|
||||||
|
l.PushFrontNamed(NamedHandler{"__anonymous", f})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFrontNamed pushes named handler f to the front of the handler list.
|
||||||
|
func (l *HandlerList) PushFrontNamed(n NamedHandler) {
|
||||||
|
if cap(l.list) == len(l.list) {
|
||||||
|
// Allocating new list required
|
||||||
|
l.list = append([]NamedHandler{n}, l.list...)
|
||||||
|
} else {
|
||||||
|
// Enough room to prepend into list.
|
||||||
|
l.list = append(l.list, NamedHandler{})
|
||||||
|
copy(l.list[1:], l.list)
|
||||||
|
l.list[0] = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a NamedHandler n
|
||||||
|
func (l *HandlerList) Remove(n NamedHandler) {
|
||||||
|
l.RemoveByName(n.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveByName removes a NamedHandler by name.
|
||||||
|
func (l *HandlerList) RemoveByName(name string) {
|
||||||
|
for i := 0; i < len(l.list); i++ {
|
||||||
|
m := l.list[i]
|
||||||
|
if m.Name == name {
|
||||||
|
// Shift array preventing creating new arrays
|
||||||
|
copy(l.list[i:], l.list[i+1:])
|
||||||
|
l.list[len(l.list)-1] = NamedHandler{}
|
||||||
|
l.list = l.list[:len(l.list)-1]
|
||||||
|
|
||||||
|
// decrement list so next check to length is correct
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapNamed will swap out any existing handlers with the same name as the
|
||||||
|
// passed in NamedHandler returning true if handlers were swapped. False is
|
||||||
|
// returned otherwise.
|
||||||
|
func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) {
|
||||||
|
for i := 0; i < len(l.list); i++ {
|
||||||
|
if l.list[i].Name == n.Name {
|
||||||
|
l.list[i].Fn = n.Fn
|
||||||
|
swapped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return swapped
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap will swap out all handlers matching the name passed in. The matched
|
||||||
|
// handlers will be swapped in. True is returned if the handlers were swapped.
|
||||||
|
func (l *HandlerList) Swap(name string, replace NamedHandler) bool {
|
||||||
|
var swapped bool
|
||||||
|
|
||||||
|
for i := 0; i < len(l.list); i++ {
|
||||||
|
if l.list[i].Name == name {
|
||||||
|
l.list[i] = replace
|
||||||
|
swapped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return swapped
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBackNamed will replace the named handler if it exists in the handler list.
|
||||||
|
// If the handler does not exist the handler will be added to the end of the list.
|
||||||
|
func (l *HandlerList) SetBackNamed(n NamedHandler) {
|
||||||
|
if !l.SwapNamed(n) {
|
||||||
|
l.PushBackNamed(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFrontNamed will replace the named handler if it exists in the handler list.
|
||||||
|
// If the handler does not exist the handler will be added to the beginning of
|
||||||
|
// the list.
|
||||||
|
func (l *HandlerList) SetFrontNamed(n NamedHandler) {
|
||||||
|
if !l.SwapNamed(n) {
|
||||||
|
l.PushFrontNamed(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes all handlers in the list with a given request object.
|
||||||
|
func (l *HandlerList) Run(r *Request) {
|
||||||
|
for i, h := range l.list {
|
||||||
|
h.Fn(r)
|
||||||
|
item := HandlerListRunItem{
|
||||||
|
Index: i, Handler: h, Request: r,
|
||||||
|
}
|
||||||
|
if l.AfterEachFn != nil && !l.AfterEachFn(item) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerListLogItem logs the request handler and the state of the
|
||||||
|
// request's Error value. Always returns true to continue iterating
|
||||||
|
// request handlers in a HandlerList.
|
||||||
|
func HandlerListLogItem(item HandlerListRunItem) bool {
|
||||||
|
if item.Request.Config.Logger == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
item.Request.Config.Logger.Log("DEBUG: RequestHandler",
|
||||||
|
item.Index, item.Handler.Name, item.Request.Error)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerListStopOnError returns false to stop the HandlerList iterating
|
||||||
|
// over request handlers if Request.Error is not nil. True otherwise
|
||||||
|
// to continue iterating.
|
||||||
|
func HandlerListStopOnError(item HandlerListRunItem) bool {
|
||||||
|
return item.Request.Error == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAppendUserAgent will add a string to the user agent prefixed with a
|
||||||
|
// single white space.
|
||||||
|
func WithAppendUserAgent(s string) Option {
|
||||||
|
return func(r *Request) {
|
||||||
|
r.Handlers.Build.PushBack(func(r2 *Request) {
|
||||||
|
AddToUserAgent(r, s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request
|
||||||
|
// header. If the extra parameters are provided they will be added as metadata to the
|
||||||
|
// name/version pair resulting in the following format.
|
||||||
|
// "name/version (extra0; extra1; ...)"
|
||||||
|
// The user agent part will be concatenated with this current request's user agent string.
|
||||||
|
func MakeAddToUserAgentHandler(name, version string, extra ...string) func(*Request) {
|
||||||
|
ua := fmt.Sprintf("%s/%s", name, version)
|
||||||
|
if len(extra) > 0 {
|
||||||
|
ua += fmt.Sprintf(" (%s)", strings.Join(extra, "; "))
|
||||||
|
}
|
||||||
|
return func(r *Request) {
|
||||||
|
AddToUserAgent(r, ua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeAddToUserAgentFreeFormHandler adds the input to the User-Agent request header.
|
||||||
|
// The input string will be concatenated with the current request's user agent string.
|
||||||
|
func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) {
|
||||||
|
return func(r *Request) {
|
||||||
|
AddToUserAgent(r, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user