sync up to mainstream and add TestEventById test

This commit is contained in:
Victor Tran 2019-06-24 13:32:13 +07:00
parent 78db8a8343
commit 10a40e5006
85 changed files with 2803 additions and 1620 deletions

View file

@ -21,6 +21,8 @@ import (
"encoding/json"
"fmt"
"io"
"github.com/ethereum/go-ethereum/common"
)
// The ABI holds information about a contract's context and available
@ -168,11 +170,11 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
// EventById looks up an event by the first topic hash
// returns nil if none was found
func (abi *ABI) EventById(topic []byte) (*Event, error) {
func (abi *ABI) EventById(id common.Hash) (*Event, error) {
for _, event := range abi.Events {
if bytes.Equal(event.Id().Bytes(), topic) {
if bytes.Equal(event.Id().Bytes(), id.Bytes()) {
return &event, nil
}
}
return nil, fmt.Errorf("no event with id: %#x", topic)
return nil, fmt.Errorf("no event with id: %#x", id)
}

View file

@ -931,3 +931,41 @@ func TestABI_MethodById(t *testing.T) {
t.Errorf("Expected error, nil is short to decode data")
}
}
func TestABI_EventById(t *testing.T) {
const abiJSON = `
[
{ "constant": true, "inputs": [], "name": "name", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" },
{ "constant": false, "inputs": [ { "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "approve", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" },
{ "constant": true, "inputs": [], "name": "totalSupply", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" },
{ "constant": false, "inputs": [ { "name": "_from", "type": "address" }, { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" },
{ "constant": true, "inputs": [], "name": "decimals", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" },
{ "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" },
{ "constant": true, "inputs": [], "name": "symbol", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" },
{ "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" },
{ "constant": true, "inputs": [ { "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" } ], "name": "allowance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" },
{ "payable": true, "stateMutability": "payable", "type": "fallback" },
{ "anonymous": false, "inputs": [ { "indexed": true, "name": "owner", "type": "address" }, { "indexed": true, "name": "spender", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" },
{ "anonymous": false, "inputs": [ { "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }
]
`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
for name, e := range abi.Events {
a := fmt.Sprintf("%v", e)
e2, err := abi.EventById(e.Id())
if err != nil {
t.Fatalf("Failed to look up ABI method: %v", err)
}
b := fmt.Sprintf("%v", e2)
if a != b {
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, e.Id().Hex())
}
}
// Also test random hash
if _, err := abi.EventById(common.HexToHash("00xea9415385bae08fe9f6dc457b02577166790cde83bb18cc340aac6cb81b824de")); err == nil {
t.Errorf("Expected error, no event with id")
}
}

View file

@ -35,7 +35,7 @@ const (
)
var reportEvent = func() func(eventType eventType, eventID int32, msg string) {
advAPI32 := syscall.MustLoadDLL("AdvAPI32.dll")
advAPI32 := syscall.MustLoadDLL("advapi32.dll") // lower case to tie in with Go's sysdll registration
registerEventSource := advAPI32.MustFindProc("RegisterEventSourceW")
sourceName, _ := os.Executable()

View file

@ -9,6 +9,23 @@ type causer interface {
Cause() error
}
func errorWithPC(msg string, pc uintptr) string {
s := ""
if fn := runtime.FuncForPC(pc); fn != nil {
file, line := fn.FileLine(pc)
s = fmt.Sprintf("-> %v, %v:%v\n", fn.Name(), file, line)
}
s += msg + "\n\n"
return s
}
func getPC(callersToSkip int) uintptr {
// Get the PC of Initialize method's caller.
pc := [1]uintptr{}
_ = runtime.Callers(callersToSkip, pc[:])
return pc[0]
}
// ErrorNode can be an embedded field in a private error object. This field
// adds Program Counter support and a 'cause' (reference to a preceding error).
// When initializing a error type with this embedded field, initialize the
@ -22,12 +39,7 @@ type ErrorNode struct {
// When defining a new error type, have its Error method call this one passing
// it the string representation of the error.
func (e *ErrorNode) Error(msg string) string {
s := ""
if fn := runtime.FuncForPC(e.pc); fn != nil {
file, line := fn.FileLine(e.pc)
s = fmt.Sprintf("-> %v, %v:%v\n", fn.Name(), file, line)
}
s += msg + "\n\n"
s := errorWithPC(msg, e.pc)
if e.cause != nil {
s += e.cause.Error() + "\n"
}
@ -83,10 +95,8 @@ func (e ErrorNode) Timeout() bool {
// value of 3 is very common; but, depending on your code nesting, you may need
// a different value.
func (ErrorNode) Initialize(cause error, callersToSkip int) ErrorNode {
// Get the PC of Initialize method's caller.
pc := [1]uintptr{}
_ = runtime.Callers(callersToSkip, pc[:])
return ErrorNode{pc: pc[0], cause: cause}
pc := getPC(callersToSkip)
return ErrorNode{pc: pc, cause: cause}
}
// Cause walks all the preceding errors and return the originating error.
@ -101,13 +111,54 @@ func Cause(err error) error {
return err
}
// ErrorNodeNoCause can be an embedded field in a private error object. This field
// adds Program Counter support.
// When initializing a error type with this embedded field, initialize the
// ErrorNodeNoCause field by calling ErrorNodeNoCause{}.Initialize().
type ErrorNodeNoCause struct {
pc uintptr // Represents a Program Counter that you can get symbols for.
}
// Error returns a string with the PC's symbols or "" if the PC is invalid.
// When defining a new error type, have its Error method call this one passing
// it the string representation of the error.
func (e *ErrorNodeNoCause) Error(msg string) string {
return errorWithPC(msg, e.pc)
}
// Temporary returns true if the error occurred due to a temporary condition.
func (e ErrorNodeNoCause) Temporary() bool {
return false
}
// Timeout returns true if the error occurred due to time expiring.
func (e ErrorNodeNoCause) Timeout() bool {
return false
}
// Initialize is used to initialize an embedded ErrorNode field.
// It captures the caller's program counter.
// To initialize the field, use "ErrorNodeNoCause{}.Initialize(3)". A callersToSkip
// value of 3 is very common; but, depending on your code nesting, you may need
// a different value.
func (ErrorNodeNoCause) Initialize(callersToSkip int) ErrorNodeNoCause {
pc := getPC(callersToSkip)
return ErrorNodeNoCause{pc: pc}
}
// NewError creates a simple string error (like Error.New). But, this
// error also captures the caller's Program Counter and the preceding error.
// error also captures the caller's Program Counter and the preceding error (if provided).
func NewError(cause error, msg string) error {
if cause != nil {
return &pcError{
ErrorNode: ErrorNode{}.Initialize(cause, 3),
msg: msg,
}
}
return &pcErrorNoCause{
ErrorNodeNoCause: ErrorNodeNoCause{}.Initialize(3),
msg: msg,
}
}
// pcError is a simple string error (like error.New) with an ErrorNode (PC & cause).
@ -119,3 +170,12 @@ type pcError struct {
// Error satisfies the error interface. It shows the error with Program Counter
// symbols and calls Error on the preceding error so you can see the full error chain.
func (e *pcError) Error() string { return e.ErrorNode.Error(e.msg) }
// pcErrorNoCause is a simple string error (like error.New) with an ErrorNode (PC).
type pcErrorNoCause struct {
ErrorNodeNoCause
msg string
}
// Error satisfies the error interface. It shows the error with Program Counter symbols.
func (e *pcErrorNoCause) Error() string { return e.ErrorNodeNoCause.Error(e.msg) }

View file

@ -77,7 +77,7 @@ func (s *SWbemServices) process(initError chan error) {
//fmt.Println("process: starting background thread initialization")
//All OLE/WMI calls must happen on the same initialized thead, so lock this goroutine
runtime.LockOSThread()
defer runtime.LockOSThread()
defer runtime.UnlockOSThread()
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
if err != nil {

View file

@ -285,6 +285,10 @@ func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismat
}
defer prop.Clear()
if prop.VT == 0x1 { //VT_NULL
continue
}
switch val := prop.Value().(type) {
case int8, int16, int32, int64, int:
v := reflect.ValueOf(val).Int()
@ -383,7 +387,7 @@ func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismat
}
f.Set(fArr)
}
case reflect.Uint8:
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
safeArray := prop.ToArray()
if safeArray != nil {
arr := safeArray.ToValueArray()
@ -394,6 +398,17 @@ func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismat
}
f.Set(fArr)
}
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
safeArray := prop.ToArray()
if safeArray != nil {
arr := safeArray.ToValueArray()
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
for i, v := range arr {
s := fArr.Index(i)
s.SetInt(reflect.ValueOf(v).Int())
}
f.Set(fArr)
}
default:
return &ErrFieldMismatch{
StructType: of.Type(),

View file

@ -1,4 +1,4 @@
// Copyright (C) 2016 Arista Networks, Inc.
// Copyright (c) 2016 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

View file

@ -1,4 +1,4 @@
// Copyright (C) 2016 Arista Networks, Inc.
// Copyright (c) 2016 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

View file

@ -32,8 +32,9 @@ func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, erro
x3 := new(big.Int).Mul(x, x)
x3.Mul(x3, x)
x3.Add(x3, curve.Params().B)
x3.Mod(x3, curve.Params().P)
// now calculate sqrt mod p of x2 + B
// Now calculate sqrt mod p of x^3 + B
// This code used to do a full sqrt based on tonelli/shanks,
// but this was replaced by the algorithms referenced in
// https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
@ -42,9 +43,19 @@ func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, erro
if ybit != isOdd(y) {
y.Sub(curve.Params().P, y)
}
// Check that y is a square root of x^3 + B.
y2 := new(big.Int).Mul(y, y)
y2.Mod(y2, curve.Params().P)
if y2.Cmp(x3) != 0 {
return nil, fmt.Errorf("invalid square root")
}
// Verify that y-coord has expected parity.
if ybit != isOdd(y) {
return nil, fmt.Errorf("ybit doesn't match oddness")
}
return y, nil
}

View file

@ -85,6 +85,11 @@ func (sig *Signature) IsEqual(otherSig *Signature) bool {
sig.S.Cmp(otherSig.S) == 0
}
// MinSigLen is the minimum length of a DER encoded signature and is when both R
// and S are 1 byte each.
// 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
const MinSigLen = 8
func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error) {
// Originally this code used encoding/asn1 in order to parse the
// signature, but a number of problems were found with this approach.
@ -98,9 +103,7 @@ func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error)
signature := &Signature{}
// minimal message is when both numbers are 1 bytes. adding up to:
// 0x30 + len + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
if len(sigStr) < 8 {
if len(sigStr) < MinSigLen {
return nil, errors.New("malformed signature: too short")
}
// 0x30
@ -112,7 +115,10 @@ func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error)
// length of remaining message
siglen := sigStr[index]
index++
if int(siglen+2) > len(sigStr) {
// siglen should be less than the entire message and greater than
// the minimal message size.
if int(siglen+2) > len(sigStr) || int(siglen+2) < MinSigLen {
return nil, errors.New("malformed signature: bad length")
}
// trim the slice we're working on so we only look at what matters.
@ -269,7 +275,7 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
return ret
}
// recoverKeyFromSignature recoves a public key from the signature "sig" on the
// recoverKeyFromSignature recovers a public key from the signature "sig" on the
// given message hash "msg". Based on the algorithm found in section 5.1.5 of
// SEC 1 Ver 2.0, page 47-48 (53 and 54 in the pdf). This performs the details
// in the inner loop in Step 1. The counter provided is actually the j parameter
@ -421,9 +427,7 @@ func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) {
k := nonceRFC6979(privkey.D, hash)
inv := new(big.Int).ModInverse(k, N)
r, _ := privkey.Curve.ScalarBaseMult(k.Bytes())
if r.Cmp(N) == 1 {
r.Sub(r, N)
}
r.Mod(r, N)
if r.Sign() == 0 {
return nil, errors.New("calculated R is zero")

66
vendor/github.com/cespare/cp/cp.go generated vendored
View file

@ -6,14 +6,28 @@ import (
"io"
"os"
"path/filepath"
"strings"
)
var errCopyFileWithDir = errors.New("dir argument to CopyFile")
// CopyFile copies the file with path src to dst. The new file must not exist.
const (
flagDefault = os.O_WRONLY | os.O_CREATE | os.O_EXCL
flagOverwrite = os.O_WRONLY | os.O_CREATE | os.O_TRUNC
)
// CopyFile copies the file at src to dst. The new file must not exist.
// It is created with the same permissions as src.
func CopyFile(dst, src string) error {
return copyFile(dst, src, flagDefault)
}
// CopyFileOverwrite is like CopyFile except that it overwrites dst
// if it already exists.
func CopyFileOverwrite(dst, src string) error {
return copyFile(dst, src, flagOverwrite)
}
func copyFile(dst, src string, flag int) error {
rf, err := os.Open(src)
if err != nil {
return err
@ -27,32 +41,62 @@ func CopyFile(dst, src string) error {
return errCopyFileWithDir
}
wf, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, rstat.Mode())
wf, err := os.OpenFile(dst, flag, rstat.Mode())
if err != nil {
return err
}
defer wf.Close()
if flag&os.O_EXCL == 0 {
// We may be overwriting an existing file.
// Ensure the file mode matches.
stat, err := wf.Stat()
if err != nil {
return err
}
if stat.Mode() != rstat.Mode() {
if err := wf.Chmod(rstat.Mode()); err != nil {
return err
}
}
}
if _, err := io.Copy(wf, rf); err != nil {
wf.Close()
return err
}
return wf.Close()
}
// CopyAll copies the file or (recursively) the directory at src to dst.
// Permissions are preserved. dst must not already exist.
// Permissions are preserved. The target directory must not already exist.
func CopyAll(dst, src string) error {
return filepath.Walk(src, makeWalkFn(dst, src))
return filepath.Walk(src, makeWalkFn(dst, src, flagDefault))
}
func makeWalkFn(dst, src string) filepath.WalkFunc {
// CopyAllOverwrite is like CopyAll except that it recursively overwrites
// any existing directories or files.
func CopyAllOverwrite(dst, src string) error {
return filepath.Walk(src, makeWalkFn(dst, src, flagOverwrite))
}
func makeWalkFn(dst, src string, flag int) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
dstPath := filepath.Join(dst, strings.TrimPrefix(path, src))
if info.IsDir() {
return os.Mkdir(dstPath, info.Mode())
rel, err := filepath.Rel(src, path)
if err != nil {
// Given the Walk contract, Rel must succeed.
panic("shouldn't happen")
}
return CopyFile(dstPath, path)
dstPath := filepath.Join(dst, rel)
if info.IsDir() {
err := os.Mkdir(dstPath, info.Mode())
// In overwrite mode, allow the directory to already exist
// (but make sure the permissions match).
if os.IsExist(err) && flag&os.O_EXCL == 0 {
return os.Chmod(dstPath, info.Mode())
}
return err
}
return copyFile(dstPath, path, flag)
}
}

View file

@ -2,7 +2,7 @@ ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

View file

@ -16,7 +16,9 @@
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
@ -34,80 +36,49 @@ const (
ptrSize = unsafe.Sizeof((*byte)(nil))
)
var (
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
// internal reflect.Value fields. These values are valid before golang
// commit ecccf07e7f9d which changed the format. The are also valid
// after commit 82f48826c6c7 which changed the format again to mirror
// the original format. Code in the init function updates these offsets
// as necessary.
offsetPtr = uintptr(ptrSize)
offsetScalar = uintptr(0)
offsetFlag = uintptr(ptrSize * 2)
type flag uintptr
// flagKindWidth and flagKindShift indicate various bits that the
// reflect package uses internally to track kind information.
//
// flagRO indicates whether or not the value field of a reflect.Value is
// read-only.
//
// flagIndir indicates whether the value field of a reflect.Value is
// the actual data or a pointer to the data.
//
// These values are valid before golang commit 90a7c3c86944 which
// changed their positions. Code in the init function updates these
// flags as necessary.
flagKindWidth = uintptr(5)
flagKindShift = uintptr(flagKindWidth - 1)
flagRO = uintptr(1 << 0)
flagIndir = uintptr(1 << 1)
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
func init() {
// Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Versions
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
// scalar for this purpose which unfortunately came before the flag
// field, so the offset of the flag field is different for those
// versions.
//
// This code constructs a new reflect.Value from a known small integer
// and checks if the size of the reflect.Value struct indicates it has
// the scalar field. When it does, the offsets are updated accordingly.
vv := reflect.ValueOf(0xf00)
if unsafe.Sizeof(vv) == (ptrSize * 4) {
offsetScalar = ptrSize * 2
offsetFlag = ptrSize * 3
}
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Commit 90a7c3c86944 changed the flag positions such that the low
// order bits are the kind. This code extracts the kind from the flags
// field and ensures it's the correct type. When it's not, the flag
// order has been changed to the newer format, so the flags are updated
// accordingly.
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
upfv := *(*uintptr)(upf)
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
flagKindShift = 0
flagRO = 1 << 5
flagIndir = 1 << 6
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
// Commit adf9b30e5594 modified the flags to separate the
// flagRO flag into two bits which specifies whether or not the
// field is embedded. This causes flagIndir to move over a bit
// and means that flagRO is the combination of either of the
// original flagRO bit and the new bit.
//
// This code detects the change by extracting what used to be
// the indirect bit to ensure it's set. When it's not, the flag
// order has been changed to the newer format, so the flags are
// updated accordingly.
if upfv&flagIndir == 0 {
flagRO = 3 << 5
flagIndir = 1 << 7
}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
@ -119,34 +90,56 @@ func init() {
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
indirects := 1
vt := v.Type()
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
if rvf&flagIndir != 0 {
vt = reflect.PtrTo(v.Type())
indirects++
} else if offsetScalar != 0 {
// The value is in the scalar field when it's not one of the
// reference types.
switch vt.Kind() {
case reflect.Uintptr:
case reflect.Chan:
case reflect.Func:
case reflect.Map:
case reflect.Ptr:
case reflect.UnsafePointer:
default:
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
offsetScalar)
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
}
pv := reflect.NewAt(vt, upv)
rv = pv
for i := 0; i < indirects; i++ {
rv = rv.Elem()
}
return rv
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
panic("reflect.Value read-only flag has changed semantics")
}

View file

@ -16,7 +16,7 @@
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe
// +build js appengine safe disableunsafe !go1.4
package spew

View file

@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.

View file

@ -35,16 +35,16 @@ var (
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
)
// dumpState contains information about the state of a dump operation.
@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound == true:
case nilFound:
d.w.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
d.w.Write(circularBytes)
default:

View file

@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
// Display dereferenced value.
switch {
case nilFound == true:
case nilFound:
f.fs.Write(nilAngleBytes)
case cycleFound == true:
case cycleFound:
f.fs.Write(circularShortBytes)
default:

View file

@ -226,8 +226,14 @@ func (set *threadSafeSet) String() string {
func (set *threadSafeSet) PowerSet() Set {
set.RLock()
ret := set.s.PowerSet()
unsafePowerSet := set.s.PowerSet().(*threadUnsafeSet)
set.RUnlock()
ret := &threadSafeSet{s: newThreadUnsafeSet()}
for subset := range unsafePowerSet.Iter() {
unsafeSubset := subset.(*threadUnsafeSet)
ret.Add(&threadSafeSet{s: *unsafeSubset})
}
return ret
}

View file

@ -76,6 +76,9 @@ func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
func (set *threadUnsafeSet) IsSubset(other Set) bool {
_ = other.(*threadUnsafeSet)
if set.Cardinality() > other.Cardinality() {
return false
}
for elem := range *set {
if !other.Contains(elem) {
return false

View file

@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
Copyright 2013-2017 Docker, Inc.
Copyright 2013-2018 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -54,6 +54,10 @@ func Map(f *os.File, prot, flags int) (MMap, error) {
// If length < 0, the entire file will be mapped.
// If ANON is set in flags, f is ignored.
func MapRegion(f *os.File, length int, prot, flags int, offset int64) (MMap, error) {
if offset%int64(os.Getpagesize()) != 0 {
return nil, errors.New("offset parameter must be a multiple of the system's page size")
}
var fd uintptr
if flags&ANON == 0 {
fd = uintptr(f.Fd())
@ -77,25 +81,27 @@ func (m *MMap) header() *reflect.SliceHeader {
return (*reflect.SliceHeader)(unsafe.Pointer(m))
}
func (m *MMap) addrLen() (uintptr, uintptr) {
header := m.header()
return header.Data, uintptr(header.Len)
}
// Lock keeps the mapped region in physical memory, ensuring that it will not be
// swapped out.
func (m MMap) Lock() error {
dh := m.header()
return lock(dh.Data, uintptr(dh.Len))
return m.lock()
}
// Unlock reverses the effect of Lock, allowing the mapped region to potentially
// be swapped out.
// If m is already unlocked, aan error will result.
func (m MMap) Unlock() error {
dh := m.header()
return unlock(dh.Data, uintptr(dh.Len))
return m.unlock()
}
// Flush synchronizes the mapping's contents to the file's contents on disk.
func (m MMap) Flush() error {
dh := m.header()
return flush(dh.Data, uintptr(dh.Len))
return m.flush()
}
// Unmap deletes the memory mapped region, flushes any remaining changes, and sets
@ -105,8 +111,7 @@ func (m MMap) Flush() error {
// Unmap should only be called on the slice value that was originally returned from
// a call to Map. Calling Unmap on a derived slice may cause errors.
func (m *MMap) Unmap() error {
dh := m.header()
err := unmap(dh.Data, uintptr(dh.Len))
err := m.unmap()
*m = nil
return err
}

View file

@ -7,61 +7,45 @@
package mmap
import (
"syscall"
"golang.org/x/sys/unix"
)
func mmap(len int, inprot, inflags, fd uintptr, off int64) ([]byte, error) {
flags := syscall.MAP_SHARED
prot := syscall.PROT_READ
flags := unix.MAP_SHARED
prot := unix.PROT_READ
switch {
case inprot&COPY != 0:
prot |= syscall.PROT_WRITE
flags = syscall.MAP_PRIVATE
prot |= unix.PROT_WRITE
flags = unix.MAP_PRIVATE
case inprot&RDWR != 0:
prot |= syscall.PROT_WRITE
prot |= unix.PROT_WRITE
}
if inprot&EXEC != 0 {
prot |= syscall.PROT_EXEC
prot |= unix.PROT_EXEC
}
if inflags&ANON != 0 {
flags |= syscall.MAP_ANON
flags |= unix.MAP_ANON
}
b, err := syscall.Mmap(int(fd), off, len, prot, flags)
b, err := unix.Mmap(int(fd), off, len, prot, flags)
if err != nil {
return nil, err
}
return b, nil
}
func flush(addr, len uintptr) error {
_, _, errno := syscall.Syscall(_SYS_MSYNC, addr, len, _MS_SYNC)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
func (m MMap) flush() error {
return unix.Msync([]byte(m), unix.MS_SYNC)
}
func lock(addr, len uintptr) error {
_, _, errno := syscall.Syscall(syscall.SYS_MLOCK, addr, len, 0)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
func (m MMap) lock() error {
return unix.Mlock([]byte(m))
}
func unlock(addr, len uintptr) error {
_, _, errno := syscall.Syscall(syscall.SYS_MUNLOCK, addr, len, 0)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
func (m MMap) unlock() error {
return unix.Munlock([]byte(m))
}
func unmap(addr, len uintptr) error {
_, _, errno := syscall.Syscall(syscall.SYS_MUNMAP, addr, len, 0)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
func (m MMap) unmap() error {
return unix.Munmap([]byte(m))
}

View file

@ -8,7 +8,8 @@ import (
"errors"
"os"
"sync"
"syscall"
"golang.org/x/sys/windows"
)
// mmap on Windows is a two-step process.
@ -19,23 +20,33 @@ import (
// not a struct, so it's convenient to manipulate.
// We keep this map so that we can get back the original handle from the memory address.
type addrinfo struct {
file windows.Handle
mapview windows.Handle
writable bool
}
var handleLock sync.Mutex
var handleMap = map[uintptr]syscall.Handle{}
var handleMap = map[uintptr]*addrinfo{}
func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
flProtect := uint32(syscall.PAGE_READONLY)
dwDesiredAccess := uint32(syscall.FILE_MAP_READ)
flProtect := uint32(windows.PAGE_READONLY)
dwDesiredAccess := uint32(windows.FILE_MAP_READ)
writable := false
switch {
case prot&COPY != 0:
flProtect = syscall.PAGE_WRITECOPY
dwDesiredAccess = syscall.FILE_MAP_COPY
flProtect = windows.PAGE_WRITECOPY
dwDesiredAccess = windows.FILE_MAP_COPY
writable = true
case prot&RDWR != 0:
flProtect = syscall.PAGE_READWRITE
dwDesiredAccess = syscall.FILE_MAP_WRITE
flProtect = windows.PAGE_READWRITE
dwDesiredAccess = windows.FILE_MAP_WRITE
writable = true
}
if prot&EXEC != 0 {
flProtect <<= 4
dwDesiredAccess |= syscall.FILE_MAP_EXECUTE
dwDesiredAccess |= windows.FILE_MAP_EXECUTE
}
// The maximum size is the area of the file, starting from 0,
@ -45,7 +56,7 @@ func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
maxSizeHigh := uint32((off + int64(len)) >> 32)
maxSizeLow := uint32((off + int64(len)) & 0xFFFFFFFF)
// TODO: Do we need to set some security attributes? It might help portability.
h, errno := syscall.CreateFileMapping(syscall.Handle(hfile), nil, flProtect, maxSizeHigh, maxSizeLow, nil)
h, errno := windows.CreateFileMapping(windows.Handle(hfile), nil, flProtect, maxSizeHigh, maxSizeLow, nil)
if h == 0 {
return nil, os.NewSyscallError("CreateFileMapping", errno)
}
@ -54,12 +65,16 @@ func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
// is the length the user requested.
fileOffsetHigh := uint32(off >> 32)
fileOffsetLow := uint32(off & 0xFFFFFFFF)
addr, errno := syscall.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len))
addr, errno := windows.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len))
if addr == 0 {
return nil, os.NewSyscallError("MapViewOfFile", errno)
}
handleLock.Lock()
handleMap[addr] = h
handleMap[addr] = &addrinfo{
file: windows.Handle(hfile),
mapview: h,
writable: writable,
}
handleLock.Unlock()
m := MMap{}
@ -71,8 +86,9 @@ func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
return m, nil
}
func flush(addr, len uintptr) error {
errno := syscall.FlushViewOfFile(addr, len)
func (m MMap) flush() error {
addr, len := m.addrLen()
errno := windows.FlushViewOfFile(addr, len)
if errno != nil {
return os.NewSyscallError("FlushViewOfFile", errno)
}
@ -85,22 +101,34 @@ func flush(addr, len uintptr) error {
return errors.New("unknown base address")
}
errno = syscall.FlushFileBuffers(handle)
return os.NewSyscallError("FlushFileBuffers", errno)
if handle.writable {
if err := windows.FlushFileBuffers(handle.file); err != nil {
return os.NewSyscallError("FlushFileBuffers", err)
}
}
return nil
}
func lock(addr, len uintptr) error {
errno := syscall.VirtualLock(addr, len)
func (m MMap) lock() error {
addr, len := m.addrLen()
errno := windows.VirtualLock(addr, len)
return os.NewSyscallError("VirtualLock", errno)
}
func unlock(addr, len uintptr) error {
errno := syscall.VirtualUnlock(addr, len)
func (m MMap) unlock() error {
addr, len := m.addrLen()
errno := windows.VirtualUnlock(addr, len)
return os.NewSyscallError("VirtualUnlock", errno)
}
func unmap(addr, len uintptr) error {
flush(addr, len)
func (m MMap) unmap() error {
err := m.flush()
if err != nil {
return err
}
addr := m.header().Data
// Lock the UnmapViewOfFile along with the handleMap deletion.
// As soon as we unmap the view, the OS is free to give the
// same addr to another new map. We don't want another goroutine
@ -108,7 +136,7 @@ func unmap(addr, len uintptr) error {
// we're trying to remove our old addr/handle pair.
handleLock.Lock()
defer handleLock.Unlock()
err := syscall.UnmapViewOfFile(addr)
err = windows.UnmapViewOfFile(addr)
if err != nil {
return err
}
@ -120,6 +148,6 @@ func unmap(addr, len uintptr) error {
}
delete(handleMap, addr)
e := syscall.CloseHandle(syscall.Handle(handle))
e := windows.CloseHandle(windows.Handle(handle.mapview))
return os.NewSyscallError("CloseHandle", e)
}

View file

@ -1,8 +0,0 @@
// Copyright 2011 Evan Shaw. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mmap
const _SYS_MSYNC = 277
const _MS_SYNC = 0x04

View file

@ -1,14 +0,0 @@
// Copyright 2011 Evan Shaw. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux openbsd solaris
package mmap
import (
"syscall"
)
const _SYS_MSYNC = syscall.SYS_MSYNC
const _MS_SYNC = syscall.MS_SYNC

View file

@ -8,12 +8,34 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- Added missing runtime import for FreeBSD. #104
### Changed
### Deprecated
## [0.10.3]
### Fixed
- ProcState.Get() doesn't fail under Windows when it cannot obtain process ownership information. #121
## [0.10.2]
### Fixed
- Fix memory leak when getting process arguments. #119
## [0.10.1]
### Fixed
- Replaced the WMI queries with win32 apis due to high CPU usage. #116
## [0.10.0]
### Added
- List filesystems on Windows that have an access path but not an assigned letter. #112
### Fixed
- Added missing runtime import for FreeBSD. #104
- Handle nil command line in Windows processes. #110
## [0.9.0]
### Added

View file

@ -111,3 +111,48 @@ func parseCpuStat(self *Cpu, line string) error {
self.Idle, _ = strtoull(fields[4])
return nil
}
func (self *Mem) Get() error {
val := C.uint32_t(0)
sc := C.size_t(4)
name := C.CString("vm.stats.vm.v_page_count")
_, err := C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
C.free(unsafe.Pointer(name))
if err != nil {
return err
}
pagecount := uint64(val)
name = C.CString("vm.stats.vm.v_page_size")
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
C.free(unsafe.Pointer(name))
if err != nil {
return err
}
pagesize := uint64(val)
name = C.CString("vm.stats.vm.v_free_count")
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
C.free(unsafe.Pointer(name))
if err != nil {
return err
}
self.Free = uint64(val) * pagesize
name = C.CString("vm.stats.vm.v_inactive_count")
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
C.free(unsafe.Pointer(name))
if err != nil {
return err
}
kern := uint64(val)
self.Total = uint64(pagecount * pagesize)
self.Used = self.Total - self.Free
self.ActualFree = self.Free + (kern * pagesize)
self.ActualUsed = self.Used - (kern * pagesize)
return nil
}

View file

@ -106,3 +106,28 @@ func parseCpuStat(self *Cpu, line string) error {
return nil
}
func (self *Mem) Get() error {
table, err := parseMeminfo()
if err != nil {
return err
}
self.Total, _ = table["MemTotal"]
self.Free, _ = table["MemFree"]
buffers, _ := table["Buffers"]
cached, _ := table["Cached"]
if available, ok := table["MemAvailable"]; ok {
// MemAvailable is in /proc/meminfo (kernel 3.14+)
self.ActualFree = available
} else {
self.ActualFree = self.Free + buffers + cached
}
self.Used = self.Total - self.Free
self.ActualUsed = self.Total - self.ActualFree
return nil
}

View file

@ -51,31 +51,6 @@ func (self *LoadAverage) Get() error {
return nil
}
func (self *Mem) Get() error {
table, err := parseMeminfo()
if err != nil {
return err
}
self.Total, _ = table["MemTotal"]
self.Free, _ = table["MemFree"]
buffers, _ := table["Buffers"]
cached, _ := table["Cached"]
if available, ok := table["MemAvailable"]; ok {
// MemAvailable is in /proc/meminfo (kernel 3.14+)
self.ActualFree = available
} else {
self.ActualFree = self.Free + buffers + cached
}
self.Used = self.Total - self.Free
self.ActualUsed = self.Total - self.ActualFree
return nil
}
func (self *Swap) Get() error {
table, err := parseMeminfo()

View file

@ -12,26 +12,10 @@ import (
"syscall"
"time"
"github.com/StackExchange/wmi"
"github.com/elastic/gosigar/sys/windows"
"github.com/pkg/errors"
)
// Win32_Process represents a process on the Windows operating system. If
// additional fields are added here (that match the Windows struct) they will
// automatically be populated when calling getWin32Process.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa394372(v=vs.85).aspx
type Win32_Process struct {
CommandLine string
}
// Win32_OperatingSystem WMI class represents a Windows-based operating system
// installed on a computer.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa394239(v=vs.85).aspx
type Win32_OperatingSystem struct {
LastBootUpTime time.Time
}
var (
// version is Windows version of the host OS.
version = windows.GetWindowsVersion()
@ -83,11 +67,12 @@ func (self *Uptime) Get() error {
bootTimeLock.Lock()
defer bootTimeLock.Unlock()
if bootTime == nil {
os, err := getWin32OperatingSystem()
uptime, err := windows.GetTickCount64()
if err != nil {
return errors.Wrap(err, "failed to get boot time using WMI")
return errors.Wrap(err, "failed to get boot time using win32 api")
}
bootTime = &os.LastBootUpTime
var boot = time.Unix(int64(uptime), 0)
bootTime = &boot
}
self.Length = time.Since(*bootTime).Seconds()
@ -155,9 +140,9 @@ func (self *CpuList) Get() error {
}
func (self *FileSystemList) Get() error {
drives, err := windows.GetLogicalDriveStrings()
drives, err := windows.GetAccessPaths()
if err != nil {
return errors.Wrap(err, "GetLogicalDriveStrings failed")
return errors.Wrap(err, "GetAccessPaths failed")
}
for _, drive := range drives {
@ -209,10 +194,11 @@ func (self *ProcState) Get(pid int) error {
errs = append(errs, errors.Wrap(err, "getParentPid failed"))
}
self.Username, err = getProcCredName(pid)
if err != nil {
errs = append(errs, errors.Wrap(err, "getProcCredName failed"))
}
// getProcCredName will often fail when run as a non-admin user. This is
// caused by strict ACL of the process token belonging to other users.
// Instead of failing completely, ignore this error and still return most
// data with an empty Username.
self.Username, _ = getProcCredName(pid)
if len(errs) > 0 {
errStrs := make([]string, 0, len(errs))
@ -251,7 +237,7 @@ func getProcStatus(pid int) (RunState, error) {
var exitCode uint32
err = syscall.GetExitCodeProcess(handle, &exitCode)
if err != nil {
return RunStateUnknown, errors.Wrapf(err, "GetExitCodeProcess failed for pid=%v")
return RunStateUnknown, errors.Wrapf(err, "GetExitCodeProcess failed for pid=%v", pid)
}
if exitCode == 259 { //still active
@ -289,6 +275,8 @@ func getProcCredName(pid int) (string, error) {
if err != nil {
return "", errors.Wrapf(err, "OpenProcessToken failed for pid=%v", pid)
}
// Close token to prevent handle leaks.
defer token.Close()
// Find the token user.
tokenUser, err := token.GetTokenUser()
@ -296,12 +284,6 @@ func getProcCredName(pid int) (string, error) {
return "", errors.Wrapf(err, "GetTokenInformation failed for pid=%v", pid)
}
// Close token to prevent handle leaks.
err = token.Close()
if err != nil {
return "", errors.Wrapf(err, "failed while closing process token handle for pid=%v", pid)
}
// Look up domain account by SID.
account, domain, _, err := tokenUser.User.Sid.LookupAccount("")
if err != nil {
@ -371,13 +353,28 @@ func (self *ProcArgs) Get(pid int) error {
if !version.IsWindowsVistaOrGreater() {
return ErrNotImplemented{runtime.GOOS}
}
process, err := getWin32Process(int32(pid))
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess|windows.PROCESS_VM_READ, false, uint32(pid))
if err != nil {
return errors.Wrapf(err, "ProcArgs failed for pid=%v", pid)
return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
}
defer syscall.CloseHandle(handle)
pbi, err := windows.NtQueryProcessBasicInformation(handle)
if err != nil {
return errors.Wrapf(err, "NtQueryProcessBasicInformation failed for pid=%v", pid)
}
if err != nil {
return nil
}
userProcParams, err := windows.GetUserProcessParams(handle, pbi)
if err != nil {
return nil
}
if argsW, err := windows.ReadProcessUnicodeString(handle, &userProcParams.CommandLine); err == nil {
self.List, err = windows.ByteSliceToStringSlice(argsW)
if err != nil {
return err
}
}
self.List = []string{process.CommandLine}
return nil
}
@ -394,35 +391,6 @@ func (self *FileSystemUsage) Get(path string) error {
return nil
}
// getWin32Process gets information about the process with the given process ID.
// It uses a WMI query to get the information from the local system.
func getWin32Process(pid int32) (Win32_Process, error) {
var dst []Win32_Process
query := fmt.Sprintf("WHERE ProcessId = %d", pid)
q := wmi.CreateQuery(&dst, query)
err := wmi.Query(q, &dst)
if err != nil {
return Win32_Process{}, fmt.Errorf("could not get Win32_Process %s: %v", query, err)
}
if len(dst) < 1 {
return Win32_Process{}, fmt.Errorf("could not get Win32_Process %s: Process not found", query)
}
return dst[0], nil
}
func getWin32OperatingSystem() (Win32_OperatingSystem, error) {
var dst []Win32_OperatingSystem
q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst)
if err != nil {
return Win32_OperatingSystem{}, errors.Wrap(err, "wmi query for Win32_OperatingSystem failed")
}
if len(dst) != 1 {
return Win32_OperatingSystem{}, errors.New("wmi query for Win32_OperatingSystem failed")
}
return dst[0], nil
}
func (self *Rusage) Get(who int) error {
if who != 0 {
return ErrNotImplemented{runtime.GOOS}

View file

@ -23,6 +23,10 @@ const (
PROCESS_VM_READ uint32 = 0x0010
)
// SizeOfRtlUserProcessParameters gives the size
// of the RtlUserProcessParameters struct.
const SizeOfRtlUserProcessParameters = unsafe.Sizeof(RtlUserProcessParameters{})
// MAX_PATH is the maximum length for a path in Windows.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
const MAX_PATH = 260
@ -43,6 +47,26 @@ const (
DRIVE_RAMDISK
)
// UnicodeString is Go's equivalent for the _UNICODE_STRING struct.
type UnicodeString struct {
Size uint16
MaximumLength uint16
Buffer uintptr
}
// RtlUserProcessParameters is Go's equivalent for the
// _RTL_USER_PROCESS_PARAMETERS struct.
// A few undocumented fields are exposed.
type RtlUserProcessParameters struct {
Reserved1 [16]byte
Reserved2 [5]uintptr
CurrentDirectoryPath UnicodeString
CurrentDirectoryHandle uintptr
DllPath UnicodeString
ImagePathName UnicodeString
CommandLine UnicodeString
}
func (dt DriveType) String() string {
names := map[DriveType]string{
DRIVE_UNKNOWN: "unknown",
@ -151,25 +175,81 @@ func GetLogicalDriveStrings() ([]string, error) {
return nil, errors.Wrap(err, "GetLogicalDriveStringsW failed")
}
// Split the uint16 slice at null-terminators.
var startIdx int
var drivesUTF16 [][]uint16
for i, value := range buffer {
if value == 0 {
drivesUTF16 = append(drivesUTF16, buffer[startIdx:i])
startIdx = i + 1
return UTF16SliceToStringSlice(buffer), nil
}
// GetAccessPaths returns the list of access paths for volumes in the system.
func GetAccessPaths() ([]string, error) {
volumes, err := GetVolumes()
if err != nil {
return nil, errors.Wrap(err, "GetVolumes failed")
}
var paths []string
for _, volumeName := range volumes {
volumePaths, err := GetVolumePathsForVolume(volumeName)
if err != nil {
return nil, errors.Wrapf(err, "failed to get list of access paths for volume '%s'", volumeName)
}
if len(volumePaths) == 0 {
continue
}
// Get only the first path
paths = append(paths, volumePaths[0])
}
return paths, nil
}
// GetVolumes returs the list of volumes in the system.
// https://docs.microsoft.com/es-es/windows/desktop/api/fileapi/nf-fileapi-findfirstvolumew
func GetVolumes() ([]string, error) {
buffer := make([]uint16, MAX_PATH+1)
var volumes []string
h, err := _FindFirstVolume(&buffer[0], uint32(len(buffer)))
if err != nil {
return nil, errors.Wrap(err, "FindFirstVolumeW failed")
}
defer _FindVolumeClose(h)
for {
volumes = append(volumes, syscall.UTF16ToString(buffer))
err = _FindNextVolume(h, &buffer[0], uint32(len(buffer)))
if err != nil {
if errors.Cause(err) == syscall.ERROR_NO_MORE_FILES {
break
}
return nil, errors.Wrap(err, "FindNextVolumeW failed")
}
}
// Convert the utf16 slices to strings.
drives := make([]string, 0, len(drivesUTF16))
for _, driveUTF16 := range drivesUTF16 {
if len(driveUTF16) > 0 {
drives = append(drives, syscall.UTF16ToString(driveUTF16))
return volumes, nil
}
// GetVolumePathsForVolume returns the list of volume paths for a volume.
// https://docs.microsoft.com/en-us/windows/desktop/api/FileAPI/nf-fileapi-getvolumepathnamesforvolumenamew
func GetVolumePathsForVolume(volumeName string) ([]string, error) {
var length uint32
err := _GetVolumePathNamesForVolumeName(volumeName, nil, 0, &length)
if errors.Cause(err) != syscall.ERROR_MORE_DATA {
return nil, errors.Wrap(err, "GetVolumePathNamesForVolumeNameW failed to get needed buffer length")
}
if length == 0 {
// Not mounted, no paths, that's ok
return nil, nil
}
return drives, nil
buffer := make([]uint16, length*(MAX_PATH+1))
err = _GetVolumePathNamesForVolumeName(volumeName, &buffer[0], length, &length)
if err != nil {
return nil, errors.Wrap(err, "GetVolumePathNamesForVolumeNameW failed")
}
return UTF16SliceToStringSlice(buffer), nil
}
// GlobalMemoryStatusEx retrieves information about the system's current usage
@ -361,10 +441,127 @@ func Process32Next(handle syscall.Handle) (ProcessEntry32, error) {
return processEntry32, nil
}
// UTF16SliceToStringSlice converts slice of uint16 containing a list of UTF16
// strings to a slice of strings.
func UTF16SliceToStringSlice(buffer []uint16) []string {
// Split the uint16 slice at null-terminators.
var startIdx int
var stringsUTF16 [][]uint16
for i, value := range buffer {
if value == 0 {
stringsUTF16 = append(stringsUTF16, buffer[startIdx:i])
startIdx = i + 1
}
}
// Convert the utf16 slices to strings.
result := make([]string, 0, len(stringsUTF16))
for _, stringUTF16 := range stringsUTF16 {
if len(stringUTF16) > 0 {
result = append(result, syscall.UTF16ToString(stringUTF16))
}
}
return result
}
func GetUserProcessParams(handle syscall.Handle, pbi ProcessBasicInformation) (params RtlUserProcessParameters, err error) {
const is32bitProc = unsafe.Sizeof(uintptr(0)) == 4
// Offset of params field within PEB structure.
// This structure is different in 32 and 64 bit.
paramsOffset := 0x20
if is32bitProc {
paramsOffset = 0x10
}
// Read the PEB from the target process memory
pebSize := paramsOffset + 8
peb := make([]byte, pebSize)
nRead, err := ReadProcessMemory(handle, pbi.PebBaseAddress, peb)
if err != nil {
return params, err
}
if nRead != uintptr(pebSize) {
return params, errors.Errorf("PEB: short read (%d/%d)", nRead, pebSize)
}
// Get the RTL_USER_PROCESS_PARAMETERS struct pointer from the PEB
paramsAddr := *(*uintptr)(unsafe.Pointer(&peb[paramsOffset]))
// Read the RTL_USER_PROCESS_PARAMETERS from the target process memory
paramsBuf := make([]byte, SizeOfRtlUserProcessParameters)
nRead, err = ReadProcessMemory(handle, paramsAddr, paramsBuf)
if err != nil {
return params, err
}
if nRead != uintptr(SizeOfRtlUserProcessParameters) {
return params, errors.Errorf("RTL_USER_PROCESS_PARAMETERS: short read (%d/%d)", nRead, SizeOfRtlUserProcessParameters)
}
params = *(*RtlUserProcessParameters)(unsafe.Pointer(&paramsBuf[0]))
return params, nil
}
func ReadProcessUnicodeString(handle syscall.Handle, s *UnicodeString) ([]byte, error) {
buf := make([]byte, s.Size)
nRead, err := ReadProcessMemory(handle, s.Buffer, buf)
if err != nil {
return nil, err
}
if nRead != uintptr(s.Size) {
return nil, errors.Errorf("unicode string: short read: (%d/%d)", nRead, s.Size)
}
return buf, nil
}
// Use Windows' CommandLineToArgv API to split an UTF-16 command line string
// into a list of parameters.
func ByteSliceToStringSlice(utf16 []byte) ([]string, error) {
if len(utf16) == 0 {
return nil, nil
}
var numArgs int32
argsWide, err := syscall.CommandLineToArgv((*uint16)(unsafe.Pointer(&utf16[0])), &numArgs)
if err != nil {
return nil, err
}
// Free memory allocated for CommandLineToArgvW arguments.
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(argsWide)))
args := make([]string, numArgs)
for idx := range args {
args[idx] = syscall.UTF16ToString(argsWide[idx][:])
}
return args, nil
}
// ReadProcessMemory reads from another process memory. The Handle needs to have
// the PROCESS_VM_READ right.
// A zero-byte read is a no-op, no error is returned.
func ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, dest []byte) (numRead uintptr, err error) {
n := len(dest)
if n == 0 {
return 0, nil
}
if err = _ReadProcessMemory(handle, baseAddress, uintptr(unsafe.Pointer(&dest[0])), uintptr(n), &numRead); err != nil {
return 0, err
}
return numRead, nil
}
func GetTickCount64() (uptime uint64, err error) {
if uptime, err = _GetTickCount64(); err != nil {
return 0, err
}
return uptime, nil
}
// Use "GOOS=windows go generate -v -x ." to generate the source.
// Add -trace to enable debug prints around syscalls.
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -systemdll=false -output zsyscall_windows.go syscall_windows.go
// Windows API calls
//sys _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) = kernel32.GlobalMemoryStatusEx
@ -383,3 +580,9 @@ func Process32Next(handle syscall.Handle) (ProcessEntry32, error) {
//sys _LookupPrivilegeName(systemName string, luid *int64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
//sys _LookupPrivilegeValue(systemName string, name string, luid *int64) (err error) = advapi32.LookupPrivilegeValueW
//sys _AdjustTokenPrivileges(token syscall.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
//sys _FindFirstVolume(volumeName *uint16, size uint32) (handle syscall.Handle, err error) = kernel32.FindFirstVolumeW
//sys _FindNextVolume(handle syscall.Handle, volumeName *uint16, size uint32) (err error) = kernel32.FindNextVolumeW
//sys _FindVolumeClose(handle syscall.Handle) (err error) = kernel32.FindVolumeClose
//sys _GetVolumePathNamesForVolumeName(volumeName string, buffer *uint16, bufferSize uint32, length *uint32) (err error) = kernel32.GetVolumePathNamesForVolumeNameW
//sys _ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, buffer uintptr, size uintptr, numRead *uintptr) (err error) = kernel32.ReadProcessMemory
//sys _GetTickCount64() (uptime uint64, err error) = kernel32.GetTickCount64

View file

@ -1,12 +1,39 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
// Code generated by 'go generate'; DO NOT EDIT.
package windows
import "unsafe"
import "syscall"
import (
"syscall"
"unsafe"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
modpsapi = syscall.NewLazyDLL("psapi.dll")
@ -29,13 +56,19 @@ var (
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
procFindFirstVolumeW = modkernel32.NewProc("FindFirstVolumeW")
procFindNextVolumeW = modkernel32.NewProc("FindNextVolumeW")
procFindVolumeClose = modkernel32.NewProc("FindVolumeClose")
procGetVolumePathNamesForVolumeNameW = modkernel32.NewProc("GetVolumePathNamesForVolumeNameW")
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
procGetTickCount64 = modkernel32.NewProc("GetTickCount64")
)
func _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) {
r1, _, e1 := syscall.Syscall(procGlobalMemoryStatusEx.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -48,7 +81,7 @@ func _GetLogicalDriveStringsW(bufferLength uint32, buffer *uint16) (length uint3
length = uint32(r0)
if length == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -60,7 +93,7 @@ func _GetProcessMemoryInfo(handle syscall.Handle, psmemCounters *ProcessMemoryCo
r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(psmemCounters)), uintptr(cb))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -73,7 +106,7 @@ func _GetProcessImageFileName(handle syscall.Handle, outImageFileName *uint16, s
length = uint32(r0)
if length == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -85,7 +118,7 @@ func _GetSystemTimes(idleTime *syscall.Filetime, kernelTime *syscall.Filetime, u
r1, _, e1 := syscall.Syscall(procGetSystemTimes.Addr(), 3, uintptr(unsafe.Pointer(idleTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -98,7 +131,7 @@ func _GetDriveType(rootPathName *uint16) (dt DriveType, err error) {
dt = DriveType(r0)
if dt == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -110,7 +143,7 @@ func _EnumProcesses(processIds *uint32, sizeBytes uint32, bytesReturned *uint32)
r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(processIds)), uintptr(sizeBytes), uintptr(unsafe.Pointer(bytesReturned)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -122,7 +155,7 @@ func _GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailable *uint64, tota
r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpaceExW.Addr(), 4, uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(freeBytesAvailable)), uintptr(unsafe.Pointer(totalNumberOfBytes)), uintptr(unsafe.Pointer(totalNumberOfFreeBytes)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -134,7 +167,7 @@ func _Process32First(handle syscall.Handle, processEntry32 *ProcessEntry32) (err
r1, _, e1 := syscall.Syscall(procProcess32FirstW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(processEntry32)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -146,7 +179,7 @@ func _Process32Next(handle syscall.Handle, processEntry32 *ProcessEntry32) (err
r1, _, e1 := syscall.Syscall(procProcess32NextW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(processEntry32)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -159,7 +192,7 @@ func _CreateToolhelp32Snapshot(flags uint32, processID uint32) (handle syscall.H
handle = syscall.Handle(r0)
if handle == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -172,7 +205,7 @@ func _NtQuerySystemInformation(systemInformationClass uint32, systemInformation
ntstatus = uint32(r0)
if ntstatus == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -185,7 +218,7 @@ func _NtQueryInformationProcess(processHandle syscall.Handle, processInformation
ntstatus = uint32(r0)
if ntstatus == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -206,7 +239,7 @@ func __LookupPrivilegeName(systemName *uint16, luid *int64, buffer *uint16, size
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -232,7 +265,7 @@ func __LookupPrivilegeValue(systemName *uint16, name *uint16, luid *int64) (err
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
@ -251,7 +284,90 @@ func _AdjustTokenPrivileges(token syscall.Token, releaseAll bool, input *byte, o
success = r0 != 0
if true {
if e1 != 0 {
err = error(e1)
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _FindFirstVolume(volumeName *uint16, size uint32) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall(procFindFirstVolumeW.Addr(), 2, uintptr(unsafe.Pointer(volumeName)), uintptr(size), 0)
handle = syscall.Handle(r0)
if handle == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _FindNextVolume(handle syscall.Handle, volumeName *uint16, size uint32) (err error) {
r1, _, e1 := syscall.Syscall(procFindNextVolumeW.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(volumeName)), uintptr(size))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _FindVolumeClose(handle syscall.Handle) (err error) {
r1, _, e1 := syscall.Syscall(procFindVolumeClose.Addr(), 1, uintptr(handle), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _GetVolumePathNamesForVolumeName(volumeName string, buffer *uint16, bufferSize uint32, length *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(volumeName)
if err != nil {
return
}
return __GetVolumePathNamesForVolumeName(_p0, buffer, bufferSize, length)
}
func __GetVolumePathNamesForVolumeName(volumeName *uint16, buffer *uint16, bufferSize uint32, length *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetVolumePathNamesForVolumeNameW.Addr(), 4, uintptr(unsafe.Pointer(volumeName)), uintptr(unsafe.Pointer(buffer)), uintptr(bufferSize), uintptr(unsafe.Pointer(length)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, buffer uintptr, size uintptr, numRead *uintptr) (err error) {
r1, _, e1 := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(handle), uintptr(baseAddress), uintptr(buffer), uintptr(size), uintptr(unsafe.Pointer(numRead)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _GetTickCount64() (uptime uint64, err error) {
r0, _, e1 := syscall.Syscall(procGetTickCount64.Addr(), 0, 0, 0, 0)
uptime = uint64(r0)
if uptime == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}

View file

@ -1,6 +1,12 @@
# Color [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/color) [![Build Status](http://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
# Archived project. No maintenance.
This project is not maintained anymore and is archived. Feel free to fork and
make your own changes if needed. For more detail read my blog post: [Taking an indefinite sabbatical from my projects](https://arslan.io/2018/10/09/taking-an-indefinite-sabbatical-from-my-projects/)
Thanks to everyone for their valuable feedback and contributions.
# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color) [![Build Status](https://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
Color lets you use colorized outputs in terms of [ANSI Escape
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
@ -8,8 +14,7 @@ has support for Windows too! The API can be used in several ways, pick one that
suits you.
![Color](http://i.imgur.com/c1JI0lA.png)
![Color](https://i.imgur.com/c1JI0lA.png)
## Install
@ -18,6 +23,9 @@ suits you.
go get github.com/fatih/color
```
Note that the `vendor` folder is here for stability. Remove the folder if you
already have the dependencies in your GOPATH.
## Examples
### Standard colors
@ -127,13 +135,15 @@ defer color.Unset() // Use it in your function
fmt.Println("All text will now be bold magenta.")
```
### Disable color
### Disable/Enable color
There might be a case where you want to disable color output (for example to
pipe the standard output of your app to somewhere else). `Color` has support to
disable colors both globally and for single color definition. For example
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
the color output with:
There might be a case where you want to explicitly disable/enable color output. the
`go-isatty` package will automatically disable color output for non-tty output streams
(for example if the output were piped directly to `less`)
`Color` has support to disable/enable colors both globally and for single color
definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You
can easily disable the color output with:
```go

View file

@ -17,12 +17,16 @@ var (
// false or true based on the stdout's file descriptor referring to a terminal
// or not. This is a global option and affects all colors. For more control
// over each color block use the methods DisableColor() individually.
NoColor = !isatty.IsTerminal(os.Stdout.Fd()) || os.Getenv("TERM") == "dumb"
NoColor = os.Getenv("TERM") == "dumb" ||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
// Output defines the standard output of the print functions. By default
// os.Stdout is used.
Output = colorable.NewColorableStdout()
// Error defines a color supporting writer for os.Stderr.
Error = colorable.NewColorableStderr()
// colorsCache is used to reduce the count of created Color objects and
// allows to reuse already created objects with required Attribute.
colorsCache = make(map[Attribute]*Color)
@ -340,7 +344,7 @@ func (c *Color) SprintlnFunc() func(a ...interface{}) string {
}
}
// sequence returns a formated SGR sequence to be plugged into a "\x1b[...m"
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
// an example output might be: "1;36" -> bold cyan
func (c *Color) sequence() string {
format := make([]string, len(c.params))
@ -458,68 +462,142 @@ func colorString(format string, p Attribute, a ...interface{}) string {
return c.SprintfFunc()(format, a...)
}
// Black is an convenient helper function to print with black foreground. A
// Black is a convenient helper function to print with black foreground. A
// newline is appended to format by default.
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
// Red is an convenient helper function to print with red foreground. A
// Red is a convenient helper function to print with red foreground. A
// newline is appended to format by default.
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
// Green is an convenient helper function to print with green foreground. A
// Green is a convenient helper function to print with green foreground. A
// newline is appended to format by default.
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
// Yellow is an convenient helper function to print with yellow foreground.
// Yellow is a convenient helper function to print with yellow foreground.
// A newline is appended to format by default.
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
// Blue is an convenient helper function to print with blue foreground. A
// Blue is a convenient helper function to print with blue foreground. A
// newline is appended to format by default.
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
// Magenta is an convenient helper function to print with magenta foreground.
// Magenta is a convenient helper function to print with magenta foreground.
// A newline is appended to format by default.
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
// Cyan is an convenient helper function to print with cyan foreground. A
// Cyan is a convenient helper function to print with cyan foreground. A
// newline is appended to format by default.
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
// White is an convenient helper function to print with white foreground. A
// White is a convenient helper function to print with white foreground. A
// newline is appended to format by default.
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
// BlackString is an convenient helper function to return a string with black
// BlackString is a convenient helper function to return a string with black
// foreground.
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
// RedString is an convenient helper function to return a string with red
// RedString is a convenient helper function to return a string with red
// foreground.
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
// GreenString is an convenient helper function to return a string with green
// GreenString is a convenient helper function to return a string with green
// foreground.
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
// YellowString is an convenient helper function to return a string with yellow
// YellowString is a convenient helper function to return a string with yellow
// foreground.
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
// BlueString is an convenient helper function to return a string with blue
// BlueString is a convenient helper function to return a string with blue
// foreground.
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
// MagentaString is an convenient helper function to return a string with magenta
// MagentaString is a convenient helper function to return a string with magenta
// foreground.
func MagentaString(format string, a ...interface{}) string {
return colorString(format, FgMagenta, a...)
}
// CyanString is an convenient helper function to return a string with cyan
// CyanString is a convenient helper function to return a string with cyan
// foreground.
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
// WhiteString is an convenient helper function to return a string with white
// WhiteString is a convenient helper function to return a string with white
// foreground.
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
// newline is appended to format by default.
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
// newline is appended to format by default.
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
// newline is appended to format by default.
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
// A newline is appended to format by default.
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
// newline is appended to format by default.
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
// A newline is appended to format by default.
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
// newline is appended to format by default.
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
// newline is appended to format by default.
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
// HiBlackString is a convenient helper function to return a string with hi-intensity black
// foreground.
func HiBlackString(format string, a ...interface{}) string {
return colorString(format, FgHiBlack, a...)
}
// HiRedString is a convenient helper function to return a string with hi-intensity red
// foreground.
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
// HiGreenString is a convenient helper function to return a string with hi-intensity green
// foreground.
func HiGreenString(format string, a ...interface{}) string {
return colorString(format, FgHiGreen, a...)
}
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
// foreground.
func HiYellowString(format string, a ...interface{}) string {
return colorString(format, FgHiYellow, a...)
}
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
// foreground.
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
// foreground.
func HiMagentaString(format string, a ...interface{}) string {
return colorString(format, FgHiMagenta, a...)
}
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
// foreground.
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
// foreground.
func HiWhiteString(format string, a ...interface{}) string {
return colorString(format, FgHiWhite, a...)
}

View file

@ -15,6 +15,11 @@ Use simple and default helper functions with predefined foreground colors:
color.Yellow("Yellow color too!")
color.Magenta("And many others ..")
// Hi-intensity colors
color.HiGreen("Bright green color.")
color.HiBlack("Bright black means gray..")
color.HiWhite("Shiny white color!")
However there are times where custom color mixes are required. Below are some
examples to create custom color objects and use the print functions of each
separate color object.
@ -74,7 +79,7 @@ Or create SprintXxx functions to mix strings with other non-colorized strings:
info := New(FgWhite, BgGreen).SprintFunc()
fmt.Printf("this %s rocks!\n", info("package"))
Windows support is enabled by default. All Print functions works as intended.
Windows support is enabled by default. All Print functions work as intended.
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
set the output to color.Output:

View file

@ -37,7 +37,7 @@ func (b *bitmap) isMarked(addr uintptr) bool {
return block.isMarked(baddr)
}
// countRange returns the number of set bits in the range (addr,addr+n).
// countRange returns the number of set bits in the range [addr, addr+n].
func (b *bitmap) countRange(addr, n uintptr) uintptr {
c := uintptr(0)
for end := addr + n; addr < end; {
@ -92,7 +92,7 @@ func (b *bmBlock) isMarked(i uintptr) bool {
return (b[i/uintptrBits] & (1 << (i % uintptrBits))) != 0
}
// count returns the number of set bits in the range (start,end).
// count returns the number of set bits in the range [start, end].
func (b *bmBlock) count(start, end uintptr) (count int) {
br := b[start/uintptrBits : end/uintptrBits+1]
for i, w := range br {

View file

@ -101,8 +101,8 @@ func newContext() *context {
return &context{seen: newBitmap(), tc: make(typCache), s: newSizes()}
}
// scan walks all objects below v, determining their size. All scan* functions return the
// amount of 'extra' memory (e.g. slice data) that is referenced by the object.
// scan walks all objects below v, determining their size. It returns the size of the
// previously unscanned parts of the object.
func (c *context) scan(addr address, v reflect.Value, add bool) (extraSize uintptr) {
size := v.Type().Size()
var marked uintptr
@ -117,15 +117,17 @@ func (c *context) scan(addr address, v reflect.Value, add bool) (extraSize uintp
if c.tc.needScan(v.Type()) {
extraSize = c.scanContent(addr, v)
}
// fmt.Printf("%v: %v %d (add %v, size %d, marked %d, extra %d)\n", addr, v.Type(), size+extraSize, add, v.Type().Size(), marked, extraSize)
if add {
size -= marked
size += extraSize
// fmt.Printf("%v: %v %d (add %v, size %d, marked %d, extra %d)\n", addr, v.Type(), size+extraSize, add, v.Type().Size(), marked, extraSize)
if add {
c.s.addValue(v, size)
}
return extraSize
return size
}
// scanContent and all other scan* functions below return the amount of 'extra' memory
// (e.g. slice data) that is referenced by the object.
func (c *context) scanContent(addr address, v reflect.Value) uintptr {
switch v.Kind() {
case reflect.Array:
@ -225,8 +227,10 @@ func (c *context) scanMap(v reflect.Value) uintptr {
extra += c.scan(invalidAddr, k, false)
extra += c.scan(invalidAddr, v.MapIndex(k), false)
}
} else {
extra = len*typ.Key().Size() + len*typ.Elem().Size()
}
return len*typ.Key().Size() + len*typ.Elem().Size() + extra
return extra
}
func (c *context) scanInterface(v reflect.Value) uintptr {
@ -234,10 +238,9 @@ func (c *context) scanInterface(v reflect.Value) uintptr {
if !elem.IsValid() {
return 0 // nil interface
}
c.scan(invalidAddr, elem, false)
if !c.tc.isPointer(elem.Type()) {
// Account for non-pointer size of the value.
return elem.Type().Size()
extra := c.scan(invalidAddr, elem, false)
if elem.Type().Kind() == reflect.Ptr {
extra -= uintptrBytes
}
return 0
return extra
}

View file

@ -1,4 +1,4 @@
#Go OLE
# Go OLE
[![Build status](https://ci.appveyor.com/api/projects/status/qr0u2sf7q43us9fj?svg=true)](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28)
[![Build Status](https://travis-ci.org/go-ole/go-ole.svg?branch=master)](https://travis-ci.org/go-ole/go-ole)
@ -35,12 +35,12 @@ AppVeyor is used to build on Windows using the (in-development) test COM server.
The tests currently do run and do pass and this should be maintained with commits.
##Versioning
## Versioning
Go OLE uses [semantic versioning](http://semver.org) for version numbers, which is similar to the version contract of the Go language. Which means that the major version will always maintain backwards compatibility with minor versions. Minor versions will only add new additions and changes. Fixes will always be in patch.
This contract should allow you to upgrade to new minor and patch versions without breakage or modifications to your existing code. Leave a ticket, if there is breakage, so that it could be fixed.
##LICENSE
## LICENSE
Under the MIT License: http://mattn.mit-license.org/2013

View file

@ -3,9 +3,7 @@
package ole
import (
"errors"
"syscall"
"time"
"unicode/utf16"
"unsafe"
)
@ -21,6 +19,7 @@ var (
procStringFromCLSID, _ = modole32.FindProc("StringFromCLSID")
procStringFromIID, _ = modole32.FindProc("StringFromIID")
procIIDFromString, _ = modole32.FindProc("IIDFromString")
procCoGetObject, _ = modole32.FindProc("CoGetObject")
procGetUserDefaultLCID, _ = modkernel32.FindProc("GetUserDefaultLCID")
procCopyMemory, _ = modkernel32.FindProc("RtlMoveMemory")
procVariantInit, _ = modoleaut32.FindProc("VariantInit")
@ -209,6 +208,32 @@ func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
return
}
type BindOpts struct {
CbStruct uint32
GrfFlags uint32
GrfMode uint32
TickCountDeadline uint32
}
// GetObject retrieves pointer to active object.
func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) {
if bindOpts != nil {
bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{}))
}
if iid == nil {
iid = IID_IUnknown
}
hr, _, _ := procCoGetObject.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))),
uintptr(unsafe.Pointer(bindOpts)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = NewError(hr)
}
return
}
// VariantInit initializes variant.
func VariantInit(v *VARIANT) (err error) {
hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
@ -317,13 +342,3 @@ func DispatchMessage(msg *Msg) (ret int32) {
ret = int32(r0)
return
}
// GetVariantDate converts COM Variant Time value to Go time.Time.
func GetVariantDate(value float64) (time.Time, error) {
var st syscall.Systemtime
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}

View file

@ -169,6 +169,6 @@ func DispatchMessage(msg *Msg) int32 {
return int32(0)
}
func GetVariantDate(value float64) (time.Time, error) {
func GetVariantDate(value uint64) (time.Time, error) {
return time.Now(), NewError(E_NOTIMPL)
}

View file

@ -3,6 +3,7 @@
package ole
import (
"math/big"
"syscall"
"time"
"unsafe"
@ -132,6 +133,8 @@ func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}
vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv)))
case *float64:
vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64)))))
case *big.Int:
vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64())
case string:
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string))))))
case *string:

View file

@ -124,12 +124,12 @@ func safeArrayGetElementSize(safearray *SafeArray) (*uint32, error) {
}
// safeArrayGetElement retrieves element at given index.
func safeArrayGetElement(safearray *SafeArray, index int64, pv unsafe.Pointer) error {
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
return NewError(E_NOTIMPL)
}
// safeArrayGetElement retrieves element at given index and converts to string.
func safeArrayGetElementString(safearray *SafeArray, index int64) (string, error) {
func safeArrayGetElementString(safearray *SafeArray, index int32) (string, error) {
return "", NewError(E_NOTIMPL)
}
@ -146,8 +146,8 @@ func safeArrayGetIID(safearray *SafeArray) (*GUID, error) {
// multidimensional array.
//
// AKA: SafeArrayGetLBound in Windows API.
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int64, error) {
return int64(0), NewError(E_NOTIMPL)
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int32, error) {
return int32(0), NewError(E_NOTIMPL)
}
// safeArrayGetUBound returns upper bounds of SafeArray.
@ -156,8 +156,8 @@ func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int64, error) {
// multidimensional array.
//
// AKA: SafeArrayGetUBound in Windows API.
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int64, error) {
return int64(0), NewError(E_NOTIMPL)
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int32, error) {
return int32(0), NewError(E_NOTIMPL)
}
// safeArrayGetVartype returns data type of SafeArray.

View file

@ -205,7 +205,7 @@ func safeArrayGetElementSize(safearray *SafeArray) (length *uint32, err error) {
}
// safeArrayGetElement retrieves element at given index.
func safeArrayGetElement(safearray *SafeArray, index int64, pv unsafe.Pointer) error {
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
return convertHresultToError(
procSafeArrayGetElement.Call(
uintptr(unsafe.Pointer(safearray)),
@ -214,7 +214,7 @@ func safeArrayGetElement(safearray *SafeArray, index int64, pv unsafe.Pointer) e
}
// safeArrayGetElementString retrieves element at given index and converts to string.
func safeArrayGetElementString(safearray *SafeArray, index int64) (str string, err error) {
func safeArrayGetElementString(safearray *SafeArray, index int32) (str string, err error) {
var element *int16
err = convertHresultToError(
procSafeArrayGetElement.Call(
@ -243,7 +243,7 @@ func safeArrayGetIID(safearray *SafeArray) (guid *GUID, err error) {
// multidimensional array.
//
// AKA: SafeArrayGetLBound in Windows API.
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int64, err error) {
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int32, err error) {
err = convertHresultToError(
procSafeArrayGetLBound.Call(
uintptr(unsafe.Pointer(safearray)),
@ -258,7 +258,7 @@ func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int6
// multidimensional array.
//
// AKA: SafeArrayGetUBound in Windows API.
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int64, err error) {
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int32, err error) {
err = convertHresultToError(
procSafeArrayGetUBound.Call(
uintptr(unsafe.Pointer(safearray)),

View file

@ -14,7 +14,7 @@ func (sac *SafeArrayConversion) ToStringArray() (strings []string) {
totalElements, _ := sac.TotalElements(0)
strings = make([]string, totalElements)
for i := int64(0); i < totalElements; i++ {
for i := int32(0); i < totalElements; i++ {
strings[int32(i)], _ = safeArrayGetElementString(sac.Array, i)
}
@ -25,7 +25,7 @@ func (sac *SafeArrayConversion) ToByteArray() (bytes []byte) {
totalElements, _ := sac.TotalElements(0)
bytes = make([]byte, totalElements)
for i := int64(0); i < totalElements; i++ {
for i := int32(0); i < totalElements; i++ {
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&bytes[int32(i)]))
}
@ -37,59 +37,59 @@ func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) {
values = make([]interface{}, totalElements)
vt, _ := safeArrayGetVartype(sac.Array)
for i := 0; i < int(totalElements); i++ {
for i := int32(0); i < totalElements; i++ {
switch VT(vt) {
case VT_BOOL:
var v bool
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I1:
var v int8
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I2:
var v int16
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I4:
var v int32
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I8:
var v int64
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI1:
var v uint8
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI2:
var v uint16
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI4:
var v uint32
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI8:
var v uint64
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_R4:
var v float32
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_R8:
var v float64
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_BSTR:
var v string
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_VARIANT:
var v VARIANT
safeArrayGetElement(sac.Array, int64(i), unsafe.Pointer(&v))
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v.Value()
default:
// TODO
@ -111,14 +111,14 @@ func (sac *SafeArrayConversion) GetSize() (length *uint32, err error) {
return safeArrayGetElementSize(sac.Array)
}
func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int64, err error) {
func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int32, err error) {
if index < 1 {
index = 1
}
// Get array bounds
var LowerBounds int64
var UpperBounds int64
var LowerBounds int32
var UpperBounds int32
LowerBounds, err = safeArrayGetLBound(sac.Array, index)
if err != nil {

View file

@ -88,10 +88,10 @@ func (v *VARIANT) Value() interface{} {
return v.ToString()
case VT_DATE:
// VT_DATE type will either return float64 or time.Time.
d := float64(v.Val)
d := uint64(v.Val)
date, err := GetVariantDate(d)
if err != nil {
return d
return float64(v.Val)
}
return date
case VT_UNKNOWN:

View file

@ -1,3 +1,5 @@
// +build go1.7
// Package stack implements utilities to capture, manipulate, and format call
// stacks. It provides a simpler API than package runtime.
//
@ -21,29 +23,31 @@ import (
// Call records a single function invocation from a goroutine stack.
type Call struct {
fn *runtime.Func
pc uintptr
frame runtime.Frame
}
// Caller returns a Call from the stack of the current goroutine. The argument
// skip is the number of stack frames to ascend, with 0 identifying the
// calling function.
func Caller(skip int) Call {
var pcs [2]uintptr
// As of Go 1.9 we need room for up to three PC entries.
//
// 0. An entry for the stack frame prior to the target to check for
// special handling needed if that prior entry is runtime.sigpanic.
// 1. A possible second entry to hold metadata about skipped inlined
// functions. If inline functions were not skipped the target frame
// PC will be here.
// 2. A third entry for the target frame PC when the second entry
// is used for skipped inline functions.
var pcs [3]uintptr
n := runtime.Callers(skip+1, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
frame, _ := frames.Next()
frame, _ = frames.Next()
var c Call
if n < 2 {
return c
return Call{
frame: frame,
}
c.pc = pcs[1]
if runtime.FuncForPC(pcs[0]).Name() != "runtime.sigpanic" {
c.pc--
}
c.fn = runtime.FuncForPC(c.pc)
return c
}
// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
@ -54,9 +58,10 @@ func (c Call) String() string {
// MarshalText implements encoding.TextMarshaler. It formats the Call the same
// as fmt.Sprintf("%v", c).
func (c Call) MarshalText() ([]byte, error) {
if c.fn == nil {
if c.frame == (runtime.Frame{}) {
return nil, ErrNoFunc
}
buf := bytes.Buffer{}
fmt.Fprint(&buf, c)
return buf.Bytes(), nil
@ -71,29 +76,33 @@ var ErrNoFunc = errors.New("no call stack information")
// %s source file
// %d line number
// %n function name
// %k last segment of the package path
// %v equivalent to %s:%d
//
// It accepts the '+' and '#' flags for most of the verbs as follows.
//
// %+s path of source file relative to the compile time GOPATH
// %+s path of source file relative to the compile time GOPATH,
// or the module path joined to the path of source file relative
// to module root
// %#s full path of source file
// %+n import path qualified function name
// %+k full package path
// %+v equivalent to %+s:%d
// %#v equivalent to %#s:%d
func (c Call) Format(s fmt.State, verb rune) {
if c.fn == nil {
if c.frame == (runtime.Frame{}) {
fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
return
}
switch verb {
case 's', 'v':
file, line := c.fn.FileLine(c.pc)
file := c.frame.File
switch {
case s.Flag('#'):
// done
case s.Flag('+'):
file = file[pkgIndex(file, c.fn.Name()):]
file = pkgFilePath(&c.frame)
default:
const sep = "/"
if i := strings.LastIndex(file, sep); i != -1 {
@ -103,16 +112,31 @@ func (c Call) Format(s fmt.State, verb rune) {
io.WriteString(s, file)
if verb == 'v' {
buf := [7]byte{':'}
s.Write(strconv.AppendInt(buf[:1], int64(line), 10))
s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10))
}
case 'd':
_, line := c.fn.FileLine(c.pc)
buf := [6]byte{}
s.Write(strconv.AppendInt(buf[:0], int64(line), 10))
s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10))
case 'k':
name := c.frame.Function
const pathSep = "/"
start, end := 0, len(name)
if i := strings.LastIndex(name, pathSep); i != -1 {
start = i + len(pathSep)
}
const pkgSep = "."
if i := strings.Index(name[start:], pkgSep); i != -1 {
end = start + i
}
if s.Flag('+') {
start = 0
}
io.WriteString(s, name[start:end])
case 'n':
name := c.fn.Name()
name := c.frame.Function
if !s.Flag('+') {
const pathSep = "/"
if i := strings.LastIndex(name, pathSep); i != -1 {
@ -127,35 +151,17 @@ func (c Call) Format(s fmt.State, verb rune) {
}
}
// Frame returns the call frame infomation for the Call.
func (c Call) Frame() runtime.Frame {
return c.frame
}
// PC returns the program counter for this call frame; multiple frames may
// have the same PC value.
//
// Deprecated: Use Call.Frame instead.
func (c Call) PC() uintptr {
return c.pc
}
// name returns the import path qualified name of the function containing the
// call.
func (c Call) name() string {
if c.fn == nil {
return "???"
}
return c.fn.Name()
}
func (c Call) file() string {
if c.fn == nil {
return "???"
}
file, _ := c.fn.FileLine(c.pc)
return file
}
func (c Call) line() int {
if c.fn == nil {
return 0
}
_, line := c.fn.FileLine(c.pc)
return line
return c.frame.PC
}
// CallStack records a sequence of function invocations from a goroutine
@ -179,9 +185,6 @@ func (cs CallStack) MarshalText() ([]byte, error) {
buf := bytes.Buffer{}
buf.Write(openBracketBytes)
for i, pc := range cs {
if pc.fn == nil {
return nil, ErrNoFunc
}
if i > 0 {
buf.Write(spaceBytes)
}
@ -209,18 +212,18 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
// identifying the calling function.
func Trace() CallStack {
var pcs [512]uintptr
n := runtime.Callers(2, pcs[:])
cs := make([]Call, n)
n := runtime.Callers(1, pcs[:])
for i, pc := range pcs[:n] {
pcFix := pc
if i > 0 && cs[i-1].fn.Name() != "runtime.sigpanic" {
pcFix--
}
cs[i] = Call{
fn: runtime.FuncForPC(pcFix),
pc: pcFix,
}
frames := runtime.CallersFrames(pcs[:n])
cs := make(CallStack, 0, n)
// Skip extra frame retrieved just to make sure the runtime.sigpanic
// special case is handled.
frame, more := frames.Next()
for more {
frame, more = frames.Next()
cs = append(cs, Call{frame: frame})
}
return cs
@ -229,7 +232,7 @@ func Trace() CallStack {
// TrimBelow returns a slice of the CallStack with all entries below c
// removed.
func (cs CallStack) TrimBelow(c Call) CallStack {
for len(cs) > 0 && cs[0].pc != c.pc {
for len(cs) > 0 && cs[0] != c {
cs = cs[1:]
}
return cs
@ -238,7 +241,7 @@ func (cs CallStack) TrimBelow(c Call) CallStack {
// TrimAbove returns a slice of the CallStack with all entries above c
// removed.
func (cs CallStack) TrimAbove(c Call) CallStack {
for len(cs) > 0 && cs[len(cs)-1].pc != c.pc {
for len(cs) > 0 && cs[len(cs)-1] != c {
cs = cs[:len(cs)-1]
}
return cs
@ -284,15 +287,90 @@ func pkgIndex(file, funcName string) int {
return i + len(sep)
}
// pkgFilePath returns the frame's filepath relative to the compile-time GOPATH,
// or its module path joined to its path relative to the module root.
//
// As of Go 1.11 there is no direct way to know the compile time GOPATH or
// module paths at runtime, but we can piece together the desired information
// from available information. We note that runtime.Frame.Function contains the
// function name qualified by the package path, which includes the module path
// but not the GOPATH. We can extract the package path from that and append the
// last segments of the file path to arrive at the desired package qualified
// file path. For example, given:
//
// GOPATH /home/user
// import path pkg/sub
// frame.File /home/user/src/pkg/sub/file.go
// frame.Function pkg/sub.Type.Method
// Desired return pkg/sub/file.go
//
// It appears that we simply need to trim ".Type.Method" from frame.Function and
// append "/" + path.Base(file).
//
// But there are other wrinkles. Although it is idiomatic to do so, the internal
// name of a package is not required to match the last segment of its import
// path. In addition, the introduction of modules in Go 1.11 allows working
// without a GOPATH. So we also must make these work right:
//
// GOPATH /home/user
// import path pkg/go-sub
// package name sub
// frame.File /home/user/src/pkg/go-sub/file.go
// frame.Function pkg/sub.Type.Method
// Desired return pkg/go-sub/file.go
//
// Module path pkg/v2
// import path pkg/v2/go-sub
// package name sub
// frame.File /home/user/cloned-pkg/go-sub/file.go
// frame.Function pkg/v2/sub.Type.Method
// Desired return pkg/v2/go-sub/file.go
//
// We can handle all of these situations by using the package path extracted
// from frame.Function up to, but not including, the last segment as the prefix
// and the last two segments of frame.File as the suffix of the returned path.
// This preserves the existing behavior when working in a GOPATH without modules
// and a semantically equivalent behavior when used in module aware project.
func pkgFilePath(frame *runtime.Frame) string {
pre := pkgPrefix(frame.Function)
post := pathSuffix(frame.File)
if pre == "" {
return post
}
return pre + "/" + post
}
// pkgPrefix returns the import path of the function's package with the final
// segment removed.
func pkgPrefix(funcName string) string {
const pathSep = "/"
end := strings.LastIndex(funcName, pathSep)
if end == -1 {
return ""
}
return funcName[:end]
}
// pathSuffix returns the last two segments of path.
func pathSuffix(path string) string {
const pathSep = "/"
lastSep := strings.LastIndex(path, pathSep)
if lastSep == -1 {
return path
}
return path[strings.LastIndex(path[:lastSep], pathSep)+1:]
}
var runtimePath string
func init() {
var pcs [1]uintptr
var pcs [3]uintptr
runtime.Callers(0, pcs[:])
fn := runtime.FuncForPC(pcs[0])
file, _ := fn.FileLine(pcs[0])
frames := runtime.CallersFrames(pcs[:])
frame, _ := frames.Next()
file := frame.File
idx := pkgIndex(file, fn.Name())
idx := pkgIndex(frame.File, frame.Function)
runtimePath = file[:idx]
if runtime.GOOS == "windows" {
@ -301,7 +379,7 @@ func init() {
}
func inGoroot(c Call) bool {
file := c.file()
file := c.frame.File
if len(file) == 0 || file[0] == '?' {
return true
}

View file

@ -2,10 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package snappy implements the snappy block-based compression format.
// It aims for very high speeds and reasonable compression.
// Package snappy implements the Snappy compression format. It aims for very
// high speeds and reasonable compression.
//
// The C++ snappy implementation is at https://github.com/google/snappy
// There are actually two Snappy formats: block and stream. They are related,
// but different: trying to decompress block-compressed data as a Snappy stream
// will fail, and vice versa. The block format is the Decode and Encode
// functions and the stream format is the Reader and Writer types.
//
// The block format, the more common case, is used when the complete size (the
// number of bytes) of the original data is known upfront, at the time
// compression starts. The stream format, also known as the framing format, is
// for when that isn't always true.
//
// The canonical, C++ implementation is at https://github.com/google/snappy and
// it only implements the block format.
package snappy // import "github.com/golang/snappy"
import (

View file

@ -1,25 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/opentracing/opentracing-go"
packages = [
".",
"ext",
"log"
]
revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38"
version = "v1.0.2"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context"]
revision = "f5dfe339be1d06f81b22525fe34671ee7d2c8904"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "f417062128566756a9360b1c13ada79bdeeb6bab1f53ee9147a3328d95c1653f"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,10 +0,0 @@
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
[[constraint]]
name = "github.com/opentracing/opentracing-go"
version = "1.0.2"
[prune]
go-tests = true
unused-packages = true

View file

@ -16,6 +16,8 @@ safe for production use.
- resolvers are matched to the schema based on method sets (can resolve a GraphQL schema with a Go interface or Go struct).
- handles panics in resolvers
- parallel execution of resolvers
- subscriptions
- [sample WS transport](https://github.com/graph-gophers/graphql-transport-ws)
## Roadmap
@ -63,7 +65,17 @@ $ curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query
### Resolvers
A resolver must have one method for each field of the GraphQL type it resolves. The method name has to be [exported](https://golang.org/ref/spec#Exported_identifiers) and match the field's name in a non-case-sensitive way.
A resolver must have one method or field for each field of the GraphQL type it resolves. The method or field name has to be [exported](https://golang.org/ref/spec#Exported_identifiers) and match the schema's field's name in a non-case-sensitive way.
You can use struct fields as resolvers by using `SchemaOpt: UseFieldResolvers()`. For example,
```
opts := []graphql.SchemaOpt{graphql.UseFieldResolvers()}
schema := graphql.MustParseSchema(s, &query{}, opts...)
```
When using `UseFieldResolvers` schema option, a struct field will be used *only* when:
- there is no method for a struct field
- a struct field does not implement an interface method
- a struct field does not have arguments
The method has up to two arguments:

View file

@ -10,6 +10,7 @@ type QueryError struct {
Path []interface{} `json:"path,omitempty"`
Rule string `json:"-"`
ResolverError error `json:"-"`
Extensions map[string]interface{} `json:"extensions,omitempty"`
}
type Location struct {

View file

@ -2,9 +2,9 @@ package graphql
import (
"context"
"fmt"
"encoding/json"
"fmt"
"reflect"
"github.com/graph-gophers/graphql-go/errors"
"github.com/graph-gophers/graphql-go/internal/common"
@ -34,17 +34,15 @@ func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (
opt(s)
}
if err := s.schema.Parse(schemaString); err != nil {
if err := s.schema.Parse(schemaString, s.useStringDescriptions); err != nil {
return nil, err
}
if resolver != nil {
r, err := resolvable.ApplyResolver(s.schema, resolver)
if err != nil {
return nil, err
}
s.res = r
}
return s, nil
}
@ -68,11 +66,30 @@ type Schema struct {
tracer trace.Tracer
validationTracer trace.ValidationTracer
logger log.Logger
useStringDescriptions bool
disableIntrospection bool
}
// SchemaOpt is an option to pass to ParseSchema or MustParseSchema.
type SchemaOpt func(*Schema)
// UseStringDescriptions enables the usage of double quoted and triple quoted
// strings as descriptions as per the June 2018 spec
// https://facebook.github.io/graphql/June2018/. When this is not enabled,
// comments are parsed as descriptions instead.
func UseStringDescriptions() SchemaOpt {
return func(s *Schema) {
s.useStringDescriptions = true
}
}
// UseFieldResolvers specifies whether to use struct field resolvers
func UseFieldResolvers() SchemaOpt {
return func(s *Schema) {
s.schema.UseFieldResolvers = true
}
}
// MaxDepth specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking.
func MaxDepth(n int) SchemaOpt {
return func(s *Schema) {
@ -108,6 +125,13 @@ func Logger(logger log.Logger) SchemaOpt {
}
}
// DisableIntrospection disables introspection queries.
func DisableIntrospection() SchemaOpt {
return func(s *Schema) {
s.disableIntrospection = true
}
}
// Response represents a typical response of a GraphQL server. It may be encoded to JSON directly or
// it may be further processed to a custom response type, for example to include custom error data.
// Errors are intentionally serialized first based on the advice in https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107
@ -124,14 +148,14 @@ func (s *Schema) Validate(queryString string) []*errors.QueryError {
return []*errors.QueryError{qErr}
}
return validation.Validate(s.schema, doc, s.maxDepth)
return validation.Validate(s.schema, doc, nil, s.maxDepth)
}
// Exec executes the given query with the schema's resolver. It panics if the schema was created
// without a resolver. If the context get cancelled, no further resolvers will be called and a
// the context error will be returned as soon as possible (not immediately).
func (s *Schema) Exec(ctx context.Context, queryString string, operationName string, variables map[string]interface{}) *Response {
if s.res == nil {
if s.res.Resolver == (reflect.Value{}) {
panic("schema created without resolver, can not exec")
}
return s.exec(ctx, queryString, operationName, variables, s.res)
@ -144,7 +168,7 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str
}
validationFinish := s.validationTracer.TraceValidation()
errs := validation.Validate(s.schema, doc, s.maxDepth)
errs := validation.Validate(s.schema, doc, variables, s.maxDepth)
validationFinish(errs)
if len(errs) != 0 {
return &Response{Errors: errs}
@ -155,11 +179,22 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str
return &Response{Errors: []*errors.QueryError{errors.Errorf("%s", err)}}
}
// Fill in variables with the defaults from the operation
if variables == nil {
variables = make(map[string]interface{}, len(op.Vars))
}
for _, v := range op.Vars {
if _, ok := variables[v.Name.Name]; !ok && v.Default != nil {
variables[v.Name.Name] = v.Default.Value(nil)
}
}
r := &exec.Request{
Request: selected.Request{
Doc: doc,
Vars: variables,
Schema: s.schema,
DisableIntrospection: s.disableIntrospection,
},
Limiter: make(chan struct{}, s.maxParallelism),
Tracer: s.tracer,

View file

@ -1,7 +1,9 @@
package common
import (
"bytes"
"fmt"
"strconv"
"strings"
"text/scanner"
@ -13,7 +15,8 @@ type syntaxError string
type Lexer struct {
sc *scanner.Scanner
next rune
descComment string
comment bytes.Buffer
useStringDescriptions bool
}
type Ident struct {
@ -21,13 +24,13 @@ type Ident struct {
Loc errors.Location
}
func NewLexer(s string) *Lexer {
func NewLexer(s string, useStringDescriptions bool) *Lexer {
sc := &scanner.Scanner{
Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings,
}
sc.Init(strings.NewReader(s))
return &Lexer{sc: sc}
return &Lexer{sc: sc, useStringDescriptions: useStringDescriptions}
}
func (l *Lexer) CatchSyntaxError(f func()) (errRes *errors.QueryError) {
@ -50,13 +53,13 @@ func (l *Lexer) Peek() rune {
return l.next
}
// Consume whitespace and tokens equivalent to whitespace (e.g. commas and comments).
// ConsumeWhitespace consumes whitespace and tokens equivalent to whitespace (e.g. commas and comments).
//
// Consumed comment characters will build the description for the next type or field encountered.
// The description is available from `DescComment()`, and will be reset every time `Consume()` is
// executed.
func (l *Lexer) Consume() {
l.descComment = ""
// The description is available from `DescComment()`, and will be reset every time `ConsumeWhitespace()` is
// executed unless l.useStringDescriptions is set.
func (l *Lexer) ConsumeWhitespace() {
l.comment.Reset()
for {
l.next = l.sc.Scan()
@ -75,7 +78,6 @@ func (l *Lexer) Consume() {
// A comment can contain any Unicode code point except `LineTerminator` so a comment always
// consists of all code points starting with the '#' character up to but not including the
// line terminator.
l.consumeComment()
continue
}
@ -84,6 +86,29 @@ func (l *Lexer) Consume() {
}
}
// consumeDescription optionally consumes a description based on the June 2018 graphql spec if any are present.
//
// Single quote strings are also single line. Triple quote strings can be multi-line. Triple quote strings
// whitespace trimmed on both ends.
// If a description is found, consume any following comments as well
//
// http://facebook.github.io/graphql/June2018/#sec-Descriptions
func (l *Lexer) consumeDescription() string {
// If the next token is not a string, we don't consume it
if l.next != scanner.String {
return ""
}
// Triple quote string is an empty "string" followed by an open quote due to the way the parser treats strings as one token
var desc string
if l.sc.Peek() == '"' {
desc = l.consumeTripleQuoteComment()
} else {
desc = l.consumeStringComment()
}
l.ConsumeWhitespace()
return desc
}
func (l *Lexer) ConsumeIdent() string {
name := l.sc.TokenText()
l.ConsumeToken(scanner.Ident)
@ -101,12 +126,12 @@ func (l *Lexer) ConsumeKeyword(keyword string) {
if l.next != scanner.Ident || l.sc.TokenText() != keyword {
l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %q", l.sc.TokenText(), keyword))
}
l.Consume()
l.ConsumeWhitespace()
}
func (l *Lexer) ConsumeLiteral() *BasicLit {
lit := &BasicLit{Type: l.next, Text: l.sc.TokenText()}
l.Consume()
l.ConsumeWhitespace()
return lit
}
@ -114,11 +139,16 @@ func (l *Lexer) ConsumeToken(expected rune) {
if l.next != expected {
l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %s", l.sc.TokenText(), scanner.TokenString(expected)))
}
l.Consume()
l.ConsumeWhitespace()
}
func (l *Lexer) DescComment() string {
return l.descComment
comment := l.comment.String()
desc := l.consumeDescription()
if l.useStringDescriptions {
return desc
}
return comment
}
func (l *Lexer) SyntaxError(message string) {
@ -132,11 +162,45 @@ func (l *Lexer) Location() errors.Location {
}
}
func (l *Lexer) consumeTripleQuoteComment() string {
l.next = l.sc.Next()
if l.next != '"' {
panic("consumeTripleQuoteComment used in wrong context: no third quote?")
}
var buf bytes.Buffer
var numQuotes int
for {
l.next = l.sc.Next()
if l.next == '"' {
numQuotes++
} else {
numQuotes = 0
}
buf.WriteRune(l.next)
if numQuotes == 3 || l.next == scanner.EOF {
break
}
}
val := buf.String()
val = val[:len(val)-numQuotes]
val = strings.TrimSpace(val)
return val
}
func (l *Lexer) consumeStringComment() string {
val, err := strconv.Unquote(l.sc.TokenText())
if err != nil {
panic(err)
}
return val
}
// consumeComment consumes all characters from `#` to the first encountered line terminator.
// The characters are appended to `l.descComment`.
// The characters are appended to `l.comment`.
func (l *Lexer) consumeComment() {
if l.next != '#' {
return
panic("consumeComment used in wrong context")
}
// TODO: count and trim whitespace so we can dedent any following lines.
@ -144,9 +208,8 @@ func (l *Lexer) consumeComment() {
l.sc.Next()
}
if l.descComment != "" {
// TODO: use a bytes.Buffer or strings.Builder instead of this.
l.descComment += "\n"
if l.comment.Len() > 0 {
l.comment.WriteRune('\n')
}
for {
@ -154,8 +217,6 @@ func (l *Lexer) consumeComment() {
if next == '\r' || next == '\n' || next == scanner.EOF {
break
}
// TODO: use a bytes.Buffer or strings.Build instead of this.
l.descComment += string(next)
l.comment.WriteRune(next)
}
}

View file

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"reflect"
"sync"
@ -31,6 +32,10 @@ func (r *Request) handlePanic(ctx context.Context) {
}
}
type extensionser interface {
Extensions() map[string]interface{}
}
func makePanicError(value interface{}) *errors.QueryError {
return errors.Errorf("graphql: panic occurred: %v", value)
}
@ -40,7 +45,7 @@ func (r *Request) Execute(ctx context.Context, s *resolvable.Schema, op *query.O
func() {
defer r.handlePanic(ctx)
sels := selected.ApplyOperation(&r.Request, s, op)
r.execSelections(ctx, sels, nil, s.Resolver, &out, op.Type == query.Mutation)
r.execSelections(ctx, sels, nil, s, s.Resolver, &out, op.Type == query.Mutation)
}()
if err := ctx.Err(); err != nil {
@ -57,11 +62,15 @@ type fieldToExec struct {
out *bytes.Buffer
}
func (r *Request) execSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, resolver reflect.Value, out *bytes.Buffer, serially bool) {
func resolvedToNull(b *bytes.Buffer) bool {
return bytes.Equal(b.Bytes(), []byte("null"))
}
func (r *Request) execSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer, serially bool) {
async := !serially && selected.HasAsyncSel(sels)
var fields []*fieldToExec
collectFieldsToResolve(sels, resolver, &fields, make(map[string]*fieldToExec))
collectFieldsToResolve(sels, s, resolver, &fields, make(map[string]*fieldToExec))
if async {
var wg sync.WaitGroup
@ -71,14 +80,28 @@ func (r *Request) execSelections(ctx context.Context, sels []selected.Selection,
defer wg.Done()
defer r.handlePanic(ctx)
f.out = new(bytes.Buffer)
execFieldSelection(ctx, r, f, &pathSegment{path, f.field.Alias}, true)
execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true)
}(f)
}
wg.Wait()
} else {
for _, f := range fields {
f.out = new(bytes.Buffer)
execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true)
}
}
out.WriteByte('{')
for i, f := range fields {
// If a non-nullable child resolved to null, an error was added to the
// "errors" list in the response, so this field resolves to null.
// If this field is non-nullable, the error is propagated to its parent.
if _, ok := f.field.Type.(*common.NonNull); ok && resolvedToNull(f.out) {
out.Reset()
out.Write([]byte("null"))
return
}
if i > 0 {
out.WriteByte(',')
}
@ -86,17 +109,12 @@ func (r *Request) execSelections(ctx context.Context, sels []selected.Selection,
out.WriteString(f.field.Alias)
out.WriteByte('"')
out.WriteByte(':')
if async {
out.Write(f.out.Bytes())
continue
}
f.out = out
execFieldSelection(ctx, r, f, &pathSegment{path, f.field.Alias}, false)
}
out.WriteByte('}')
}
func collectFieldsToResolve(sels []selected.Selection, resolver reflect.Value, fields *[]*fieldToExec, fieldByAlias map[string]*fieldToExec) {
func collectFieldsToResolve(sels []selected.Selection, s *resolvable.Schema, resolver reflect.Value, fields *[]*fieldToExec, fieldByAlias map[string]*fieldToExec) {
for _, sel := range sels {
switch sel := sel.(type) {
case *selected.SchemaField:
@ -110,7 +128,7 @@ func collectFieldsToResolve(sels []selected.Selection, resolver reflect.Value, f
case *selected.TypenameField:
sf := &selected.SchemaField{
Field: resolvable.MetaFieldTypename,
Field: s.Meta.FieldTypename,
Alias: sel.Alias,
FixedResult: reflect.ValueOf(typeOf(sel, resolver)),
}
@ -121,7 +139,7 @@ func collectFieldsToResolve(sels []selected.Selection, resolver reflect.Value, f
if !out[1].Bool() {
continue
}
collectFieldsToResolve(sel.Sels, out[0], fields, fieldByAlias)
collectFieldsToResolve(sel.Sels, s, out[0], fields, fieldByAlias)
default:
panic("unreachable")
@ -142,7 +160,7 @@ func typeOf(tf *selected.TypenameField, resolver reflect.Value) string {
return ""
}
func execFieldSelection(ctx context.Context, r *Request, f *fieldToExec, path *pathSegment, applyLimiter bool) {
func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f *fieldToExec, path *pathSegment, applyLimiter bool) {
if applyLimiter {
r.Limiter <- struct{}{}
}
@ -173,6 +191,8 @@ func execFieldSelection(ctx context.Context, r *Request, f *fieldToExec, path *p
return errors.Errorf("%s", err) // don't execute any more resolvers if context got cancelled
}
res := f.resolver
if f.field.UseMethodResolver() {
var in []reflect.Value
if f.field.HasContext {
in = append(in, reflect.ValueOf(traceCtx))
@ -180,15 +200,25 @@ func execFieldSelection(ctx context.Context, r *Request, f *fieldToExec, path *p
if f.field.ArgsPacker != nil {
in = append(in, f.field.PackedArgs)
}
callOut := f.resolver.Method(f.field.MethodIndex).Call(in)
callOut := res.Method(f.field.MethodIndex).Call(in)
result = callOut[0]
if f.field.HasError && !callOut[1].IsNil() {
resolverErr := callOut[1].Interface().(error)
err := errors.Errorf("%s", resolverErr)
err.Path = path.toSlice()
err.ResolverError = resolverErr
if ex, ok := callOut[1].Interface().(extensionser); ok {
err.Extensions = ex.Extensions()
}
return err
}
} else {
// TODO extract out unwrapping ptr logic to a common place
if res.Kind() == reflect.Ptr {
res = res.Elem()
}
result = res.Field(f.field.FieldIndex)
}
return nil
}()
@ -197,28 +227,35 @@ func execFieldSelection(ctx context.Context, r *Request, f *fieldToExec, path *p
}
if err != nil {
// If an error occurred while resolving a field, it should be treated as though the field
// returned null, and an error must be added to the "errors" list in the response.
r.AddError(err)
f.out.WriteString("null") // TODO handle non-nil
f.out.WriteString("null")
return
}
r.execSelectionSet(traceCtx, f.sels, f.field.Type, path, result, f.out)
r.execSelectionSet(traceCtx, f.sels, f.field.Type, path, s, result, f.out)
}
func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selection, typ common.Type, path *pathSegment, resolver reflect.Value, out *bytes.Buffer) {
func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selection, typ common.Type, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) {
t, nonNull := unwrapNonNull(typ)
switch t := t.(type) {
case *schema.Object, *schema.Interface, *schema.Union:
// a reflect.Value of a nil interface will show up as an Invalid value
if resolver.Kind() == reflect.Invalid || ((resolver.Kind() == reflect.Ptr || resolver.Kind() == reflect.Interface) && resolver.IsNil()) {
// If a field of a non-null type resolves to null (either because the
// function to resolve the field returned null or because an error occurred),
// add an error to the "errors" list in the response.
if nonNull {
panic(errors.Errorf("got nil for non-null %q", t))
err := errors.Errorf("graphql: got nil for non-null %q", t)
err.Path = path.toSlice()
r.AddError(err)
}
out.WriteString("null")
return
}
r.execSelections(ctx, sels, path, resolver, out, false)
r.execSelections(ctx, sels, path, s, resolver, out, false)
return
}
@ -232,40 +269,7 @@ func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selectio
switch t := t.(type) {
case *common.List:
l := resolver.Len()
if selected.HasAsyncSel(sels) {
var wg sync.WaitGroup
wg.Add(l)
entryouts := make([]bytes.Buffer, l)
for i := 0; i < l; i++ {
go func(i int) {
defer wg.Done()
defer r.handlePanic(ctx)
r.execSelectionSet(ctx, sels, t.OfType, &pathSegment{path, i}, resolver.Index(i), &entryouts[i])
}(i)
}
wg.Wait()
out.WriteByte('[')
for i, entryout := range entryouts {
if i > 0 {
out.WriteByte(',')
}
out.Write(entryout.Bytes())
}
out.WriteByte(']')
return
}
out.WriteByte('[')
for i := 0; i < l; i++ {
if i > 0 {
out.WriteByte(',')
}
r.execSelectionSet(ctx, sels, t.OfType, &pathSegment{path, i}, resolver.Index(i), out)
}
out.WriteByte(']')
r.execList(ctx, sels, t, path, s, resolver, out)
case *schema.Scalar:
v := resolver.Interface()
@ -276,8 +280,27 @@ func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selectio
out.Write(data)
case *schema.Enum:
var stringer fmt.Stringer = resolver
if s, ok := resolver.Interface().(fmt.Stringer); ok {
stringer = s
}
name := stringer.String()
var valid bool
for _, v := range t.Values {
if v.Name == name {
valid = true
break
}
}
if !valid {
err := errors.Errorf("Invalid value %s.\nExpected type %s, found %s.", name, t.Name, name)
err.Path = path.toSlice()
r.AddError(err)
out.WriteString("null")
return
}
out.WriteByte('"')
out.WriteString(resolver.String())
out.WriteString(name)
out.WriteByte('"')
default:
@ -285,6 +308,47 @@ func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selectio
}
}
func (r *Request) execList(ctx context.Context, sels []selected.Selection, typ *common.List, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) {
l := resolver.Len()
entryouts := make([]bytes.Buffer, l)
if selected.HasAsyncSel(sels) {
var wg sync.WaitGroup
wg.Add(l)
for i := 0; i < l; i++ {
go func(i int) {
defer wg.Done()
defer r.handlePanic(ctx)
r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i])
}(i)
}
wg.Wait()
} else {
for i := 0; i < l; i++ {
r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i])
}
}
_, listOfNonNull := typ.OfType.(*common.NonNull)
out.WriteByte('[')
for i, entryout := range entryouts {
// If the list wraps a non-null type and one of the list elements
// resolves to null, then the entire list resolves to null.
if listOfNonNull && resolvedToNull(&entryout) {
out.Reset()
out.WriteString("null")
return
}
if i > 0 {
out.WriteByte(',')
}
out.Write(entryout.Bytes())
}
out.WriteByte(']')
}
func unwrapNonNull(t common.Type) (common.Type, bool) {
if nn, ok := t.(*common.NonNull); ok {
return nn.OfType, true

View file

@ -9,21 +9,27 @@ import (
"github.com/graph-gophers/graphql-go/introspection"
)
var MetaSchema *Object
var MetaType *Object
// Meta defines the details of the metadata schema for introspection.
type Meta struct {
FieldSchema Field
FieldType Field
FieldTypename Field
Schema *Object
Type *Object
}
func init() {
func newMeta(s *schema.Schema) *Meta {
var err error
b := newBuilder(schema.Meta)
b := newBuilder(s)
metaSchema := schema.Meta.Types["__Schema"].(*schema.Object)
MetaSchema, err = b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, false, reflect.TypeOf(&introspection.Schema{}))
metaSchema := s.Types["__Schema"].(*schema.Object)
so, err := b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, false, reflect.TypeOf(&introspection.Schema{}))
if err != nil {
panic(err)
}
metaType := schema.Meta.Types["__Type"].(*schema.Object)
MetaType, err = b.makeObjectExec(metaType.Name, metaType.Fields, nil, false, reflect.TypeOf(&introspection.Type{}))
metaType := s.Types["__Type"].(*schema.Object)
t, err := b.makeObjectExec(metaType.Name, metaType.Fields, nil, false, reflect.TypeOf(&introspection.Type{}))
if err != nil {
panic(err)
}
@ -31,28 +37,36 @@ func init() {
if err := b.finish(); err != nil {
panic(err)
}
}
var MetaFieldTypename = Field{
fieldTypename := Field{
Field: schema.Field{
Name: "__typename",
Type: &common.NonNull{OfType: schema.Meta.Types["String"]},
Type: &common.NonNull{OfType: s.Types["String"]},
},
TraceLabel: fmt.Sprintf("GraphQL field: __typename"),
}
}
var MetaFieldSchema = Field{
fieldSchema := Field{
Field: schema.Field{
Name: "__schema",
Type: schema.Meta.Types["__Schema"],
Type: s.Types["__Schema"],
},
TraceLabel: fmt.Sprintf("GraphQL field: __schema"),
}
}
var MetaFieldType = Field{
fieldType := Field{
Field: schema.Field{
Name: "__type",
Type: schema.Meta.Types["__Type"],
Type: s.Types["__Type"],
},
TraceLabel: fmt.Sprintf("GraphQL field: __type"),
}
return &Meta{
FieldSchema: fieldSchema,
FieldTypename: fieldTypename,
FieldType: fieldType,
Schema: so,
Type: t,
}
}

View file

@ -12,9 +12,11 @@ import (
)
type Schema struct {
*Meta
schema.Schema
Query Resolvable
Mutation Resolvable
Subscription Resolvable
Resolver reflect.Value
}
@ -32,6 +34,7 @@ type Field struct {
schema.Field
TypeName string
MethodIndex int
FieldIndex int
HasContext bool
HasError bool
ArgsPacker *packer.StructPacker
@ -39,6 +42,10 @@ type Field struct {
TraceLabel string
}
func (f *Field) UseMethodResolver() bool {
return f.FieldIndex == -1
}
type TypeAssertion struct {
MethodIndex int
TypeExec Resolvable
@ -55,9 +62,13 @@ func (*List) isResolvable() {}
func (*Scalar) isResolvable() {}
func ApplyResolver(s *schema.Schema, resolver interface{}) (*Schema, error) {
if resolver == nil {
return &Schema{Meta: newMeta(s), Schema: *s}, nil
}
b := newBuilder(s)
var query, mutation Resolvable
var query, mutation, subscription Resolvable
if t, ok := s.EntryPoints["query"]; ok {
if err := b.assignExec(&query, t, reflect.TypeOf(resolver)); err != nil {
@ -71,15 +82,23 @@ func ApplyResolver(s *schema.Schema, resolver interface{}) (*Schema, error) {
}
}
if t, ok := s.EntryPoints["subscription"]; ok {
if err := b.assignExec(&subscription, t, reflect.TypeOf(resolver)); err != nil {
return nil, err
}
}
if err := b.finish(); err != nil {
return nil, err
}
return &Schema{
Meta: newMeta(s),
Schema: *s,
Resolver: reflect.ValueOf(resolver),
Query: query,
Mutation: mutation,
Subscription: subscription,
}, nil
}
@ -181,13 +200,13 @@ func makeScalarExec(t *schema.Scalar, resolverType reflect.Type) (Resolvable, er
implementsType := false
switch r := reflect.New(resolverType).Interface().(type) {
case *int32:
implementsType = (t.Name == "Int")
implementsType = t.Name == "Int"
case *float64:
implementsType = (t.Name == "Float")
implementsType = t.Name == "Float"
case *string:
implementsType = (t.Name == "String")
implementsType = t.Name == "String"
case *bool:
implementsType = (t.Name == "Boolean")
implementsType = t.Name == "Boolean"
case packer.Unmarshaler:
implementsType = r.ImplementsGraphQLType(t.Name)
}
@ -197,7 +216,8 @@ func makeScalarExec(t *schema.Scalar, resolverType reflect.Type) (Resolvable, er
return &Scalar{}, nil
}
func (b *execBuilder) makeObjectExec(typeName string, fields schema.FieldList, possibleTypes []*schema.Object, nonNull bool, resolverType reflect.Type) (*Object, error) {
func (b *execBuilder) makeObjectExec(typeName string, fields schema.FieldList, possibleTypes []*schema.Object,
nonNull bool, resolverType reflect.Type) (*Object, error) {
if !nonNull {
if resolverType.Kind() != reflect.Ptr && resolverType.Kind() != reflect.Interface {
return nil, fmt.Errorf("%s is not a pointer or interface", resolverType)
@ -207,9 +227,14 @@ func (b *execBuilder) makeObjectExec(typeName string, fields schema.FieldList, p
methodHasReceiver := resolverType.Kind() != reflect.Interface
Fields := make(map[string]*Field)
rt := unwrapPtr(resolverType)
for _, f := range fields {
fieldIndex := -1
methodIndex := findMethod(resolverType, f.Name)
if methodIndex == -1 {
if b.schema.UseFieldResolvers && methodIndex == -1 {
fieldIndex = findField(rt, f.Name)
}
if methodIndex == -1 && fieldIndex == -1 {
hint := ""
if findMethod(reflect.PtrTo(resolverType), f.Name) != -1 {
hint = " (hint: the method exists on the pointer type)"
@ -217,15 +242,25 @@ func (b *execBuilder) makeObjectExec(typeName string, fields schema.FieldList, p
return nil, fmt.Errorf("%s does not resolve %q: missing method for field %q%s", resolverType, typeName, f.Name, hint)
}
m := resolverType.Method(methodIndex)
fe, err := b.makeFieldExec(typeName, f, m, methodIndex, methodHasReceiver)
var m reflect.Method
var sf reflect.StructField
if methodIndex != -1 {
m = resolverType.Method(methodIndex)
} else {
sf = rt.Field(fieldIndex)
}
fe, err := b.makeFieldExec(typeName, f, m, sf, methodIndex, fieldIndex, methodHasReceiver)
if err != nil {
return nil, fmt.Errorf("%s\n\treturned by (%s).%s", err, resolverType, m.Name)
}
Fields[f.Name] = fe
}
// Check type assertions when
// 1) using method resolvers
// 2) Or resolver is not an interface type
typeAssertions := make(map[string]*TypeAssertion)
if !b.schema.UseFieldResolvers || resolverType.Kind() != reflect.Interface {
for _, impl := range possibleTypes {
methodIndex := findMethod(resolverType, "To"+impl.Name)
if methodIndex == -1 {
@ -242,6 +277,7 @@ func (b *execBuilder) makeObjectExec(typeName string, fields schema.FieldList, p
}
typeAssertions[impl.Name] = a
}
}
return &Object{
Name: typeName,
@ -253,7 +289,15 @@ func (b *execBuilder) makeObjectExec(typeName string, fields schema.FieldList, p
var contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
var errorType = reflect.TypeOf((*error)(nil)).Elem()
func (b *execBuilder) makeFieldExec(typeName string, f *schema.Field, m reflect.Method, methodIndex int, methodHasReceiver bool) (*Field, error) {
func (b *execBuilder) makeFieldExec(typeName string, f *schema.Field, m reflect.Method, sf reflect.StructField,
methodIndex, fieldIndex int, methodHasReceiver bool) (*Field, error) {
var argsPacker *packer.StructPacker
var hasError bool
var hasContext bool
// Validate resolver method only when there is one
if methodIndex != -1 {
in := make([]reflect.Type, m.Type.NumIn())
for i := range in {
in[i] = m.Type.In(i)
@ -262,12 +306,11 @@ func (b *execBuilder) makeFieldExec(typeName string, f *schema.Field, m reflect.
in = in[1:] // first parameter is receiver
}
hasContext := len(in) > 0 && in[0] == contextType
hasContext = len(in) > 0 && in[0] == contextType
if hasContext {
in = in[1:]
}
var argsPacker *packer.StructPacker
if len(f.Args) > 0 {
if len(in) == 0 {
return nil, fmt.Errorf("must have parameter for field arguments")
@ -284,14 +327,20 @@ func (b *execBuilder) makeFieldExec(typeName string, f *schema.Field, m reflect.
return nil, fmt.Errorf("too many parameters")
}
if m.Type.NumOut() > 2 {
maxNumOfReturns := 2
if m.Type.NumOut() < maxNumOfReturns-1 {
return nil, fmt.Errorf("too few return values")
}
if m.Type.NumOut() > maxNumOfReturns {
return nil, fmt.Errorf("too many return values")
}
hasError := m.Type.NumOut() == 2
hasError = m.Type.NumOut() == maxNumOfReturns
if hasError {
if m.Type.Out(1) != errorType {
return nil, fmt.Errorf(`must have "error" as its second return value`)
if m.Type.Out(maxNumOfReturns-1) != errorType {
return nil, fmt.Errorf(`must have "error" as its last return value`)
}
}
}
@ -299,14 +348,26 @@ func (b *execBuilder) makeFieldExec(typeName string, f *schema.Field, m reflect.
Field: *f,
TypeName: typeName,
MethodIndex: methodIndex,
FieldIndex: fieldIndex,
HasContext: hasContext,
ArgsPacker: argsPacker,
HasError: hasError,
TraceLabel: fmt.Sprintf("GraphQL field: %s.%s", typeName, f.Name),
}
if err := b.assignExec(&fe.ValueExec, f.Type, m.Type.Out(0)); err != nil {
var out reflect.Type
if methodIndex != -1 {
out = m.Type.Out(0)
if typeName == "Subscription" && out.Kind() == reflect.Chan {
out = m.Type.Out(0).Elem()
}
} else {
out = sf.Type
}
if err := b.assignExec(&fe.ValueExec, f.Type, out); err != nil {
return nil, err
}
return fe, nil
}
@ -319,6 +380,15 @@ func findMethod(t reflect.Type, name string) int {
return -1
}
func findField(t reflect.Type, name string) int {
for i := 0; i < t.NumField(); i++ {
if strings.EqualFold(stripUnderscore(name), stripUnderscore(t.Field(i).Name)) {
return i
}
}
return -1
}
func unwrapNonNull(t common.Type) (common.Type, bool) {
if nn, ok := t.(*common.NonNull); ok {
return nn.OfType, true
@ -329,3 +399,10 @@ func unwrapNonNull(t common.Type) (common.Type, bool) {
func stripUnderscore(s string) string {
return strings.Replace(s, "_", "", -1)
}
func unwrapPtr(t reflect.Type) reflect.Type {
if t.Kind() == reflect.Ptr {
return t.Elem()
}
return t
}

View file

@ -20,6 +20,7 @@ type Request struct {
Vars map[string]interface{}
Mu sync.Mutex
Errs []*errors.QueryError
DisableIntrospection bool
}
func (r *Request) AddError(err *errors.QueryError) {
@ -35,8 +36,10 @@ func ApplyOperation(r *Request, s *resolvable.Schema, op *query.Operation) []Sel
obj = s.Query.(*resolvable.Object)
case query.Mutation:
obj = s.Mutation.(*resolvable.Object)
case query.Subscription:
obj = s.Subscription.(*resolvable.Object)
}
return applySelectionSet(r, obj, op.Selections)
return applySelectionSet(r, s, obj, op.Selections)
}
type Selection interface {
@ -67,7 +70,7 @@ func (*SchemaField) isSelection() {}
func (*TypeAssertion) isSelection() {}
func (*TypenameField) isSelection() {}
func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection) (flattenedSels []Selection) {
func applySelectionSet(r *Request, s *resolvable.Schema, e *resolvable.Object, sels []query.Selection) (flattenedSels []Selection) {
for _, sel := range sels {
switch sel := sel.(type) {
case *query.Field:
@ -78,21 +81,26 @@ func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection)
switch field.Name.Name {
case "__typename":
if !r.DisableIntrospection {
flattenedSels = append(flattenedSels, &TypenameField{
Object: *e,
Alias: field.Alias.Name,
})
}
case "__schema":
if !r.DisableIntrospection {
flattenedSels = append(flattenedSels, &SchemaField{
Field: resolvable.MetaFieldSchema,
Field: s.Meta.FieldSchema,
Alias: field.Alias.Name,
Sels: applySelectionSet(r, resolvable.MetaSchema, field.Selections),
Sels: applySelectionSet(r, s, s.Meta.Schema, field.Selections),
Async: true,
FixedResult: reflect.ValueOf(introspection.WrapSchema(r.Schema)),
})
}
case "__type":
if !r.DisableIntrospection {
p := packer.ValuePacker{ValueType: reflect.TypeOf("")}
v, err := p.Pack(field.Arguments.MustGet("name").Value(r.Vars))
if err != nil {
@ -106,12 +114,13 @@ func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection)
}
flattenedSels = append(flattenedSels, &SchemaField{
Field: resolvable.MetaFieldType,
Field: s.Meta.FieldType,
Alias: field.Alias.Name,
Sels: applySelectionSet(r, resolvable.MetaType, field.Selections),
Sels: applySelectionSet(r, s, s.Meta.Type, field.Selections),
Async: true,
FixedResult: reflect.ValueOf(introspection.WrapType(t)),
})
}
default:
fe := e.Fields[field.Name.Name]
@ -131,7 +140,7 @@ func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection)
}
}
fieldSels := applyField(r, fe.ValueExec, field.Selections)
fieldSels := applyField(r, s, fe.ValueExec, field.Selections)
flattenedSels = append(flattenedSels, &SchemaField{
Field: *fe,
Alias: field.Alias.Name,
@ -147,14 +156,14 @@ func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection)
if skipByDirective(r, frag.Directives) {
continue
}
flattenedSels = append(flattenedSels, applyFragment(r, e, &frag.Fragment)...)
flattenedSels = append(flattenedSels, applyFragment(r, s, e, &frag.Fragment)...)
case *query.FragmentSpread:
spread := sel
if skipByDirective(r, spread.Directives) {
continue
}
flattenedSels = append(flattenedSels, applyFragment(r, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...)
flattenedSels = append(flattenedSels, applyFragment(r, s, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...)
default:
panic("invalid type")
@ -163,7 +172,7 @@ func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection)
return
}
func applyFragment(r *Request, e *resolvable.Object, frag *query.Fragment) []Selection {
func applyFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag *query.Fragment) []Selection {
if frag.On.Name != "" && frag.On.Name != e.Name {
a, ok := e.TypeAssertions[frag.On.Name]
if !ok {
@ -172,18 +181,18 @@ func applyFragment(r *Request, e *resolvable.Object, frag *query.Fragment) []Sel
return []Selection{&TypeAssertion{
TypeAssertion: *a,
Sels: applySelectionSet(r, a.TypeExec.(*resolvable.Object), frag.Selections),
Sels: applySelectionSet(r, s, a.TypeExec.(*resolvable.Object), frag.Selections),
}}
}
return applySelectionSet(r, e, frag.Selections)
return applySelectionSet(r, s, e, frag.Selections)
}
func applyField(r *Request, e resolvable.Resolvable, sels []query.Selection) []Selection {
func applyField(r *Request, s *resolvable.Schema, e resolvable.Resolvable, sels []query.Selection) []Selection {
switch e := e.(type) {
case *resolvable.Object:
return applySelectionSet(r, e, sels)
return applySelectionSet(r, s, e, sels)
case *resolvable.List:
return applyField(r, e.Elem, sels)
return applyField(r, s, e.Elem, sels)
case *resolvable.Scalar:
return nil
default:

View file

@ -94,7 +94,7 @@ func (InlineFragment) isSelection() {}
func (FragmentSpread) isSelection() {}
func Parse(queryString string) (*Document, *errors.QueryError) {
l := common.NewLexer(queryString)
l := common.NewLexer(queryString, false)
var doc *Document
err := l.CatchSyntaxError(func() { doc = parseDocument(l) })
@ -107,7 +107,7 @@ func Parse(queryString string) (*Document, *errors.QueryError) {
func parseDocument(l *common.Lexer) *Document {
d := &Document{}
l.Consume()
l.ConsumeWhitespace()
for l.Peek() != scanner.EOF {
if l.Peek() == '{' {
op := &Operation{Type: Query, Loc: l.Location()}

View file

@ -1,13 +1,20 @@
package schema
var Meta *Schema
func init() {
Meta = &Schema{} // bootstrap
Meta = New()
if err := Meta.Parse(metaSrc); err != nil {
_ = newMeta()
}
// newMeta initializes an instance of the meta Schema.
func newMeta() *Schema {
s := &Schema{
entryPointNames: make(map[string]string),
Types: make(map[string]NamedType),
Directives: make(map[string]*DirectiveDecl),
}
if err := s.Parse(metaSrc, false); err != nil {
panic(err)
}
return s
}
var metaSrc = `

View file

@ -41,6 +41,8 @@ type Schema struct {
// http://facebook.github.io/graphql/draft/#sec-Type-System.Directives
Directives map[string]*DirectiveDecl
UseFieldResolvers bool
entryPointNames map[string]string
objects []*Object
unions []*Union
@ -236,18 +238,19 @@ func New() *Schema {
Types: make(map[string]NamedType),
Directives: make(map[string]*DirectiveDecl),
}
for n, t := range Meta.Types {
m := newMeta()
for n, t := range m.Types {
s.Types[n] = t
}
for n, d := range Meta.Directives {
for n, d := range m.Directives {
s.Directives[n] = d
}
return s
}
// Parse the schema string.
func (s *Schema) Parse(schemaString string) error {
l := common.NewLexer(schemaString)
func (s *Schema) Parse(schemaString string, useStringDescriptions bool) error {
l := common.NewLexer(schemaString, useStringDescriptions)
err := l.CatchSyntaxError(func() { parseSchema(s, l) })
if err != nil {
@ -291,6 +294,11 @@ func (s *Schema) Parse(schemaString string) error {
if !ok {
return errors.Errorf("type %q is not an interface", intfName)
}
for _, f := range intf.Fields.Names() {
if obj.Fields.Get(f) == nil {
return errors.Errorf("interface %q expects field %q but %q does not provide it", intfName, f, obj.Name)
}
}
obj.Interfaces[i] = intf
intf.PossibleTypes = append(intf.PossibleTypes, obj)
}
@ -389,7 +397,7 @@ func resolveInputObject(s *Schema, values common.InputValueList) error {
}
func parseSchema(s *Schema, l *common.Lexer) {
l.Consume()
l.ConsumeWhitespace()
for l.Peek() != scanner.EOF {
desc := l.DescComment()

View file

@ -63,7 +63,7 @@ func newContext(s *schema.Schema, doc *query.Document, maxDepth int) *context {
}
}
func Validate(s *schema.Schema, doc *query.Document, maxDepth int) []*errors.QueryError {
func Validate(s *schema.Schema, doc *query.Document, variables map[string]interface{}, maxDepth int) []*errors.QueryError {
c := newContext(s, doc, maxDepth)
opNames := make(nameSet)
@ -95,6 +95,7 @@ func Validate(s *schema.Schema, doc *query.Document, maxDepth int) []*errors.Que
if !canBeInput(t) {
c.addErr(v.TypeLoc, "VariablesAreInputTypes", "Variable %q cannot be non-input type %q.", "$"+v.Name.Name, t)
}
validateValue(opc, v, variables[v.Name.Name], t)
if v.Default != nil {
validateLiteral(opc, v.Default)
@ -178,6 +179,58 @@ func Validate(s *schema.Schema, doc *query.Document, maxDepth int) []*errors.Que
return c.errs
}
func validateValue(c *opContext, v *common.InputValue, val interface{}, t common.Type) {
switch t := t.(type) {
case *common.NonNull:
if val == nil {
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid value null.\nExpected type \"%s\", found null.", v.Name.Name, t)
return
}
validateValue(c, v, val, t.OfType)
case *common.List:
if val == nil {
return
}
vv, ok := val.([]interface{})
if !ok {
// Input coercion rules allow single items without wrapping array
validateValue(c, v, val, t.OfType)
return
}
for _, elem := range vv {
validateValue(c, v, elem, t.OfType)
}
case *schema.Enum:
if val == nil {
return
}
e, ok := val.(string)
if !ok {
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid type %T.\nExpected type \"%s\", found %v.", v.Name.Name, val, t, val)
return
}
for _, option := range t.Values {
if option.Name == e {
return
}
}
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid value %s.\nExpected type \"%s\", found %s.", v.Name.Name, e, t, e)
case *schema.InputObject:
if val == nil {
return
}
in, ok := val.(map[string]interface{})
if !ok {
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid type %T.\nExpected type \"%s\", found %s.", v.Name.Name, val, t, val)
return
}
for _, f := range t.Values {
fieldVal := in[f.Name.Name]
validateValue(c, f, fieldVal, f.Type)
}
}
}
// validates the query doesn't go deeper than maxDepth (if set). Returns whether
// or not query validated max depth to avoid excessive recursion.
func validateMaxDepth(c *opContext, sels []query.Selection, depth int) bool {
@ -686,6 +739,7 @@ func validateLiteral(c *opContext, l common.Literal) {
})
continue
}
validateValueType(c, l, resolveType(c.context, v.Type))
c.usedVars[op][v] = struct{}{}
}
}

View file

@ -16,6 +16,7 @@ func (s *Schema) Inspect() *introspection.Schema {
// ToJSON encodes the schema in a JSON format used by tools like Relay.
func (s *Schema) ToJSON() ([]byte, error) {
result := s.exec(context.Background(), introspectionQuery, "", nil, &resolvable.Schema{
Meta: s.res.Meta,
Query: &resolvable.Object{},
Schema: *s.schema,
})

View file

@ -30,9 +30,9 @@ type TwoQueueCache struct {
size int
recentSize int
recent *simplelru.LRU
frequent *simplelru.LRU
recentEvict *simplelru.LRU
recent simplelru.LRUCache
frequent simplelru.LRUCache
recentEvict simplelru.LRUCache
lock sync.RWMutex
}
@ -84,7 +84,8 @@ func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCa
return c, nil
}
func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
// Get looks up a key's value from the cache.
func (c *TwoQueueCache) Get(key interface{}) (value interface{}, ok bool) {
c.lock.Lock()
defer c.lock.Unlock()
@ -105,6 +106,7 @@ func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
return nil, false
}
// Add adds a value to the cache.
func (c *TwoQueueCache) Add(key, value interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
@ -160,12 +162,15 @@ func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
c.frequent.RemoveOldest()
}
// Len returns the number of items in the cache.
func (c *TwoQueueCache) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return c.recent.Len() + c.frequent.Len()
}
// Keys returns a slice of the keys in the cache.
// The frequently used keys are first in the returned slice.
func (c *TwoQueueCache) Keys() []interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
@ -174,6 +179,7 @@ func (c *TwoQueueCache) Keys() []interface{} {
return append(k1, k2...)
}
// Remove removes the provided key from the cache.
func (c *TwoQueueCache) Remove(key interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
@ -188,6 +194,7 @@ func (c *TwoQueueCache) Remove(key interface{}) {
}
}
// Purge is used to completely clear the cache.
func (c *TwoQueueCache) Purge() {
c.lock.Lock()
defer c.lock.Unlock()
@ -196,13 +203,17 @@ func (c *TwoQueueCache) Purge() {
c.recentEvict.Purge()
}
// Contains is used to check if the cache contains a key
// without updating recency or frequency.
func (c *TwoQueueCache) Contains(key interface{}) bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.frequent.Contains(key) || c.recent.Contains(key)
}
func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) {
// Peek is used to inspect the cache value of a key
// without updating recency or frequency.
func (c *TwoQueueCache) Peek(key interface{}) (value interface{}, ok bool) {
c.lock.RLock()
defer c.lock.RUnlock()
if val, ok := c.frequent.Peek(key); ok {

View file

@ -18,11 +18,11 @@ type ARCCache struct {
size int // Size is the total capacity of the cache
p int // P is the dynamic preference towards T1 or T2
t1 *simplelru.LRU // T1 is the LRU for recently accessed items
b1 *simplelru.LRU // B1 is the LRU for evictions from t1
t1 simplelru.LRUCache // T1 is the LRU for recently accessed items
b1 simplelru.LRUCache // B1 is the LRU for evictions from t1
t2 *simplelru.LRU // T2 is the LRU for frequently accessed items
b2 *simplelru.LRU // B2 is the LRU for evictions from t2
t2 simplelru.LRUCache // T2 is the LRU for frequently accessed items
b2 simplelru.LRUCache // B2 is the LRU for evictions from t2
lock sync.RWMutex
}
@ -60,11 +60,11 @@ func NewARC(size int) (*ARCCache, error) {
}
// Get looks up a key's value from the cache.
func (c *ARCCache) Get(key interface{}) (interface{}, bool) {
func (c *ARCCache) Get(key interface{}) (value interface{}, ok bool) {
c.lock.Lock()
defer c.lock.Unlock()
// Ff the value is contained in T1 (recent), then
// If the value is contained in T1 (recent), then
// promote it to T2 (frequent)
if val, ok := c.t1.Peek(key); ok {
c.t1.Remove(key)
@ -153,7 +153,7 @@ func (c *ARCCache) Add(key, value interface{}) {
// Remove from B2
c.b2.Remove(key)
// Add the key to the frequntly used list
// Add the key to the frequently used list
c.t2.Add(key, value)
return
}
@ -247,7 +247,7 @@ func (c *ARCCache) Contains(key interface{}) bool {
// Peek is used to inspect the cache value of a key
// without updating recency or frequency.
func (c *ARCCache) Peek(key interface{}) (interface{}, bool) {
func (c *ARCCache) Peek(key interface{}) (value interface{}, ok bool) {
c.lock.RLock()
defer c.lock.RUnlock()
if val, ok := c.t1.Peek(key); ok {

View file

@ -1,6 +1,3 @@
// This package provides a simple LRU cache. It is based on the
// LRU implementation in groupcache:
// https://github.com/golang/groupcache/tree/master/lru
package lru
import (
@ -11,11 +8,11 @@ import (
// Cache is a thread-safe fixed size LRU cache.
type Cache struct {
lru *simplelru.LRU
lru simplelru.LRUCache
lock sync.RWMutex
}
// New creates an LRU of the given size
// New creates an LRU of the given size.
func New(size int) (*Cache, error) {
return NewWithEvict(size, nil)
}
@ -33,7 +30,7 @@ func NewWithEvict(size int, onEvicted func(key interface{}, value interface{}))
return c, nil
}
// Purge is used to completely clear the cache
// Purge is used to completely clear the cache.
func (c *Cache) Purge() {
c.lock.Lock()
c.lru.Purge()
@ -41,48 +38,51 @@ func (c *Cache) Purge() {
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *Cache) Add(key, value interface{}) bool {
func (c *Cache) Add(key, value interface{}) (evicted bool) {
c.lock.Lock()
defer c.lock.Unlock()
return c.lru.Add(key, value)
evicted = c.lru.Add(key, value)
c.lock.Unlock()
return evicted
}
// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (interface{}, bool) {
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
c.lock.Lock()
defer c.lock.Unlock()
return c.lru.Get(key)
value, ok = c.lru.Get(key)
c.lock.Unlock()
return value, ok
}
// Check if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
// Contains checks if a key is in the cache, without updating the
// recent-ness or deleting it for being stale.
func (c *Cache) Contains(key interface{}) bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Contains(key)
containKey := c.lru.Contains(key)
c.lock.RUnlock()
return containKey
}
// Returns the key value (or undefined if not found) without updating
// Peek returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *Cache) Peek(key interface{}) (interface{}, bool) {
func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Peek(key)
value, ok = c.lru.Peek(key)
c.lock.RUnlock()
return value, ok
}
// ContainsOrAdd checks if a key is in the cache without updating the
// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) {
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
c.lock.Lock()
defer c.lock.Unlock()
if c.lru.Contains(key) {
return true, false
} else {
evict := c.lru.Add(key, value)
return false, evict
}
evicted = c.lru.Add(key, value)
return false, evicted
}
// Remove removes the provided key from the cache.
@ -102,13 +102,15 @@ func (c *Cache) RemoveOldest() {
// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *Cache) Keys() []interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Keys()
keys := c.lru.Keys()
c.lock.RUnlock()
return keys
}
// Len returns the number of items in the cache.
func (c *Cache) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Len()
length := c.lru.Len()
c.lock.RUnlock()
return length
}

View file

@ -36,7 +36,7 @@ func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
return c, nil
}
// Purge is used to completely clear the cache
// Purge is used to completely clear the cache.
func (c *LRU) Purge() {
for k, v := range c.items {
if c.onEvict != nil {
@ -48,7 +48,7 @@ func (c *LRU) Purge() {
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *LRU) Add(key, value interface{}) bool {
func (c *LRU) Add(key, value interface{}) (evicted bool) {
// Check for existing item
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
@ -73,22 +73,26 @@ func (c *LRU) Add(key, value interface{}) bool {
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
if ent.Value.(*entry) == nil {
return nil, false
}
return ent.Value.(*entry).value, true
}
return
}
// Check if a key is in the cache, without updating the recent-ness
// Contains checks if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func (c *LRU) Contains(key interface{}) (ok bool) {
_, ok = c.items[key]
return ok
}
// Returns the key value (or undefined if not found) without updating
// Peek returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
if ent, ok := c.items[key]; ok {
var ent *list.Element
if ent, ok = c.items[key]; ok {
return ent.Value.(*entry).value, true
}
return nil, ok
@ -96,7 +100,7 @@ func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
// Remove removes the provided key from the cache, returning if the
// key was contained.
func (c *LRU) Remove(key interface{}) bool {
func (c *LRU) Remove(key interface{}) (present bool) {
if ent, ok := c.items[key]; ok {
c.removeElement(ent)
return true
@ -105,7 +109,7 @@ func (c *LRU) Remove(key interface{}) bool {
}
// RemoveOldest removes the oldest item from the cache.
func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) {
ent := c.evictList.Back()
if ent != nil {
c.removeElement(ent)
@ -116,7 +120,7 @@ func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
}
// GetOldest returns the oldest entry
func (c *LRU) GetOldest() (interface{}, interface{}, bool) {
func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) {
ent := c.evictList.Back()
if ent != nil {
kv := ent.Value.(*entry)

View file

@ -1,4 +1,4 @@
Copyright (c) 2013, John Beisley <greatred@gmail.com>
Copyright (c) 2013, John Beisley <johnbeisleyuk@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,

View file

@ -25,15 +25,19 @@ Core components:
Regenerating dcps generated source code:
----------------------------------------
1. Install gotasks: `go get -u github.com/jingweno/gotask`
2. Change to the gotasks directory: `cd gotasks`
3. Run specgen task: `gotask specgen`
1. Build code generator:
`go get -u github.com/huin/goupnp/cmd/goupnpdcpgen`
2. Regenerate the code:
`go generate ./...`
Supporting additional UPnP devices and services:
------------------------------------------------
Supporting additional services is, in the trivial case, simply a matter of
adding the service to the `dcpMetadata` whitelist in `gotasks/specgen_task.go`,
adding the service to the `dcpMetadata` whitelist in `cmd/goupnpdcpgen/metadata.go`,
regenerating the source code (see above), and committing that source code.
However, it would be helpful if anyone needing such a service could test the

View file

@ -5,7 +5,9 @@
// Typically, use one of the New* functions to create clients for services.
package internetgateway1
// Generated file - do not edit by hand. See README.md
// ***********************************************************
// GENERATED FILE - DO NOT EDIT BY HAND. See README.md
// ***********************************************************
import (
"net/url"
@ -388,7 +390,6 @@ func (client *LANHostConfigManagement1) SetAddressRange(NewMinAddress string, Ne
// Request structure.
request := &struct {
NewMinAddress string
NewMaxAddress string
}{}
// BEGIN Marshal arguments into request.
@ -425,7 +426,6 @@ func (client *LANHostConfigManagement1) GetAddressRange() (NewMinAddress string,
// Response structure.
response := &struct {
NewMinAddress string
NewMaxAddress string
}{}
@ -790,7 +790,6 @@ func (client *WANCableLinkConfig1) GetCableLinkConfigInfo() (NewCableLinkConfigS
// Response structure.
response := &struct {
NewCableLinkConfigState string
NewLinkType string
}{}
@ -1181,11 +1180,8 @@ func (client *WANCommonInterfaceConfig1) GetCommonLinkProperties() (NewWANAccess
// Response structure.
response := &struct {
NewWANAccessType string
NewLayer1UpstreamMaxBitRate string
NewLayer1DownstreamMaxBitRate string
NewPhysicalLinkStatus string
}{}
@ -1268,7 +1264,7 @@ func (client *WANCommonInterfaceConfig1) GetMaximumActiveConnections() (NewMaxim
return
}
func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint32, err error) {
func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint64, err error) {
// Request structure.
request := interface{}(nil)
// BEGIN Marshal arguments into request.
@ -1287,14 +1283,14 @@ func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent
// BEGIN Unmarshal arguments from response.
if NewTotalBytesSent, err = soap.UnmarshalUi4(response.NewTotalBytesSent); err != nil {
if NewTotalBytesSent, err = soap.UnmarshalUi8(response.NewTotalBytesSent); err != nil {
return
}
// END Unmarshal arguments from response.
return
}
func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint32, err error) {
func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint64, err error) {
// Request structure.
request := interface{}(nil)
// BEGIN Marshal arguments into request.
@ -1313,7 +1309,7 @@ func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesR
// BEGIN Unmarshal arguments from response.
if NewTotalBytesReceived, err = soap.UnmarshalUi4(response.NewTotalBytesReceived); err != nil {
if NewTotalBytesReceived, err = soap.UnmarshalUi8(response.NewTotalBytesReceived); err != nil {
return
}
// END Unmarshal arguments from response.
@ -1387,7 +1383,6 @@ func (client *WANCommonInterfaceConfig1) GetActiveConnection(NewActiveConnection
// Response structure.
response := &struct {
NewActiveConnDeviceContainer string
NewActiveConnectionServiceID string
}{}
@ -1508,7 +1503,6 @@ func (client *WANDSLLinkConfig1) GetDSLLinkInfo() (NewLinkType string, NewLinkSt
// Response structure.
response := &struct {
NewLinkType string
NewLinkStatus string
}{}
@ -1927,7 +1921,6 @@ func (client *WANIPConnection1) GetConnectionTypeInfo() (NewConnectionType strin
// Response structure.
response := &struct {
NewConnectionType string
NewPossibleConnectionTypes string
}{}
@ -2105,9 +2098,7 @@ func (client *WANIPConnection1) GetStatusInfo() (NewConnectionStatus string, New
// Response structure.
response := &struct {
NewConnectionStatus string
NewLastConnectionError string
NewUptime string
}{}
@ -2219,7 +2210,6 @@ func (client *WANIPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNA
// Response structure.
response := &struct {
NewRSIPAvailable string
NewNATEnabled string
}{}
@ -2259,19 +2249,12 @@ func (client *WANIPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex u
// Response structure.
response := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -2319,9 +2302,7 @@ func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -2340,13 +2321,9 @@ func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string
// Response structure.
response := &struct {
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -2385,19 +2362,12 @@ func (client *WANIPConnection1) AddPortMapping(NewRemoteHost string, NewExternal
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
// BEGIN Marshal arguments into request.
@ -2451,9 +2421,7 @@ func (client *WANIPConnection1) DeletePortMapping(NewRemoteHost string, NewExter
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -2578,9 +2546,7 @@ func (client *WANPOTSLinkConfig1) SetISPInfo(NewISPPhoneNumber string, NewISPInf
// Request structure.
request := &struct {
NewISPPhoneNumber string
NewISPInfo string
NewLinkType string
}{}
// BEGIN Marshal arguments into request.
@ -2614,7 +2580,6 @@ func (client *WANPOTSLinkConfig1) SetCallRetryInfo(NewNumberOfRetries uint32, Ne
// Request structure.
request := &struct {
NewNumberOfRetries string
NewDelayBetweenRetries string
}{}
// BEGIN Marshal arguments into request.
@ -2655,9 +2620,7 @@ func (client *WANPOTSLinkConfig1) GetISPInfo() (NewISPPhoneNumber string, NewISP
// Response structure.
response := &struct {
NewISPPhoneNumber string
NewISPInfo string
NewLinkType string
}{}
@ -2691,7 +2654,6 @@ func (client *WANPOTSLinkConfig1) GetCallRetryInfo() (NewNumberOfRetries uint32,
// Response structure.
response := &struct {
NewNumberOfRetries string
NewDelayBetweenRetries string
}{}
@ -2942,7 +2904,6 @@ func (client *WANPPPConnection1) GetConnectionTypeInfo() (NewConnectionType stri
// Response structure.
response := &struct {
NewConnectionType string
NewPossibleConnectionTypes string
}{}
@ -2967,7 +2928,6 @@ func (client *WANPPPConnection1) ConfigureConnection(NewUserName string, NewPass
// Request structure.
request := &struct {
NewUserName string
NewPassword string
}{}
// BEGIN Marshal arguments into request.
@ -3151,9 +3111,7 @@ func (client *WANPPPConnection1) GetStatusInfo() (NewConnectionStatus string, Ne
// Response structure.
response := &struct {
NewConnectionStatus string
NewLastConnectionError string
NewUptime string
}{}
@ -3187,7 +3145,6 @@ func (client *WANPPPConnection1) GetLinkLayerMaxBitRates() (NewUpstreamMaxBitRat
// Response structure.
response := &struct {
NewUpstreamMaxBitRate string
NewDownstreamMaxBitRate string
}{}
@ -3426,7 +3383,6 @@ func (client *WANPPPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewN
// Response structure.
response := &struct {
NewRSIPAvailable string
NewNATEnabled string
}{}
@ -3466,19 +3422,12 @@ func (client *WANPPPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex
// Response structure.
response := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -3526,9 +3475,7 @@ func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost strin
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -3547,13 +3494,9 @@ func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost strin
// Response structure.
response := &struct {
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -3592,19 +3535,12 @@ func (client *WANPPPConnection1) AddPortMapping(NewRemoteHost string, NewExterna
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
// BEGIN Marshal arguments into request.
@ -3658,9 +3594,7 @@ func (client *WANPPPConnection1) DeletePortMapping(NewRemoteHost string, NewExte
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.

View file

@ -5,7 +5,9 @@
// Typically, use one of the New* functions to create clients for services.
package internetgateway2
// Generated file - do not edit by hand. See README.md
// ***********************************************************
// GENERATED FILE - DO NOT EDIT BY HAND. See README.md
// ***********************************************************
import (
"net/url"
@ -107,7 +109,6 @@ func (client *DeviceProtection1) SendSetupMessage(ProtocolType string, InMessage
// Request structure.
request := &struct {
ProtocolType string
InMessage string
}{}
// BEGIN Marshal arguments into request.
@ -195,9 +196,7 @@ func (client *DeviceProtection1) GetRolesForAction(DeviceUDN string, ServiceId s
// Request structure.
request := &struct {
DeviceUDN string
ServiceId string
ActionName string
}{}
// BEGIN Marshal arguments into request.
@ -216,7 +215,6 @@ func (client *DeviceProtection1) GetRolesForAction(DeviceUDN string, ServiceId s
// Response structure.
response := &struct {
RoleList string
RestrictedRoleList string
}{}
@ -241,7 +239,6 @@ func (client *DeviceProtection1) GetUserLoginChallenge(ProtocolType string, Name
// Request structure.
request := &struct {
ProtocolType string
Name string
}{}
// BEGIN Marshal arguments into request.
@ -257,7 +254,6 @@ func (client *DeviceProtection1) GetUserLoginChallenge(ProtocolType string, Name
// Response structure.
response := &struct {
Salt string
Challenge string
}{}
@ -282,9 +278,7 @@ func (client *DeviceProtection1) UserLogin(ProtocolType string, Challenge []byte
// Request structure.
request := &struct {
ProtocolType string
Challenge string
Authenticator string
}{}
// BEGIN Marshal arguments into request.
@ -422,11 +416,8 @@ func (client *DeviceProtection1) SetUserLoginPassword(ProtocolType string, Name
// Request structure.
request := &struct {
ProtocolType string
Name string
Stored string
Salt string
}{}
// BEGIN Marshal arguments into request.
@ -463,7 +454,6 @@ func (client *DeviceProtection1) AddRolesForIdentity(Identity string, RoleList s
// Request structure.
request := &struct {
Identity string
RoleList string
}{}
// BEGIN Marshal arguments into request.
@ -494,7 +484,6 @@ func (client *DeviceProtection1) RemoveRolesForIdentity(Identity string, RoleLis
// Request structure.
request := &struct {
Identity string
RoleList string
}{}
// BEGIN Marshal arguments into request.
@ -871,7 +860,6 @@ func (client *LANHostConfigManagement1) SetAddressRange(NewMinAddress string, Ne
// Request structure.
request := &struct {
NewMinAddress string
NewMaxAddress string
}{}
// BEGIN Marshal arguments into request.
@ -908,7 +896,6 @@ func (client *LANHostConfigManagement1) GetAddressRange() (NewMinAddress string,
// Response structure.
response := &struct {
NewMinAddress string
NewMaxAddress string
}{}
@ -1273,7 +1260,6 @@ func (client *WANCableLinkConfig1) GetCableLinkConfigInfo() (NewCableLinkConfigS
// Response structure.
response := &struct {
NewCableLinkConfigState string
NewLinkType string
}{}
@ -1664,11 +1650,8 @@ func (client *WANCommonInterfaceConfig1) GetCommonLinkProperties() (NewWANAccess
// Response structure.
response := &struct {
NewWANAccessType string
NewLayer1UpstreamMaxBitRate string
NewLayer1DownstreamMaxBitRate string
NewPhysicalLinkStatus string
}{}
@ -1751,7 +1734,7 @@ func (client *WANCommonInterfaceConfig1) GetMaximumActiveConnections() (NewMaxim
return
}
func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint32, err error) {
func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint64, err error) {
// Request structure.
request := interface{}(nil)
// BEGIN Marshal arguments into request.
@ -1770,14 +1753,14 @@ func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent
// BEGIN Unmarshal arguments from response.
if NewTotalBytesSent, err = soap.UnmarshalUi4(response.NewTotalBytesSent); err != nil {
if NewTotalBytesSent, err = soap.UnmarshalUi8(response.NewTotalBytesSent); err != nil {
return
}
// END Unmarshal arguments from response.
return
}
func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint32, err error) {
func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint64, err error) {
// Request structure.
request := interface{}(nil)
// BEGIN Marshal arguments into request.
@ -1796,7 +1779,7 @@ func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesR
// BEGIN Unmarshal arguments from response.
if NewTotalBytesReceived, err = soap.UnmarshalUi4(response.NewTotalBytesReceived); err != nil {
if NewTotalBytesReceived, err = soap.UnmarshalUi8(response.NewTotalBytesReceived); err != nil {
return
}
// END Unmarshal arguments from response.
@ -1870,7 +1853,6 @@ func (client *WANCommonInterfaceConfig1) GetActiveConnection(NewActiveConnection
// Response structure.
response := &struct {
NewActiveConnDeviceContainer string
NewActiveConnectionServiceID string
}{}
@ -1991,7 +1973,6 @@ func (client *WANDSLLinkConfig1) GetDSLLinkInfo() (NewLinkType string, NewLinkSt
// Response structure.
response := &struct {
NewLinkType string
NewLinkStatus string
}{}
@ -2410,7 +2391,6 @@ func (client *WANIPConnection1) GetConnectionTypeInfo() (NewConnectionType strin
// Response structure.
response := &struct {
NewConnectionType string
NewPossibleConnectionTypes string
}{}
@ -2588,9 +2568,7 @@ func (client *WANIPConnection1) GetStatusInfo() (NewConnectionStatus string, New
// Response structure.
response := &struct {
NewConnectionStatus string
NewLastConnectionError string
NewUptime string
}{}
@ -2702,7 +2680,6 @@ func (client *WANIPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNA
// Response structure.
response := &struct {
NewRSIPAvailable string
NewNATEnabled string
}{}
@ -2742,19 +2719,12 @@ func (client *WANIPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex u
// Response structure.
response := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -2802,9 +2772,7 @@ func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -2823,13 +2791,9 @@ func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string
// Response structure.
response := &struct {
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -2868,19 +2832,12 @@ func (client *WANIPConnection1) AddPortMapping(NewRemoteHost string, NewExternal
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
// BEGIN Marshal arguments into request.
@ -2934,9 +2891,7 @@ func (client *WANIPConnection1) DeletePortMapping(NewRemoteHost string, NewExter
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -3088,7 +3043,6 @@ func (client *WANIPConnection2) GetConnectionTypeInfo() (NewConnectionType strin
// Response structure.
response := &struct {
NewConnectionType string
NewPossibleConnectionTypes string
}{}
@ -3266,9 +3220,7 @@ func (client *WANIPConnection2) GetStatusInfo() (NewConnectionStatus string, New
// Response structure.
response := &struct {
NewConnectionStatus string
NewLastConnectionError string
NewUptime string
}{}
@ -3380,7 +3332,6 @@ func (client *WANIPConnection2) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNA
// Response structure.
response := &struct {
NewRSIPAvailable string
NewNATEnabled string
}{}
@ -3420,19 +3371,12 @@ func (client *WANIPConnection2) GetGenericPortMappingEntry(NewPortMappingIndex u
// Response structure.
response := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -3480,9 +3424,7 @@ func (client *WANIPConnection2) GetSpecificPortMappingEntry(NewRemoteHost string
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -3501,13 +3443,9 @@ func (client *WANIPConnection2) GetSpecificPortMappingEntry(NewRemoteHost string
// Response structure.
response := &struct {
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -3546,19 +3484,12 @@ func (client *WANIPConnection2) AddPortMapping(NewRemoteHost string, NewExternal
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
// BEGIN Marshal arguments into request.
@ -3612,9 +3543,7 @@ func (client *WANIPConnection2) DeletePortMapping(NewRemoteHost string, NewExter
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -3653,11 +3582,8 @@ func (client *WANIPConnection2) DeletePortMappingRange(NewStartPort uint16, NewE
// Request structure.
request := &struct {
NewStartPort string
NewEndPort string
NewProtocol string
NewManage string
}{}
// BEGIN Marshal arguments into request.
@ -3725,13 +3651,9 @@ func (client *WANIPConnection2) GetListOfPortMappings(NewStartPort uint16, NewEn
// Request structure.
request := &struct {
NewStartPort string
NewEndPort string
NewProtocol string
NewManage string
NewNumberOfPorts string
}{}
// BEGIN Marshal arguments into request.
@ -3781,19 +3703,12 @@ func (client *WANIPConnection2) AddAnyPortMapping(NewRemoteHost string, NewExter
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
// BEGIN Marshal arguments into request.
@ -3913,7 +3828,6 @@ func (client *WANIPv6FirewallControl1) GetFirewallStatus() (FirewallEnabled bool
// Response structure.
response := &struct {
FirewallEnabled string
InboundPinholeAllowed string
}{}
@ -3938,13 +3852,9 @@ func (client *WANIPv6FirewallControl1) GetOutboundPinholeTimeout(RemoteHost stri
// Request structure.
request := &struct {
RemoteHost string
RemotePort string
InternalClient string
InternalPort string
Protocol string
}{}
// BEGIN Marshal arguments into request.
@ -3994,15 +3904,10 @@ func (client *WANIPv6FirewallControl1) AddPinhole(RemoteHost string, RemotePort
// Request structure.
request := &struct {
RemoteHost string
RemotePort string
InternalClient string
InternalPort string
Protocol string
LeaseTime string
}{}
// BEGIN Marshal arguments into request.
@ -4055,7 +3960,6 @@ func (client *WANIPv6FirewallControl1) UpdatePinhole(UniqueID uint16, NewLeaseTi
// Request structure.
request := &struct {
UniqueID string
NewLeaseTime string
}{}
// BEGIN Marshal arguments into request.
@ -4239,9 +4143,7 @@ func (client *WANPOTSLinkConfig1) SetISPInfo(NewISPPhoneNumber string, NewISPInf
// Request structure.
request := &struct {
NewISPPhoneNumber string
NewISPInfo string
NewLinkType string
}{}
// BEGIN Marshal arguments into request.
@ -4275,7 +4177,6 @@ func (client *WANPOTSLinkConfig1) SetCallRetryInfo(NewNumberOfRetries uint32, Ne
// Request structure.
request := &struct {
NewNumberOfRetries string
NewDelayBetweenRetries string
}{}
// BEGIN Marshal arguments into request.
@ -4316,9 +4217,7 @@ func (client *WANPOTSLinkConfig1) GetISPInfo() (NewISPPhoneNumber string, NewISP
// Response structure.
response := &struct {
NewISPPhoneNumber string
NewISPInfo string
NewLinkType string
}{}
@ -4352,7 +4251,6 @@ func (client *WANPOTSLinkConfig1) GetCallRetryInfo() (NewNumberOfRetries uint32,
// Response structure.
response := &struct {
NewNumberOfRetries string
NewDelayBetweenRetries string
}{}
@ -4603,7 +4501,6 @@ func (client *WANPPPConnection1) GetConnectionTypeInfo() (NewConnectionType stri
// Response structure.
response := &struct {
NewConnectionType string
NewPossibleConnectionTypes string
}{}
@ -4628,7 +4525,6 @@ func (client *WANPPPConnection1) ConfigureConnection(NewUserName string, NewPass
// Request structure.
request := &struct {
NewUserName string
NewPassword string
}{}
// BEGIN Marshal arguments into request.
@ -4812,9 +4708,7 @@ func (client *WANPPPConnection1) GetStatusInfo() (NewConnectionStatus string, Ne
// Response structure.
response := &struct {
NewConnectionStatus string
NewLastConnectionError string
NewUptime string
}{}
@ -4848,7 +4742,6 @@ func (client *WANPPPConnection1) GetLinkLayerMaxBitRates() (NewUpstreamMaxBitRat
// Response structure.
response := &struct {
NewUpstreamMaxBitRate string
NewDownstreamMaxBitRate string
}{}
@ -5087,7 +4980,6 @@ func (client *WANPPPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewN
// Response structure.
response := &struct {
NewRSIPAvailable string
NewNATEnabled string
}{}
@ -5127,19 +5019,12 @@ func (client *WANPPPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex
// Response structure.
response := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -5187,9 +5072,7 @@ func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost strin
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.
@ -5208,13 +5091,9 @@ func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost strin
// Response structure.
response := &struct {
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
@ -5253,19 +5132,12 @@ func (client *WANPPPConnection1) AddPortMapping(NewRemoteHost string, NewExterna
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
NewInternalPort string
NewInternalClient string
NewEnabled string
NewPortMappingDescription string
NewLeaseDuration string
}{}
// BEGIN Marshal arguments into request.
@ -5319,9 +5191,7 @@ func (client *WANPPPConnection1) DeletePortMapping(NewRemoteHost string, NewExte
// Request structure.
request := &struct {
NewRemoteHost string
NewExternalPort string
NewProtocol string
}{}
// BEGIN Marshal arguments into request.

View file

@ -147,9 +147,9 @@ func (srv *Service) String() string {
return fmt.Sprintf("Service ID %s : %s", srv.ServiceId, srv.ServiceType)
}
// RequestSCDP requests the SCPD (soap actions and state variables description)
// RequestSCPD requests the SCPD (soap actions and state variables description)
// for the service.
func (srv *Service) RequestSCDP() (*scpd.SCPD, error) {
func (srv *Service) RequestSCPD() (*scpd.SCPD, error) {
if !srv.SCPDURL.Ok {
return nil, errors.New("bad/missing SCPD URL, or no URLBase has been set")
}
@ -160,6 +160,12 @@ func (srv *Service) RequestSCDP() (*scpd.SCPD, error) {
return s, nil
}
// RequestSCDP is for compatibility only, prefer RequestSCPD. This was a
// misspelling of RequestSCDP.
func (srv *Service) RequestSCDP() (*scpd.SCPD, error) {
return srv.RequestSCPD()
}
func (srv *Service) NewSOAPClient() *soap.SOAPClient {
return soap.NewSOAPClient(srv.ControlURL.URL)
}

View file

@ -122,11 +122,13 @@ func (httpu *HTTPUClient) Do(req *http.Request, timeout time.Duration, numSends
// Parse response.
response, err := http.ReadResponse(bufio.NewReader(bytes.NewBuffer(responseBytes[:n])), req)
if err != nil {
log.Print("httpu: error while parsing response: %v", err)
log.Printf("httpu: error while parsing response: %v", err)
continue
}
responses = append(responses, response)
}
return responses, err
// Timeout reached - return discovered responses.
return responses, nil
}

View file

@ -10,6 +10,7 @@ import (
"net/http"
"net/url"
"reflect"
"regexp"
)
const (
@ -126,14 +127,49 @@ func encodeRequestArgs(w *bytes.Buffer, inAction interface{}) error {
if value.Kind() != reflect.String {
return fmt.Errorf("goupnp: SOAP arg %q is not of type string, but of type %v", argName, value.Type())
}
if err := enc.EncodeElement(value.Interface(), xml.StartElement{xml.Name{"", argName}, nil}); err != nil {
return fmt.Errorf("goupnp: error encoding SOAP arg %q: %v", argName, err)
elem := xml.StartElement{xml.Name{"", argName}, nil}
if err := enc.EncodeToken(elem); err != nil {
return fmt.Errorf("goupnp: error encoding start element for SOAP arg %q: %v", argName, err)
}
if err := enc.Flush(); err != nil {
return fmt.Errorf("goupnp: error flushing start element for SOAP arg %q: %v", argName, err)
}
if _, err := w.Write([]byte(escapeXMLText(value.Interface().(string)))); err != nil {
return fmt.Errorf("goupnp: error writing value for SOAP arg %q: %v", argName, err)
}
if err := enc.EncodeToken(elem.End()); err != nil {
return fmt.Errorf("goupnp: error encoding end element for SOAP arg %q: %v", argName, err)
}
}
enc.Flush()
return nil
}
var xmlCharRx = regexp.MustCompile("[<>&]")
// escapeXMLText is used by generated code to escape text in XML, but only
// escaping the characters `<`, `>`, and `&`.
//
// This is provided in order to work around SOAP server implementations that
// fail to decode XML correctly, specifically failing to decode `"`, `'`. Note
// that this can only be safely used for injecting into XML text, but not into
// attributes or other contexts.
func escapeXMLText(s string) string {
return xmlCharRx.ReplaceAllStringFunc(s, replaceEntity)
}
func replaceEntity(s string) string {
switch s {
case "<":
return "&lt;"
case ">":
return "&gt;"
case "&":
return "&amp;"
}
return s
}
type soapEnvelope struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
EncodingStyle string `xml:"http://schemas.xmlsoap.org/soap/envelope/ encodingStyle,attr"`

View file

@ -47,6 +47,15 @@ func UnmarshalUi4(s string) (uint32, error) {
return uint32(v), err
}
func MarshalUi8(v uint64) (string, error) {
return strconv.FormatUint(v, 10), nil
}
func UnmarshalUi8(s string) (uint64, error) {
v, err := strconv.ParseUint(s, 10, 64)
return uint64(v), err
}
func MarshalI1(v int8) (string, error) {
return strconv.FormatInt(int64(v), 10), nil
}
@ -325,7 +334,7 @@ func UnmarshalTimeOfDay(s string) (TimeOfDay, error) {
if err != nil {
return TimeOfDay{}, err
} else if t.HasOffset {
return TimeOfDay{}, fmt.Errorf("soap time: value %q contains unexpected timezone")
return TimeOfDay{}, fmt.Errorf("soap time: value %q contains unexpected timezone", s)
}
return t, nil
}

View file

@ -20,6 +20,11 @@ const (
ssdpSearchPort = 1900
methodSearch = "M-SEARCH"
methodNotify = "NOTIFY"
// SSDPAll is a value for searchTarget that searches for all devices and services.
SSDPAll = "ssdp:all"
// UPNPRootDevice is a value for searchTarget that searches for all root devices.
UPNPRootDevice = "upnp:rootdevice"
)
// SSDPRawSearch performs a fairly raw SSDP search request, and returns the
@ -54,13 +59,15 @@ func SSDPRawSearch(httpu *httpu.HTTPUClient, searchTarget string, maxWaitSeconds
if err != nil {
return nil, err
}
isExactSearch := searchTarget != SSDPAll && searchTarget != UPNPRootDevice
for _, response := range allResponses {
if response.StatusCode != 200 {
log.Printf("ssdp: got response status code %q in search response", response.Status)
continue
}
if st := response.Header.Get("ST"); st != searchTarget {
log.Printf("ssdp: got unexpected search target result %q", st)
if st := response.Header.Get("ST"); isExactSearch && st != searchTarget {
continue
}
location, err := response.Location()

View file

@ -1,20 +1,21 @@
The MIT License (MIT)
MIT License
Copyright (c) 2013-2016 Errplane Inc.
Copyright (c) 2018 InfluxData
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,48 +0,0 @@
package models
import (
"errors"
"strings"
)
// ConsistencyLevel represent a required replication criteria before a write can
// be returned as successful.
//
// The consistency level is handled in open-source InfluxDB but only applicable to clusters.
type ConsistencyLevel int
const (
// ConsistencyLevelAny allows for hinted handoff, potentially no write happened yet.
ConsistencyLevelAny ConsistencyLevel = iota
// ConsistencyLevelOne requires at least one data node acknowledged a write.
ConsistencyLevelOne
// ConsistencyLevelQuorum requires a quorum of data nodes to acknowledge a write.
ConsistencyLevelQuorum
// ConsistencyLevelAll requires all data nodes to acknowledge a write.
ConsistencyLevelAll
)
var (
// ErrInvalidConsistencyLevel is returned when parsing the string version
// of a consistency level.
ErrInvalidConsistencyLevel = errors.New("invalid consistency level")
)
// ParseConsistencyLevel converts a consistency level string to the corresponding ConsistencyLevel const.
func ParseConsistencyLevel(level string) (ConsistencyLevel, error) {
switch strings.ToLower(level) {
case "any":
return ConsistencyLevelAny, nil
case "one":
return ConsistencyLevelOne, nil
case "quorum":
return ConsistencyLevelQuorum, nil
case "all":
return ConsistencyLevelAll, nil
default:
return 0, ErrInvalidConsistencyLevel
}
}

View file

@ -12,20 +12,39 @@ import (
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
"github.com/influxdata/influxdb/pkg/escape"
)
// Values used to store the field key and measurement name as special internal tags.
const (
FieldKeyTagKey = "\xff"
MeasurementTagKey = "\x00"
)
// Predefined byte representations of special tag keys.
var (
measurementEscapeCodes = map[byte][]byte{
',': []byte(`\,`),
' ': []byte(`\ `),
FieldKeyTagKeyBytes = []byte(FieldKeyTagKey)
MeasurementTagKeyBytes = []byte(MeasurementTagKey)
)
type escapeSet struct {
k [1]byte
esc [2]byte
}
var (
measurementEscapeCodes = [...]escapeSet{
{k: [1]byte{','}, esc: [2]byte{'\\', ','}},
{k: [1]byte{' '}, esc: [2]byte{'\\', ' '}},
}
tagEscapeCodes = map[byte][]byte{
',': []byte(`\,`),
' ': []byte(`\ `),
'=': []byte(`\=`),
tagEscapeCodes = [...]escapeSet{
{k: [1]byte{','}, esc: [2]byte{'\\', ','}},
{k: [1]byte{' '}, esc: [2]byte{'\\', ' '}},
{k: [1]byte{'='}, esc: [2]byte{'\\', '='}},
}
// ErrPointMustHaveAField is returned when operating on a point that does not have any fields.
@ -64,6 +83,9 @@ type Point interface {
// Tags returns the tag set for the point.
Tags() Tags
// ForEachTag iterates over each tag invoking fn. If fn return false, iteration stops.
ForEachTag(fn func(k, v []byte) bool)
// AddTag adds or replaces a tag value for a point.
AddTag(key, value string)
@ -124,7 +146,7 @@ type Point interface {
// the result, potentially reducing string allocations.
AppendString(buf []byte) []byte
// FieldIterator retuns a FieldIterator that can be used to traverse the
// FieldIterator returns a FieldIterator that can be used to traverse the
// fields of a point without constructing the in-memory map.
FieldIterator() FieldIterator
}
@ -152,6 +174,23 @@ const (
Unsigned
)
func (t FieldType) String() string {
switch t {
case Integer:
return "Integer"
case Float:
return "Float"
case Boolean:
return "Boolean"
case String:
return "String"
case Empty:
return "Empty"
default:
return "<unknown>"
}
}
// FieldIterator provides a low-allocation interface to iterate through a point's fields.
type FieldIterator interface {
// Next indicates whether there any fields remaining.
@ -249,13 +288,17 @@ const (
// ParsePoints returns a slice of Points from a text representation of a point
// with each point separated by newlines. If any points fail to parse, a non-nil error
// will be returned in addition to the points that parsed successfully.
func ParsePoints(buf []byte) ([]Point, error) {
return ParsePointsWithPrecision(buf, time.Now().UTC(), "n")
//
// The mm argument supplies the new measurement which is generated by calling
// EscapeMeasurement(EncodeName(orgID, bucketID)). The existing measurement is
// moved to the "_m" tag.
func ParsePoints(buf, mm []byte) ([]Point, error) {
return ParsePointsWithPrecision(buf, mm, time.Now().UTC(), "n")
}
// ParsePointsString is identical to ParsePoints but accepts a string.
func ParsePointsString(buf string) ([]Point, error) {
return ParsePoints([]byte(buf))
func ParsePointsString(buf, mm string) ([]Point, error) {
return ParsePoints([]byte(buf), []byte(mm))
}
// ParseKey returns the measurement name and tags from a point.
@ -263,36 +306,66 @@ func ParsePointsString(buf string) ([]Point, error) {
// NOTE: to minimize heap allocations, the returned Tags will refer to subslices of buf.
// This can have the unintended effect preventing buf from being garbage collected.
func ParseKey(buf []byte) (string, Tags) {
meas, tags := ParseKeyBytes(buf)
return string(meas), tags
name, tags := ParseKeyBytes(buf)
return string(name), tags
}
func ParseKeyBytes(buf []byte) ([]byte, Tags) {
return ParseKeyBytesWithTags(buf, nil)
}
func ParseKeyBytesWithTags(buf []byte, tags Tags) ([]byte, Tags) {
// Ignore the error because scanMeasurement returns "missing fields" which we ignore
// when just parsing a key
state, i, _ := scanMeasurement(buf, 0)
var tags Tags
var name []byte
if state == tagKeyState {
tags = parseTags(buf)
tags = parseTags(buf, tags)
// scanMeasurement returns the location of the comma if there are tags, strip that off
return buf[:i-1], tags
name = buf[:i-1]
} else {
name = buf[:i]
}
return buf[:i], tags
return UnescapeMeasurement(name), tags
}
func ParseTags(buf []byte) Tags {
return parseTags(buf)
return parseTags(buf, nil)
}
func ParseName(buf []byte) ([]byte, error) {
func ParseTagsWithTags(buf []byte, tags Tags) Tags {
return parseTags(buf, tags)
}
func ParseName(buf []byte) []byte {
// Ignore the error because scanMeasurement returns "missing fields" which we ignore
// when just parsing a key
state, i, _ := scanMeasurement(buf, 0)
var name []byte
if state == tagKeyState {
return buf[:i-1], nil
name = buf[:i-1]
} else {
name = buf[:i]
}
return buf[:i], nil
return UnescapeMeasurement(name)
}
// ValidPrecision checks if the precision is known.
func ValidPrecision(precision string) bool {
switch precision {
case "ns", "us", "ms", "s":
return true
default:
return false
}
}
// ParsePointsWithPrecisionV1 is similar to ParsePointsWithPrecision but does
// not rewrite the measurement & field keys.
func ParsePointsWithPrecisionV1(buf []byte, mm []byte, defaultTime time.Time, precision string) (_ []Point, err error) {
return parsePointsWithPrecision(buf, mm, defaultTime, precision, false)
}
// ParsePointsWithPrecision is similar to ParsePoints, but allows the
@ -300,7 +373,11 @@ func ParseName(buf []byte) ([]byte, error) {
//
// NOTE: to minimize heap allocations, the returned Points will refer to subslices of buf.
// This can have the unintended effect preventing buf from being garbage collected.
func ParsePointsWithPrecision(buf []byte, defaultTime time.Time, precision string) ([]Point, error) {
func ParsePointsWithPrecision(buf []byte, mm []byte, defaultTime time.Time, precision string) (_ []Point, err error) {
return parsePointsWithPrecision(buf, mm, defaultTime, precision, true)
}
func parsePointsWithPrecision(buf []byte, mm []byte, defaultTime time.Time, precision string, rewrite bool) (_ []Point, err error) {
points := make([]Point, 0, bytes.Count(buf, []byte{'\n'})+1)
var (
pos int
@ -332,22 +409,19 @@ func ParsePointsWithPrecision(buf []byte, defaultTime time.Time, precision strin
block = block[:len(block)-1]
}
pt, err := parsePoint(block[start:], defaultTime, precision)
points, err = parsePointsAppend(points, block[start:], mm, defaultTime, precision, rewrite)
if err != nil {
failed = append(failed, fmt.Sprintf("unable to parse '%s': %v", string(block[start:]), err))
} else {
points = append(points, pt)
}
}
if len(failed) > 0 {
return points, fmt.Errorf("%s", strings.Join(failed, "\n"))
}
return points, nil
return points, nil
}
func parsePoint(buf []byte, defaultTime time.Time, precision string) (Point, error) {
func parsePointsAppend(points []Point, buf []byte, mm []byte, defaultTime time.Time, precision string, rewrite bool) ([]Point, error) {
// scan the first block which is measurement[,tag1=value1,tag2=value=2...]
pos, key, err := scanKey(buf, 0)
if err != nil {
@ -356,48 +430,43 @@ func parsePoint(buf []byte, defaultTime time.Time, precision string) (Point, err
// measurement name is required
if len(key) == 0 {
return nil, fmt.Errorf("missing measurement")
return points, fmt.Errorf("missing measurement")
}
if len(key) > MaxKeyLength {
return nil, fmt.Errorf("max key length exceeded: %v > %v", len(key), MaxKeyLength)
return points, fmt.Errorf("max key length exceeded: %v > %v", len(key), MaxKeyLength)
}
// Since the measurement is converted to a tag and measurements & tags have
// different escaping rules, we need to check if the measurement needs escaping.
_, i, _ := scanMeasurement(key, 0)
keyMeasurement := key[:i-1]
if rewrite && bytes.IndexByte(keyMeasurement, '=') != -1 {
escapedKeyMeasurement := bytes.Replace(keyMeasurement, []byte("="), []byte(`\=`), -1)
newKey := make([]byte, len(escapedKeyMeasurement)+(len(key)-len(keyMeasurement)))
copy(newKey, escapedKeyMeasurement)
copy(newKey[len(escapedKeyMeasurement):], key[len(keyMeasurement):])
key = newKey
}
// scan the second block is which is field1=value1[,field2=value2,...]
// at least one field is required
pos, fields, err := scanFields(buf, pos)
if err != nil {
return nil, err
}
// at least one field is required
if len(fields) == 0 {
return nil, fmt.Errorf("missing fields")
}
var maxKeyErr error
walkFields(fields, func(k, v []byte) bool {
if sz := seriesKeySize(key, k); sz > MaxKeyLength {
maxKeyErr = fmt.Errorf("max key length exceeded: %v > %v", sz, MaxKeyLength)
return false
}
return true
})
if maxKeyErr != nil {
return nil, maxKeyErr
return points, err
} else if len(fields) == 0 {
return points, fmt.Errorf("missing fields")
}
// scan the last block which is an optional integer timestamp
pos, ts, err := scanTime(buf, pos)
if err != nil {
return nil, err
return points, err
}
pt := &point{
key: key,
fields: fields,
ts: ts,
}
// Build point with timestamp only.
pt := point{ts: ts}
if len(ts) == 0 {
pt.time = defaultTime
@ -405,39 +474,80 @@ func parsePoint(buf []byte, defaultTime time.Time, precision string) (Point, err
} else {
ts, err := parseIntBytes(ts, 10, 64)
if err != nil {
return nil, err
return points, err
}
pt.time, err = SafeCalcTime(ts, precision)
if err != nil {
return nil, err
return points, err
}
// Determine if there are illegal non-whitespace characters after the
// timestamp block.
for pos < len(buf) {
if buf[pos] != ' ' {
return nil, ErrInvalidPoint
return points, ErrInvalidPoint
}
pos++
}
}
return pt, nil
// Loop over fields and split points while validating field.
var maxKeyErr error
if err := walkFields(fields, func(k, v, fieldBuf []byte) bool {
newKey := key
// Build new key with measurement & field as keys.
if rewrite {
newKey = newV2Key(key, mm, k)
if sz := seriesKeySizeV2(key, mm, k); sz > MaxKeyLength {
maxKeyErr = fmt.Errorf("max key length exceeded: %v > %v", sz, MaxKeyLength)
return false
}
}
other := pt
other.key = newKey
other.fields = fieldBuf
points = append(points, &other)
return true
}); err != nil {
return points, err
} else if maxKeyErr != nil {
return points, maxKeyErr
}
return points, nil
}
// newV2Key returns a new key by converting the old measurement & field into keys.
func newV2Key(oldKey, mm, field []byte) []byte {
newKey := make([]byte, len(mm)+1+len(MeasurementTagKey)+1+len(oldKey)+1+len(FieldKeyTagKey)+1+len(field))
buf := newKey
copy(buf, mm)
buf = buf[len(mm):]
buf[0], buf[1], buf[2], buf = ',', MeasurementTagKeyBytes[0], '=', buf[3:]
copy(buf, oldKey)
buf = buf[len(oldKey):]
buf[0], buf[1], buf[2], buf = ',', FieldKeyTagKeyBytes[0], '=', buf[3:]
copy(buf, field)
return newKey
}
// GetPrecisionMultiplier will return a multiplier for the precision specified.
func GetPrecisionMultiplier(precision string) int64 {
d := time.Nanosecond
switch precision {
case "u":
case "us":
d = time.Microsecond
case "ms":
d = time.Millisecond
case "s":
d = time.Second
case "m":
d = time.Minute
case "h":
d = time.Hour
}
return int64(d)
}
@ -1199,23 +1309,33 @@ func scanFieldValue(buf []byte, i int) (int, []byte) {
}
func EscapeMeasurement(in []byte) []byte {
for b, esc := range measurementEscapeCodes {
in = bytes.Replace(in, []byte{b}, esc, -1)
for _, c := range measurementEscapeCodes {
if bytes.IndexByte(in, c.k[0]) != -1 {
in = bytes.Replace(in, c.k[:], c.esc[:], -1)
}
}
return in
}
func unescapeMeasurement(in []byte) []byte {
for b, esc := range measurementEscapeCodes {
in = bytes.Replace(in, esc, []byte{b}, -1)
func UnescapeMeasurement(in []byte) []byte {
if bytes.IndexByte(in, '\\') == -1 {
return in
}
for i := range measurementEscapeCodes {
c := &measurementEscapeCodes[i]
if bytes.IndexByte(in, c.k[0]) != -1 {
in = bytes.Replace(in, c.esc[:], c.k[:], -1)
}
}
return in
}
func escapeTag(in []byte) []byte {
for b, esc := range tagEscapeCodes {
if bytes.IndexByte(in, b) != -1 {
in = bytes.Replace(in, []byte{b}, esc, -1)
for i := range tagEscapeCodes {
c := &tagEscapeCodes[i]
if bytes.IndexByte(in, c.k[0]) != -1 {
in = bytes.Replace(in, c.k[:], c.esc[:], -1)
}
}
return in
@ -1226,9 +1346,10 @@ func unescapeTag(in []byte) []byte {
return in
}
for b, esc := range tagEscapeCodes {
if bytes.IndexByte(in, b) != -1 {
in = bytes.Replace(in, esc, []byte{b}, -1)
for i := range tagEscapeCodes {
c := &tagEscapeCodes[i]
if bytes.IndexByte(in, c.k[0]) != -1 {
in = bytes.Replace(in, c.esc[:], c.k[:], -1)
}
}
return in
@ -1280,7 +1401,8 @@ func unescapeStringField(in string) string {
}
// NewPoint returns a new point with the given measurement name, tags, fields and timestamp. If
// an unsupported field value (NaN) or out of range time is passed, this function returns an error.
// an unsupported field value (NaN, or +/-Inf) or out of range time is passed, this function
// returns an error.
func NewPoint(name string, tags Tags, fields Fields, t time.Time) (Point, error) {
key, err := pointKey(name, tags, fields, t)
if err != nil {
@ -1294,6 +1416,15 @@ func NewPoint(name string, tags Tags, fields Fields, t time.Time) (Point, error)
}, nil
}
// NewPointFromSeries returns a Point given the serialized key, some fields, and a time.
func NewPointFromSeries(key []byte, fields Fields, t time.Time) Point {
return &point{
key: key,
time: t,
fields: fields.MarshalBinary(),
}
}
// pointKey checks some basic requirements for valid points, and returns the
// key, along with an possible error.
func pointKey(measurement string, tags Tags, fields Fields, t time.Time) ([]byte, error) {
@ -1311,13 +1442,19 @@ func pointKey(measurement string, tags Tags, fields Fields, t time.Time) ([]byte
switch value := value.(type) {
case float64:
// Ensure the caller validates and handles invalid field values
if math.IsInf(value, 0) {
return nil, fmt.Errorf("+/-Inf is an unsupported value for field %s", key)
}
if math.IsNaN(value) {
return nil, fmt.Errorf("NaN is an unsupported value for field %s", key)
return nil, fmt.Errorf("NAN is an unsupported value for field %s", key)
}
case float32:
// Ensure the caller validates and handles invalid field values
if math.IsInf(float64(value), 0) {
return nil, fmt.Errorf("+/-Inf is an unsupported value for field %s", key)
}
if math.IsNaN(float64(value)) {
return nil, fmt.Errorf("NaN is an unsupported value for field %s", key)
return nil, fmt.Errorf("NAN is an unsupported value for field %s", key)
}
}
if len(key) == 0 {
@ -1327,7 +1464,7 @@ func pointKey(measurement string, tags Tags, fields Fields, t time.Time) ([]byte
key := MakeKey([]byte(measurement), tags)
for field := range fields {
sz := seriesKeySize(key, []byte(field))
sz := seriesKeySizeV1(key, []byte(field))
if sz > MaxKeyLength {
return nil, fmt.Errorf("max key length exceeded: %v > %v", sz, MaxKeyLength)
}
@ -1336,10 +1473,12 @@ func pointKey(measurement string, tags Tags, fields Fields, t time.Time) ([]byte
return key, nil
}
func seriesKeySize(key, field []byte) int {
// 4 is the length of the tsm1.fieldKeySeparator constant. It's inlined here to avoid a circular
// dependency.
return len(key) + 4 + len(field)
func seriesKeySizeV1(key, field []byte) int {
return len(key) + len("#!~#") + len(field)
}
func seriesKeySizeV2(key, mm, field []byte) int {
return len(mm) + len(",\xFF=") + len(field) + len(",\x00=") + len(key) + len("#!~#") + len(field)
}
// NewPointFromBytes returns a new Point from a marshalled Point.
@ -1441,10 +1580,14 @@ func (p *point) Tags() Tags {
if p.cachedTags != nil {
return p.cachedTags
}
p.cachedTags = parseTags(p.key)
p.cachedTags = parseTags(p.key, nil)
return p.cachedTags
}
func (p *point) ForEachTag(fn func(k, v []byte) bool) {
walkTags(p.key, fn)
}
func (p *point) HasTag(tag []byte) bool {
if len(p.key) == 0 {
return false
@ -1504,15 +1647,20 @@ func walkTags(buf []byte, fn func(key, value []byte) bool) {
// walkFields walks each field key and value via fn. If fn returns false, the iteration
// is stopped. The values are the raw byte slices and not the converted types.
func walkFields(buf []byte, fn func(key, value []byte) bool) {
func walkFields(buf []byte, fn func(key, value, data []byte) bool) error {
var i int
var key, val []byte
for len(buf) > 0 {
data := buf
i, key = scanTo(buf, 0, '=')
if i > len(buf)-2 {
return fmt.Errorf("invalid value: field-key=%s", key)
}
buf = buf[i+1:]
i, val = scanFieldValue(buf, 0)
buf = buf[i:]
if !fn(key, val) {
if !fn(key, val, data[:len(data)-len(buf)]) {
break
}
@ -1521,29 +1669,52 @@ func walkFields(buf []byte, fn func(key, value []byte) bool) {
buf = buf[1:]
}
}
return nil
}
func parseTags(buf []byte) Tags {
// parseTags parses buf into the provided destination tags, returning destination
// Tags, which may have a different length and capacity.
func parseTags(buf []byte, dst Tags) Tags {
if len(buf) == 0 {
return nil
}
tags := make(Tags, bytes.Count(buf, []byte(",")))
p := 0
n := bytes.Count(buf, []byte(","))
if cap(dst) < n {
dst = make(Tags, n)
} else {
dst = dst[:n]
}
// Ensure existing behaviour when point has no tags and nil slice passed in.
if dst == nil {
dst = Tags{}
}
// Series keys can contain escaped commas, therefore the number of commas
// in a series key only gives an estimation of the upper bound on the number
// of tags.
var i int
walkTags(buf, func(key, value []byte) bool {
tags[p].Key = key
tags[p].Value = value
p++
dst[i].Key, dst[i].Value = key, value
i++
return true
})
return tags
return dst[:i]
}
// MakeKey creates a key for a set of tags.
func MakeKey(name []byte, tags Tags) []byte {
return AppendMakeKey(nil, name, tags)
}
// AppendMakeKey appends the key derived from name and tags to dst and returns the extended buffer.
func AppendMakeKey(dst []byte, name []byte, tags Tags) []byte {
// unescape the name and then re-escape it to avoid double escaping.
// The key should always be stored in escaped form.
return append(EscapeMeasurement(unescapeMeasurement(name)), tags.HashKey()...)
dst = append(dst, EscapeMeasurement(UnescapeMeasurement(name))...)
dst = tags.AppendHashKey(dst)
return dst
}
// SetTags replaces the tags for the point.
@ -1577,17 +1748,12 @@ func (p *point) Fields() (Fields, error) {
// SetPrecision will round a time to the specified precision.
func (p *point) SetPrecision(precision string) {
switch precision {
case "n":
case "u":
case "us":
p.SetTime(p.Time().Truncate(time.Microsecond))
case "ms":
p.SetTime(p.Time().Truncate(time.Millisecond))
case "s":
p.SetTime(p.Time().Truncate(time.Second))
case "m":
p.SetTime(p.Time().Truncate(time.Minute))
case "h":
p.SetTime(p.Time().Truncate(time.Hour))
}
}
@ -1692,10 +1858,7 @@ func (p *point) UnmarshalBinary(b []byte) error {
p.fields, b = b[:n], b[n:]
// Read timestamp.
if err := p.time.UnmarshalBinary(b); err != nil {
return err
}
return nil
return p.time.UnmarshalBinary(b)
}
// PrecisionString returns a string representation of the point. If there
@ -1914,8 +2077,8 @@ func (a Tags) String() string {
// for data structures or delimiters for example.
func (a Tags) Size() int {
var total int
for _, t := range a {
total += t.Size()
for i := range a {
total += a[i].Size()
}
return total
}
@ -2048,42 +2211,78 @@ func (a Tags) Merge(other map[string]string) Tags {
// HashKey hashes all of a tag's keys.
func (a Tags) HashKey() []byte {
return a.AppendHashKey(nil)
}
func (a Tags) needsEscape() bool {
for i := range a {
t := &a[i]
for j := range tagEscapeCodes {
c := &tagEscapeCodes[j]
if bytes.IndexByte(t.Key, c.k[0]) != -1 || bytes.IndexByte(t.Value, c.k[0]) != -1 {
return true
}
}
}
return false
}
// AppendHashKey appends the result of hashing all of a tag's keys and values to dst and returns the extended buffer.
func (a Tags) AppendHashKey(dst []byte) []byte {
// Empty maps marshal to empty bytes.
if len(a) == 0 {
return nil
return dst
}
// Type invariant: Tags are sorted
escaped := make(Tags, 0, len(a))
sz := 0
for _, t := range a {
ek := escapeTag(t.Key)
ev := escapeTag(t.Value)
if len(ev) > 0 {
escaped = append(escaped, Tag{Key: ek, Value: ev})
sz += len(ek) + len(ev)
var escaped Tags
if a.needsEscape() {
var tmp [20]Tag
if len(a) < len(tmp) {
escaped = tmp[:len(a)]
} else {
escaped = make(Tags, len(a))
}
for i := range a {
t := &a[i]
nt := &escaped[i]
nt.Key = escapeTag(t.Key)
nt.Value = escapeTag(t.Value)
sz += len(nt.Key) + len(nt.Value)
}
} else {
sz = a.Size()
escaped = a
}
sz += len(escaped) + (len(escaped) * 2) // separators
// Generate marshaled bytes.
b := make([]byte, sz)
buf := b
if cap(dst)-len(dst) < sz {
nd := make([]byte, len(dst), len(dst)+sz)
copy(nd, dst)
dst = nd
}
buf := dst[len(dst) : len(dst)+sz]
idx := 0
for _, k := range escaped {
for i := range escaped {
k := &escaped[i]
if len(k.Value) == 0 {
continue
}
buf[idx] = ','
idx++
copy(buf[idx:idx+len(k.Key)], k.Key)
copy(buf[idx:], k.Key)
idx += len(k.Key)
buf[idx] = '='
idx++
copy(buf[idx:idx+len(k.Value)], k.Value)
copy(buf[idx:], k.Value)
idx += len(k.Value)
}
return b[:idx]
return dst[:len(dst)+idx]
}
// CopyTags returns a shallow copy of tags.
@ -2121,7 +2320,7 @@ func DeepCopyTags(a Tags) Tags {
// values.
type Fields map[string]interface{}
// FieldIterator retuns a FieldIterator that can be used to traverse the
// FieldIterator returns a FieldIterator that can be used to traverse the
// fields of a point without constructing the in-memory map.
func (p *point) FieldIterator() FieldIterator {
p.Reset()
@ -2242,7 +2441,7 @@ func (p *point) Reset() {
}
// MarshalBinary encodes all the fields to their proper type and returns the binary
// represenation
// representation
// NOTE: uint64 is specifically not supported due to potential overflow when we decode
// again later to an int64
// NOTE2: uint is accepted, and may be 64 bits, and is for some reason accepted...
@ -2330,8 +2529,37 @@ func appendField(b []byte, k string, v interface{}) []byte {
return b
}
type byteSlices [][]byte
// ValidToken returns true if the provided token is a valid unicode string, and
// only contains printable, non-replacement characters.
func ValidToken(a []byte) bool {
if !utf8.Valid(a) {
return false
}
func (a byteSlices) Len() int { return len(a) }
func (a byteSlices) Less(i, j int) bool { return bytes.Compare(a[i], a[j]) == -1 }
func (a byteSlices) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
for _, r := range string(a) {
if !unicode.IsPrint(r) || r == unicode.ReplacementChar {
return false
}
}
return true
}
// ValidTagTokens returns true if all the provided tag key and values are
// valid.
//
// ValidTagTokens does not validate the special tag keys used to represent the
// measurement name and field key, but it does validate the associated values.
func ValidTagTokens(tags Tags) bool {
for _, tag := range tags {
// Validate all external tag keys.
if !bytes.Equal(tag.Key, MeasurementTagKeyBytes) && !bytes.Equal(tag.Key, FieldKeyTagKeyBytes) && !ValidToken(tag.Key) {
return false
}
// Validate all tag values (this will also validate the field key, which is a tag value for the special field key tag key).
if !ValidToken(tag.Value) {
return false
}
}
return true
}

View file

@ -10,7 +10,7 @@ import (
)
const (
// MinNanoTime is the minumum time that can be represented.
// MinNanoTime is the minimum time that can be represented.
//
// 1677-09-21 00:12:43.145224194 +0000 UTC
//

View file

@ -6,7 +6,7 @@ IP address of a firewall.
NAT-PMP is supported by Apple brand routers and open source routers like Tomato and DD-WRT.
See http://tools.ietf.org/html/draft-cheshire-nat-pmp-03
See https://tools.ietf.org/rfc/rfc6886.txt
[![Build Status](https://travis-ci.org/jackpal/go-nat-pmp.svg)](https://travis-ci.org/jackpal/go-nat-pmp)
@ -20,11 +20,12 @@ Usage
-----
import (
"fmt"
"github.com/jackpal/gateway"
natpmp "github.com/jackpal/go-nat-pmp"
)
gatewayIP, err = gateway.DiscoverGateway()
gatewayIP, err := gateway.DiscoverGateway()
if err != nil {
return
}
@ -34,7 +35,7 @@ Usage
if err != nil {
return
}
print("External IP address:", response.ExternalIPAddress)
fmt.Println("External IP address: %v", response.ExternalIPAddress)
Clients
-------

View file

@ -9,14 +9,14 @@ import (
// Implement the NAT-PMP protocol, typically supported by Apple routers and open source
// routers such as DD-WRT and Tomato.
//
// See http://tools.ietf.org/html/draft-cheshire-nat-pmp-03
// See https://tools.ietf.org/rfc/rfc6886.txt
//
// Usage:
//
// client := natpmp.NewClient(gatewayIP)
// response, err := client.GetExternalAddress()
// The recommended mapping lifetime for AddPortMapping
// The recommended mapping lifetime for AddPortMapping.
const RECOMMENDED_MAPPING_LIFETIME_SECONDS = 3600
// Interface used to make remote procedure calls.
@ -49,6 +49,8 @@ type GetExternalAddressResult struct {
}
// Get the external address of the router.
//
// Note that this call can take up to 128 seconds to return.
func (n *Client) GetExternalAddress() (result *GetExternalAddressResult, err error) {
msg := make([]byte, 2)
msg[0] = 0 // Version 0
@ -71,7 +73,8 @@ type AddPortMappingResult struct {
PortMappingLifetimeInSeconds uint32
}
// Add (or delete) a port mapping. To delete a mapping, set the requestedExternalPort and lifetime to 0
// Add (or delete) a port mapping. To delete a mapping, set the requestedExternalPort and lifetime to 0.
// Note that this call can take up to 128 seconds to return.
func (n *Client) AddPortMapping(protocol string, internalPort, requestedExternalPort int, lifetime int) (result *AddPortMappingResult, err error) {
var opcode byte
if protocol == "udp" {
@ -85,6 +88,7 @@ func (n *Client) AddPortMapping(protocol string, internalPort, requestedExternal
msg := make([]byte, 12)
msg[0] = 0 // Version 0
msg[1] = opcode
// [2:3] is reserved.
writeNetworkOrderUint16(msg[4:6], uint16(internalPort))
writeNetworkOrderUint16(msg[6:8], uint16(requestedExternalPort))
writeNetworkOrderUint32(msg[8:12], uint32(lifetime))

View file

@ -16,7 +16,7 @@ The router is optimized for high performance and a small memory footprint. It sc
**Parameters in your routing pattern:** Stop parsing the requested URL path, just give the path segment a name and the router delivers the dynamic value to you. Because of the design of the router, path parameters are very cheap.
**Zero Garbage:** The matching and dispatching process generates zero bytes of garbage. In fact, the only heap allocations that are made, is by building the slice of the key-value pairs for path parameters. If the request path contains no parameters, not a single heap allocation is necessary.
**Zero Garbage:** The matching and dispatching process generates zero bytes of garbage. The only heap allocations that are made are building the slice of the key-value pairs for path parameters, and building new context and request objects (the latter only in the standard `Handler`/`HandlerFunc` api). In the 3-argument API, if the request path contains no parameters not a single heap allocation is necessary.
**Best Performance:** [Benchmarks speak for themselves](https://github.com/julienschmidt/go-http-routing-benchmark). See below for technical details of the implementation.
@ -108,7 +108,7 @@ Priority Path Handle
Every `*<num>` represents the memory address of a handler function (a pointer). If you follow a path trough the tree from the root to the leaf, you get the complete route path, e.g `\blog\:post\`, where `:post` is just a placeholder ([*parameter*](#named-parameters)) for an actual post name. Unlike hash-maps, a tree structure also allows us to use dynamic parts like the `:post` parameter, since we actually match against the routing patterns instead of just comparing hashes. [As benchmarks show](https://github.com/julienschmidt/go-http-routing-benchmark), this works very well and efficient.
Since URL paths have a hierarchical structure and make use only of a limited set of characters (byte values), it is very likely that there are a lot of common prefixes. This allows us to easily reduce the routing into ever smaller problems. Moreover the router manages a separate tree for every request method. For one thing it is more space efficient than holding a method->handle map in every single node, for another thing is also allows us to greatly reduce the routing problem before even starting the look-up in the prefix-tree.
Since URL paths have a hierarchical structure and make use only of a limited set of characters (byte values), it is very likely that there are a lot of common prefixes. This allows us to easily reduce the routing into ever smaller problems. Moreover the router manages a separate tree for every request method. For one thing it is more space efficient than holding a method->handle map in every single node, it also allows us to greatly reduce the routing problem before even starting the look-up in the prefix-tree.
For even better scalability, the child nodes on each tree level are ordered by priority, where the priority is just the number of handles registered in sub nodes (children, grandchildren, and so on..). This helps in two ways:
@ -149,14 +149,14 @@ Define a router per host!
// We just use a map here, in which we map host names (with port) to http.Handlers
type HostSwitch map[string]http.Handler
// Implement the ServerHTTP method on our new type
// Implement the ServeHTTP method on our new type
func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Check if a http.Handler is registered for the given host.
// If yes, use it to handle the request.
if handler := hs[r.Host]; handler != nil {
handler.ServeHTTP(w, r)
} else {
// Handle host names for wich no handler is registered
// Handle host names for which no handler is registered
http.Error(w, "Forbidden", 403) // Or Redirect?
}
}

View file

@ -41,7 +41,7 @@ func CleanPath(p string) string {
buf[0] = '/'
}
trailing := n > 2 && p[n-1] == '/'
trailing := n > 1 && p[n-1] == '/'
// A bit more clunky without a 'lazybuf' like the path package, but the loop
// gets completely inlined (bufApp). So in contrast to the path package this
@ -59,11 +59,11 @@ func CleanPath(p string) string {
case p[r] == '.' && p[r+1] == '/':
// . element
r++
r += 2
case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'):
// .. element: remove to last /
r += 2
r += 3
if w > 1 {
// can backtrack

View file

@ -236,16 +236,6 @@ func (r *Router) Handle(method, path string, handle Handle) {
root.addRoute(path, handle)
}
// Handler is an adapter which allows the usage of an http.Handler as a
// request handle.
func (r *Router) Handler(method, path string, handler http.Handler) {
r.Handle(method, path,
func(w http.ResponseWriter, req *http.Request, _ Params) {
handler.ServeHTTP(w, req)
},
)
}
// HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a
// request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
@ -376,14 +366,12 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
if req.Method == "OPTIONS" {
if req.Method == "OPTIONS" && r.HandleOPTIONS {
// Handle OPTIONS requests
if r.HandleOPTIONS {
if allow := r.allowed(path, req.Method); len(allow) > 0 {
w.Header().Set("Allow", allow)
return
}
}
} else {
// Handle 405
if r.HandleMethodNotAllowed {

274
vendor/vendor.json vendored
View file

@ -3,10 +3,10 @@
"ignore": "test",
"package": [
{
"checksumSHA1": "z+M6FYl9EKsoZZMLcT0Ktwfk8pI=",
"checksumSHA1": "xrIesz0blvPSWEz5hsS85bcM04o=",
"path": "github.com/Azure/azure-pipeline-go/pipeline",
"revision": "7571e8eb0876932ab505918ff7ed5107773e5ee2",
"revisionTime": "2018-06-07T21:19:23Z"
"revision": "55fedc85a614dcd0e942a66f302ae3efb83d563c",
"revisionTime": "2019-04-17T01:50:18Z"
},
{
"checksumSHA1": "5nsGu77r69lloEWbFhMof2UA9rY=",
@ -15,22 +15,22 @@
"revisionTime": "2018-07-12T00:56:34Z"
},
{
"checksumSHA1": "QC55lHNOv1+UAL2xtIHw17MJ8J8=",
"checksumSHA1": "+uOjgDmeVWUwUI9l/AVZGa6+yQs=",
"path": "github.com/StackExchange/wmi",
"revision": "5d049714c4a64225c3c79a7cf7d02f7fb5b96338",
"revisionTime": "2018-01-16T20:38:02Z"
"revision": "cbe66965904dbe8a6cd589e2298e5d8b986bd7dd",
"revisionTime": "2019-05-23T21:33:15Z"
},
{
"checksumSHA1": "8skJYOdQytXjimcDPLRW4tonX3A=",
"checksumSHA1": "q2qmF0r4PmyMnsDb/CUj8GJET9Q=",
"path": "github.com/allegro/bigcache",
"revision": "e24eb225f15679bbe54f91bfa7da3b00e59b9768",
"revisionTime": "2019-02-18T06:46:05Z"
"revision": "69ea0af04088faa57adb9ac683934277141e92a5",
"revisionTime": "2019-06-18T19:10:10Z"
},
{
"checksumSHA1": "vtT7NcYLatJmxVQQEeSESyrgVg0=",
"path": "github.com/allegro/bigcache/queue",
"revision": "e24eb225f15679bbe54f91bfa7da3b00e59b9768",
"revisionTime": "2019-02-18T06:46:05Z"
"revision": "69ea0af04088faa57adb9ac683934277141e92a5",
"revisionTime": "2019-06-18T19:10:10Z"
},
{
"checksumSHA1": "hp2pna9yEn9hemIjc7asalxL2Qs=",
@ -39,76 +39,76 @@
"revisionTime": "2018-07-02T11:14:01Z"
},
{
"checksumSHA1": "USkefO0g1U9mr+8hagv3fpSkrxg=",
"checksumSHA1": "kto0asmOE+Nuhj7T8cvrvIwq2ak=",
"path": "github.com/aristanetworks/goarista/monotime",
"revision": "ea17b1a17847fb6e4c0a91de0b674704693469b0",
"revisionTime": "2017-02-10T01:56:32Z"
"revision": "52c2a7864a0891eefaed13a457510c7405a7105b",
"revisionTime": "2019-06-07T11:12:40Z"
},
{
"checksumSHA1": "gZQ6HheWahvZzIc3phBnOwoWHjE=",
"checksumSHA1": "WBJp3KWXMrlROP4qZWhZq0dvnLM=",
"path": "github.com/btcsuite/btcd/btcec",
"revision": "2e60448ffcc6bf78332d1fe590260095f554dd78",
"revisionTime": "2017-11-28T15:02:46Z"
"revision": "962a206e94e9151fe41bbd6d6464af4ba7168f50",
"revisionTime": "2019-06-14T01:37:41Z"
},
{
"checksumSHA1": "cDMtzKmdTx4CcIpP4broa+16X9g=",
"checksumSHA1": "Q43R3tBW9/xOqcSTQ2dm7+2I2LY=",
"path": "github.com/cespare/cp",
"revision": "165db2f241fd235aec29ba6d9b1ccd5f1c14637c",
"revisionTime": "2015-01-22T07:26:53Z"
"revision": "db1407d84ae423533fe1d25510c1c4c4d831f0fc",
"revisionTime": "2018-12-20T00:00:49Z"
},
{
"checksumSHA1": "dvabztWVQX8f6oMLRyv4dLH+TGY=",
"checksumSHA1": "CSPbwbyzqA6sfORicn4HFtIhF/c=",
"path": "github.com/davecgh/go-spew/spew",
"revision": "346938d642f2ec3594ed81d874461961cd0faa76",
"revisionTime": "2016-10-29T20:57:26Z"
"revision": "d8f796af33cc11cb798c1aaeb27a4ebc5099927d",
"revisionTime": "2018-08-30T19:11:22Z"
},
{
"checksumSHA1": "1xK7ycc1ICRInk/S9iiyB9Rpv50=",
"checksumSHA1": "vwNjR8772Pqs8z9ZdPFoatNI9Kg=",
"path": "github.com/deckarep/golang-set",
"revision": "504e848d77ea4752b3057b8fb46da0e7f746ccf3",
"revisionTime": "2018-06-03T19:32:48Z"
"revision": "699df6a3acf6867538e50931511e9dc403da108a",
"revisionTime": "2018-09-27T02:58:44Z"
},
{
"checksumSHA1": "Ad8LPSCP9HctFrmskh+S5HpHXcs=",
"path": "github.com/docker/docker/pkg/reexec",
"revision": "8e610b2b55bfd1bfa9436ab110d311f5e8a74dcb",
"revisionTime": "2018-06-25T18:44:42Z"
"revision": "52c16677b22d0aafc0e56db04e691164d46bb2c4",
"revisionTime": "2019-06-21T08:12:58Z"
},
{
"checksumSHA1": "zYnPsNAVm1/ViwCkN++dX2JQhBo=",
"checksumSHA1": "Vdaftt1J1nSEmhiLz4m90YY+S0A=",
"path": "github.com/edsrzf/mmap-go",
"revision": "935e0e8a636ca4ba70b713f3e38a19e1b77739e8",
"revisionTime": "2016-05-12T03:30:02Z"
"revision": "904c4ced31cdffe19e971afa0b3d319ff06d9c72",
"revisionTime": "2018-12-22T14:20:22Z"
},
{
"checksumSHA1": "jElNoLEe7m/iaoF1vYIHyNaS2SE=",
"checksumSHA1": "tuhGcluN3UtoiFBovqsep6aPx3s=",
"path": "github.com/elastic/gosigar",
"revision": "37f05ff46ffa7a825d1b24cf2b62d4a4c1a9d2e8",
"revisionTime": "2018-03-30T10:04:40Z"
"revision": "99ed9cf55303a9d3936cb656b9a86a4a6e67b30a",
"revisionTime": "2019-05-27T11:32:19Z"
},
{
"checksumSHA1": "qDsgp2kAeI9nhj565HUScaUyjU4=",
"checksumSHA1": "R70u1XUHH/t1pquvHEFDeUFtkFk=",
"path": "github.com/elastic/gosigar/sys/windows",
"revision": "a3814ce5008e612a0c6d027608b54e1d0d9a5613",
"revisionTime": "2018-01-22T22:25:45Z"
"revision": "99ed9cf55303a9d3936cb656b9a86a4a6e67b30a",
"revisionTime": "2019-05-27T11:32:19Z"
},
{
"checksumSHA1": "7oFpbmDfGobwKsFLIf6wMUvVoKw=",
"checksumSHA1": "BxH9xJUqczhpL57gfKZe2/VlBHY=",
"path": "github.com/fatih/color",
"revision": "5ec5d9d3c2cf82e9688b34e9bc27a94d616a7193",
"revisionTime": "2017-02-09T08:00:14Z"
"revision": "3f9d52f7176a6927daacff70a3e8d1dc2025c53e",
"revisionTime": "2018-10-10T23:13:11Z"
},
{
"checksumSHA1": "Jq1rrHSGPfh689nA2hL1QVb62zE=",
"checksumSHA1": "IfDucP2AWAj+1uW+ho6NEEDG7nk=",
"path": "github.com/fjl/memsize",
"revision": "ca190fb6ffbc076ff49197b7168a760f30182d2e",
"revisionTime": "2018-04-18T12:24:29Z"
"revision": "2a09253e352a56f419bd88effab0483f52da4c7d",
"revisionTime": "2018-09-29T19:40:37Z"
},
{
"checksumSHA1": "Z13QAYTqeW4cTiglkc2F05gWLu4=",
"path": "github.com/fjl/memsize/memsizeui",
"revision": "ca190fb6ffbc076ff49197b7168a760f30182d2e",
"revisionTime": "2018-04-18T12:24:29Z"
"revision": "2a09253e352a56f419bd88effab0483f52da4c7d",
"revisionTime": "2018-09-29T19:40:37Z"
},
{
"checksumSHA1": "gsiYVjwKzFKe+JuIimgKlrPyipA=",
@ -117,22 +117,22 @@
"revisionTime": "2019-06-07T06:51:34Z"
},
{
"checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=",
"checksumSHA1": "vTmc/uvCPpTs51Rl9bVomPZMZIM=",
"path": "github.com/go-ole/go-ole",
"revision": "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506",
"revisionTime": "2017-11-10T16:07:06Z"
"revision": "97b6244175ae18ea6eef668034fd6565847501c9",
"revisionTime": "2019-02-26T14:26:00Z"
},
{
"checksumSHA1": "PArleDBtadu2qO4hJwHR8a3IOTA=",
"path": "github.com/go-ole/go-ole/oleutil",
"revision": "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506",
"revisionTime": "2017-11-10T16:07:06Z"
"revision": "97b6244175ae18ea6eef668034fd6565847501c9",
"revisionTime": "2019-02-26T14:26:00Z"
},
{
"checksumSHA1": "KZ3QD2QgUS4RcoKiA3mn5pSlJxQ=",
"checksumSHA1": "H8wo+NR5z+VRl0wqPYpVQfC06ks=",
"path": "github.com/go-stack/stack",
"revision": "54be5f394ed2c3e19dac9134a40a95ba5a017f7b",
"revisionTime": "2017-07-10T16:04:46Z"
"revision": "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a",
"revisionTime": "2018-08-26T13:48:48Z"
},
{
"checksumSHA1": "CGj8VcI/CpzxaNqlqpEVM7qElD4=",
@ -147,22 +147,106 @@
"revisionTime": "2019-05-17T06:12:10Z"
},
{
"checksumSHA1": "p/8vSviYF91gFflhrt5vkyksroo=",
"checksumSHA1": "L3HoHVqp2EaBSOqBxB7l0PTyu7g=",
"path": "github.com/golang/snappy",
"revision": "553a641470496b2327abcac10b36396bd98e45c9",
"revisionTime": "2017-02-15T23:32:05Z"
"revision": "2a8bb927dd31d8daada140a5d09578521ce5c36a",
"revisionTime": "2019-02-18T23:22:22Z"
},
{
"checksumSHA1": "d9PxF1XQGLMJZRct2R8qVM/eYlE=",
"checksumSHA1": "2Ow9mKLW+Bs7kKc2VAurAt65ke4=",
"path": "github.com/graph-gophers/graphql-go",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "nSpiywJOLU4e01NPAQf4lBtnKtw=",
"path": "github.com/graph-gophers/graphql-go/errors",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "vKQxqeKlmMEYjgHISd1nmdVASGs=",
"path": "github.com/graph-gophers/graphql-go/internal/common",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "4dn67lmOWtvQNr7YF/KDux+oYLU=",
"path": "github.com/graph-gophers/graphql-go/internal/exec",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "Bucl75VZRgPBboreGJQKIv/RHcY=",
"path": "github.com/graph-gophers/graphql-go/internal/exec/packer",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "YBLNBJzcLnMDqgJRV0MY52TVOyE=",
"path": "github.com/graph-gophers/graphql-go/internal/exec/resolvable",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "AG9OwCZI6uncNdsLnYzetvGhgoM=",
"path": "github.com/graph-gophers/graphql-go/internal/exec/selected",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "VSDhUJOJzbyTIeYfunqWTTKDBok=",
"path": "github.com/graph-gophers/graphql-go/internal/query",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "jWj+ES9xBD5AAiOv1kqRVm+C/kM=",
"path": "github.com/graph-gophers/graphql-go/internal/schema",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "MhEoghTb3LXzuOwS19qKgZtrZOM=",
"path": "github.com/graph-gophers/graphql-go/internal/validation",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "JY1146PnFG5ZY82Drfti2yW6Wqo=",
"path": "github.com/graph-gophers/graphql-go/introspection",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "Bv2f9HtxVix0i3vgwGUMfIeBVRQ=",
"path": "github.com/graph-gophers/graphql-go/log",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "h9lR2l7dWiXo/yF4zq6XZE4MEBY=",
"path": "github.com/graph-gophers/graphql-go/relay",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "4rdWXZp7lvNmRwUHYnELTPng8/8=",
"path": "github.com/graph-gophers/graphql-go/trace",
"revision": "8f92f34fc59823d34fc08bfdc9fd266b854e2b50",
"revisionTime": "2019-06-10T16:17:39Z"
},
{
"checksumSHA1": "/04S+1K8V7ix4YQ9TdYUA+MMdRo=",
"path": "github.com/hashicorp/golang-lru",
"revision": "0a025b7e63adc15a622f29b0b2c4c3848243bbf6",
"revisionTime": "2016-08-13T22:13:03Z"
"revision": "59383c442f7d7b190497e9bb8fc17a48d06cd03f",
"revisionTime": "2019-05-20T14:04:33Z"
},
{
"checksumSHA1": "9hffs0bAIU6CquiRhKQdzjHnKt0=",
"checksumSHA1": "oPFbkG2QReaXuViYt+zMXLdT4Mo=",
"path": "github.com/hashicorp/golang-lru/simplelru",
"revision": "0a025b7e63adc15a622f29b0b2c4c3848243bbf6",
"revisionTime": "2016-08-13T22:13:03Z"
"revision": "59383c442f7d7b190497e9bb8fc17a48d06cd03f",
"revisionTime": "2019-05-20T14:04:33Z"
},
{
"checksumSHA1": "ZxzYc1JwJ3U6kZbw/KGuPko5lSY=",
@ -171,46 +255,46 @@
"revisionTime": "2015-10-03T19:46:02Z"
},
{
"checksumSHA1": "f55gR+6YClh0i/FOhdy66SOUiwY=",
"checksumSHA1": "RBg+tt0WVRJPktk4/0hjW/oMHgo=",
"path": "github.com/huin/goupnp",
"revision": "679507af18f3c7ba2bcc7905392ce23e148661c3",
"revisionTime": "2016-12-24T10:41:01Z"
"revision": "656e61dfadd241c7cbdd22a023fa81ecb6860ea8",
"revisionTime": "2018-10-13T14:04:17Z"
},
{
"checksumSHA1": "U3NsxkodNX/tmOqkVDnGFRZ6dI4=",
"checksumSHA1": "xpDViB1cPwd5TRhi8M1lsi+tLeQ=",
"path": "github.com/huin/goupnp/dcps/internetgateway1",
"revision": "679507af18f3c7ba2bcc7905392ce23e148661c3",
"revisionTime": "2016-12-24T10:41:01Z"
"revision": "656e61dfadd241c7cbdd22a023fa81ecb6860ea8",
"revisionTime": "2018-10-13T14:04:17Z"
},
{
"checksumSHA1": "znTn+P/iEwi6Ax7r3N0GikeYMlk=",
"checksumSHA1": "IgbsyspRShLpG4bJXr9+jIOBuzA=",
"path": "github.com/huin/goupnp/dcps/internetgateway2",
"revision": "679507af18f3c7ba2bcc7905392ce23e148661c3",
"revisionTime": "2016-12-24T10:41:01Z"
"revision": "656e61dfadd241c7cbdd22a023fa81ecb6860ea8",
"revisionTime": "2018-10-13T14:04:17Z"
},
{
"checksumSHA1": "RLygtUlTOCtrI3KMswYLJnte1OU=",
"checksumSHA1": "CSFM1dzHvJr3u7cvSw2hr58+/5E=",
"path": "github.com/huin/goupnp/httpu",
"revision": "679507af18f3c7ba2bcc7905392ce23e148661c3",
"revisionTime": "2016-12-24T10:41:01Z"
"revision": "656e61dfadd241c7cbdd22a023fa81ecb6860ea8",
"revisionTime": "2018-10-13T14:04:17Z"
},
{
"checksumSHA1": "+S2t2qKK+wcpM+07eW7dCK/6oFU=",
"path": "github.com/huin/goupnp/scpd",
"revision": "679507af18f3c7ba2bcc7905392ce23e148661c3",
"revisionTime": "2016-12-24T10:41:01Z"
"revision": "656e61dfadd241c7cbdd22a023fa81ecb6860ea8",
"revisionTime": "2018-10-13T14:04:17Z"
},
{
"checksumSHA1": "80ieA8iPFaFeQFw++EiYn4jhcGs=",
"checksumSHA1": "GW81GsQSWYvxK6XoRJ6L+Op5bKg=",
"path": "github.com/huin/goupnp/soap",
"revision": "679507af18f3c7ba2bcc7905392ce23e148661c3",
"revisionTime": "2016-12-24T10:41:01Z"
"revision": "656e61dfadd241c7cbdd22a023fa81ecb6860ea8",
"revisionTime": "2018-10-13T14:04:17Z"
},
{
"checksumSHA1": "iqPUC/MoFGaRQnAudYGAW9BvF2o=",
"checksumSHA1": "PDmT/Xpscyf2Qc6XWYQ/yy8zz7w=",
"path": "github.com/huin/goupnp/ssdp",
"revision": "679507af18f3c7ba2bcc7905392ce23e148661c3",
"revisionTime": "2016-12-24T10:41:01Z"
"revision": "656e61dfadd241c7cbdd22a023fa81ecb6860ea8",
"revisionTime": "2018-10-13T14:04:17Z"
},
{
"checksumSHA1": "6tNwbL5tUS0dxYzADKVZtI2d/lE=",
@ -219,28 +303,28 @@
"revisionTime": "2017-10-09T17:24:46Z"
},
{
"checksumSHA1": "cfumoC9gHEUROd+fA8qK3WLFAZQ=",
"checksumSHA1": "Z2XCUBzGGV6d2jP6vzOvG01v6LA=",
"path": "github.com/influxdata/influxdb/models",
"revision": "b36b9f109f2da91c8941679caf5356e08eee0b2b",
"revisionTime": "2018-01-17T01:42:09Z"
"revision": "d45786570411039c77918315b96f9d6aecce53ec",
"revisionTime": "2019-06-21T23:19:42Z"
},
{
"checksumSHA1": "Z0Bb5PWa5WL/j5Dm2KJCLGn1l7U=",
"path": "github.com/influxdata/influxdb/pkg/escape",
"revision": "01288bdb0883a01cac999326bd34421b29acaec8",
"revisionTime": "2018-02-21T22:33:40Z"
"revision": "d45786570411039c77918315b96f9d6aecce53ec",
"revisionTime": "2019-06-21T23:19:42Z"
},
{
"checksumSHA1": "vTGKMIfiMwz43y5bsgx9PrL+AVw=",
"checksumSHA1": "RB2di6332iVfJoNbxC9lr6t3ScE=",
"path": "github.com/jackpal/go-nat-pmp",
"revision": "1fa385a6f45828c83361136b45b1a21a12139493",
"revisionTime": "2016-06-03T03:41:37Z"
"revision": "d89d09f6f3329bc3c2479aa3cafd76a5aa93a35c",
"revisionTime": "2018-10-21T19:25:11Z"
},
{
"checksumSHA1": "gKyBj05YkfuLFruAyPZ4KV9nFp8=",
"checksumSHA1": "z/DzcKNumSHzzxg9Widbi9KgwNw=",
"path": "github.com/julienschmidt/httprouter",
"revision": "975b5c4c7c21c0e3d2764200bf2aa8e34657ae6e",
"revisionTime": "2017-04-30T22:20:11Z"
"revision": "26a05976f9bf5c3aa992cc20e8588c359418ee58",
"revisionTime": "2018-10-21T22:38:31Z"
},
{
"checksumSHA1": "TU/WaqL7fYPDovmGVRSo8btD4ZM=",
@ -274,6 +358,10 @@
"revision": "c48cc78d482608239f6c4c92a4abd87eb8761c90",
"revisionTime": "2017-09-29T03:49:55Z"
},
{
"path": "github.com/naoina/go-stringutil",
"revision": ""
},
{
"checksumSHA1": "FYM/8R2CqS6PSNAoKl6X5gNJ20A=",
"path": "github.com/naoina/toml",