diff --git a/HISTORY.md b/HISTORY.md index 132dfaf8..eac91bf6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -28,8 +28,13 @@ The codebase for Dependency Injection, Internationalization and localization and ## Fixes and Improvements +- Minor improvements to the [JSON Kitchen Time](x/jsonx/kitchen_time.go). +- A session database can now implement the `EndRequest(ctx *context.Context, session *Session)` method which will be fired at the end of the request-response lifecycle. +- Improvements on JSON and ReadJSON when `Iris.Configuration.EnableOptimizations` is true. The request's Context is used whenever is necessary. +- New [monitor](_examples/monitor/monitor-middleware/main.go) middleware. + - New `RegisterRequestHandler` package-level and client methods to the new `x/client` package. Control or log the request-response lifecycle. -- New `RateLimit` HTTP Client option to the new `x/client` package. +- New `RateLimit` and `Debug` HTTP Client options to the new `x/client` package. - Push a security fix reported by [Kirill Efimov](https://github.com/kirill89) for older go runtimes. diff --git a/_examples/README.md b/_examples/README.md index 070610d3..5a563f69 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -93,6 +93,7 @@ * [The CSV Formatter](logging/request-logger/accesslog-csv/main.go) * [Create your own Formatter](logging/request-logger/accesslog-formatter/main.go) * [Root and Proxy AccessLog instances](logging/request-logger/accesslog-proxy/main.go) + * [Slack integration example](logging/request-logger/accesslog-slack/main.go) * API Documentation * [Yaag](apidoc/yaag/main.go) * [Swagger](https://github.com/iris-contrib/swagger/tree/master/_examples/basic) diff --git a/_examples/logging/request-logger/accesslog-slack/main.go b/_examples/logging/request-logger/accesslog-slack/main.go new file mode 100644 index 00000000..701e2d11 --- /dev/null +++ b/_examples/logging/request-logger/accesslog-slack/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + println("Navigate to: https://github.com/kataras/iris/issues/1808#issuecomment-1013757925") +} diff --git a/_examples/monitor/monitor-middleware/main.go b/_examples/monitor/monitor-middleware/main.go new file mode 100644 index 00000000..79208853 --- /dev/null +++ b/_examples/monitor/monitor-middleware/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "time" + + "github.com/kataras/iris/v12" + + "github.com/kataras/iris/v12/middleware/monitor" +) + +func main() { + app := iris.New() + + // Initialize and start the monitor middleware. + m := monitor.New(monitor.Options{ + RefreshInterval: 2 * time.Second, + ViewRefreshInterval: 2 * time.Second, + ViewTitle: "MyServer Monitor", + }) + // Manually stop monitoring on CMD/CTRL+C. + iris.RegisterOnInterrupt(m.Stop) + + // Serve the actual server's process and operating system statistics as JSON. + app.Post("/monitor", m.Stats) + // Render with the default page. + app.Get("/monitor", m.View) + + /* You can protect the /monitor under an /admin group of routes + with basic authentication or any type authorization and authentication system. + Example Code: + + app.Post("/monitor", myProtectMiddleware, m.Stats) + app.Get("/monitor", myProtectMiddleware, m.View) + */ + + /* You can also get the OS statistics using the Holder.GetStats method. + Example Code: + for { + stats := m.Holder.GetStats() + fmt.Printf("%#+v\n", stats) + time.Sleep(time.Second) + } + + Note that the same stats are also stored in the expvar metrics: + - pid_cpu + - pid_ram + - pid_conns + - os_cpu + - os_ram + - os_total_ram + - os_load_avg + - os_conns + Check https://github.com/iris-contrib/middleware/tree/master/expmetric + which can be integrated with datadog or other platforms. + */ + + app.Get("/", handler) + + app.Listen(":8080") +} + +func handler(ctx iris.Context) { + ctx.WriteString("Test Index Handler") +} diff --git a/_examples/sessions/overview/example/example.go b/_examples/sessions/overview/example/example.go index 7472e571..48c349c3 100644 --- a/_examples/sessions/overview/example/example.go +++ b/_examples/sessions/overview/example/example.go @@ -16,7 +16,7 @@ type BusinessModel struct { // NewApp returns a new application for showcasing the sessions feature. func NewApp(sess *sessions.Sessions) *iris.Application { app := iris.New() - app.Use(sess.Handler()) // session is always non-nil inside handlers now. + app.Use(sess.Handler()) // register the session manager on a group of routes or the root app. app.Get("/", func(ctx iris.Context) { session := sessions.Get(ctx) // same as sess.Start(ctx, cookieOptions...) diff --git a/_examples/sessions/securecookie/main.go b/_examples/sessions/securecookie/main.go index b2686626..d33c69e0 100644 --- a/_examples/sessions/securecookie/main.go +++ b/_examples/sessions/securecookie/main.go @@ -15,23 +15,21 @@ import ( ) func newApp() *iris.Application { - app := iris.New() - cookieName := "_session_id" // AES only supports key sizes of 16, 24 or 32 bytes. // You either need to provide exactly that amount or you derive the key from what you type in. hashKey := securecookie.GenerateRandomKey(64) blockKey := securecookie.GenerateRandomKey(32) s := securecookie.New(hashKey, blockKey) - mySessions := sessions.New(sessions.Config{ Cookie: cookieName, Encoding: s, AllowReclaim: true, }) - app = example.NewApp(mySessions) - return app + // mySessions.UseDatabase(...see sessions/database example folder) + + return example.NewApp(mySessions) } func main() { diff --git a/context/context.go b/context/context.go index 535da6d4..5c457b8c 100644 --- a/context/context.go +++ b/context/context.go @@ -30,8 +30,8 @@ import ( "github.com/Shopify/goreferrer" "github.com/fatih/structs" + gojson "github.com/goccy/go-json" "github.com/iris-contrib/schema" - jsoniter "github.com/json-iterator/go" "github.com/kataras/golog" "github.com/mailru/easyjson" "github.com/mailru/easyjson/jwriter" @@ -67,6 +67,12 @@ type ( Decode(data []byte) error } + // BodyDecoderWithContext same as BodyDecoder but it can accept a standard context, + // which is binded to the HTTP request's context. + BodyDecoderWithContext interface { + DecodeContext(ctx stdContext.Context, data []byte) error + } + // Unmarshaler is the interface implemented by types that can unmarshal any raw data. // TIP INFO: Any pointer to a value which implements the BodyDecoder can be override the unmarshaler. Unmarshaler interface { @@ -85,7 +91,7 @@ type ( // is terminated and the error is received by the ReadJSONStream method, // otherwise it continues to read the next available object. // Look the `Context.ReadJSONStream` method. - DecodeFunc func(outPtr interface{}) error + DecodeFunc func(ctx stdContext.Context, outPtr interface{}) error ) // Unmarshal parses the X-encoded data and stores the result in the value pointed to by v. @@ -2297,6 +2303,10 @@ func (ctx *Context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) e return err } + if decoderWithCtx, ok := outPtr.(BodyDecoderWithContext); ok { + return decoderWithCtx.DecodeContext(ctx.request.Context(), rawData) + } + // check if the v contains its own decode // in this case the v should be a pointer also, // but this is up to the user's custom Decode implementation* @@ -2321,36 +2331,6 @@ func (ctx *Context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) e return ctx.app.Validate(outPtr) } -// internalBodyDecoder is a generic type of decoder, usually used to export stream reading functionality -// of a JSON request. -type internalBodyDecoder interface { - Decode(outPutr interface{}) error -} - -// Same as UnmarshalBody but it operates on body stream. -func (ctx *Context) decodeBody(outPtr interface{}, decoder internalBodyDecoder) error { - // check if the v contains its own decode - // in this case the v should be a pointer also, - // but this is up to the user's custom Decode implementation* - // - // See 'BodyDecoder' for more. - if structDecoder, isDecoder := outPtr.(BodyDecoder); isDecoder { - rawData, err := ctx.GetBody() - if err != nil { - return err - } - - return structDecoder.Decode(rawData) - } - - err := decoder.Decode(outPtr) - if err != nil { - return err - } - - return ctx.app.Validate(outPtr) -} - func (ctx *Context) shouldOptimize() bool { return ctx.app.ConfigurationReadOnly().GetEnableOptimizations() } @@ -2379,45 +2359,69 @@ type JSONReader struct { // Note(@kataras): struct instead of optional funcs to // {"username": "makis"} // {"username": "george"} ArrayStream bool + + // Optional context cancelation of decoder when Optimize field is enabled. + // On ReadJSON method this is automatically binded to the request context. + Context stdContext.Context } type internalJSONDecoder interface { - internalBodyDecoder - DisallowUnknownFields() + Token() (json.Token, error) // gojson.Token is an alias of this, so we are ok. More() bool + DisallowUnknownFields() } -func (cfg JSONReader) getDecoder(r io.Reader, globalShouldOptimize bool) (decoder internalJSONDecoder) { - if cfg.Optimize || globalShouldOptimize { - decoder = jsoniter.ConfigCompatibleWithStandardLibrary.NewDecoder(r) +func (options JSONReader) getDecoder(r io.Reader) (internalJSONDecoder, DecodeFunc) { + var ( + decoder internalJSONDecoder + decodeFunc DecodeFunc + ) + + if options.Optimize { + dec := gojson.NewDecoder(r) + decodeFunc = dec.DecodeContext + decoder = dec } else { - decoder = json.NewDecoder(r) + dec := json.NewDecoder(r) + decodeFunc = func(_ stdContext.Context, outPtr interface{}) error { + return dec.Decode(outPtr) + } + decoder = dec } - if cfg.DisallowUnknownFields { + if options.DisallowUnknownFields { decoder.DisallowUnknownFields() } - return + return decoder, decodeFunc } // ReadJSON reads JSON from request's body and binds it to a value of any json-valid type. // // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json/main.go func (ctx *Context) ReadJSON(outPtr interface{}, opts ...JSONReader) error { - shouldOptimize := ctx.shouldOptimize() + var options JSONReader + options.Optimize = ctx.shouldOptimize() if len(opts) > 0 { - cfg := opts[0] - return ctx.decodeBody(outPtr, cfg.getDecoder(ctx.request.Body, shouldOptimize)) + options = opts[0] } - unmarshaler := json.Unmarshal - if shouldOptimize { - unmarshaler = jsoniter.Unmarshal - } + _, decodeFunc := options.getDecoder(ctx.request.Body) + return decodeFunc(ctx.request.Context(), outPtr) - return ctx.UnmarshalBody(outPtr, UnmarshalerFunc(unmarshaler)) + /* + b, err := ctx.GetBody() + if err != nil { + return err + } + + if options.Optimize { + return gojson.UnmarshalContext(ctx.request.Context(), b, outPtr) + } else { + return json.Unmarshal(b, outPtr) + } + */ } // ReadJSONStream is an alternative of ReadJSON which can reduce the memory load @@ -2431,20 +2435,14 @@ func (ctx *Context) ReadJSON(outPtr interface{}, opts ...JSONReader) error { // // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json-stream/main.go func (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSONReader) error { - var cfg JSONReader + var options JSONReader if len(opts) > 0 { - cfg = opts[0] + options = opts[0] } - // note that only the standard package supports an object - // stream of arrays (when the receiver is not an array). - if cfg.ArrayStream || !cfg.Optimize { - decoder := json.NewDecoder(ctx.request.Body) - if cfg.DisallowUnknownFields { - decoder.DisallowUnknownFields() - } - decodeFunc := decoder.Decode + decoder, decodeFunc := options.getDecoder(ctx.request.Body) + if options.ArrayStream { _, err := decoder.Token() // read open bracket. if err != nil { return err @@ -2460,11 +2458,8 @@ func (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSON return err } - dec := cfg.getDecoder(ctx.request.Body, ctx.shouldOptimize()) - decodeFunc := dec.Decode - // while the array contains values - for dec.More() { + for decoder.More() { if err := onDecode(decodeFunc); err != nil { return err } @@ -2496,6 +2491,10 @@ var ( return false } + if errors.Is(err, io.EOF) { + return true + } + if v, ok := err.(*json.SyntaxError); ok { // standard go json encoder error. return v.Offset == 0 && v.Error() == "unexpected end of JSON input" @@ -3518,6 +3517,30 @@ type JSON struct { Secure bool // if true then it prepends a "while(1);" when Go slice (to JSON Array) value. // proto.Message specific marshal options. Proto ProtoMarshalOptions + + // Optional context cancelation of encoder when Iris optimizations field is enabled. + // On JSON method this is automatically binded to the request context. + Context stdContext.Context +} + +// IsDefault reports whether this JSON options structure holds the default values. +func (j *JSON) IsDefault() bool { + return j.StreamingJSON == DefaultJSONOptions.StreamingJSON && + j.UnescapeHTML == DefaultJSONOptions.UnescapeHTML && + j.Indent == DefaultJSONOptions.Indent && + j.Prefix == DefaultJSONOptions.Prefix && + j.ASCII == DefaultJSONOptions.ASCII && + j.Secure == DefaultJSONOptions.Secure && + j.Proto == DefaultJSONOptions.Proto +} + +// GetContext returns the option's Context or the HTTP request's one. +func (j *JSON) GetContext(ctx *Context) stdContext.Context { + if j.Context == nil { + return ctx.request.Context() + } + + return j.Context } // JSONP contains the options for the JSONP (Context's) Renderer. @@ -3558,48 +3581,63 @@ var ( secureJSONPrefix = []byte("while(1);") ) -// WriteJSON marshals the given interface object and writes the JSON response to the 'writer'. -// Ignores StatusCode and StreamingJSON options. -func WriteJSON(writer io.Writer, v interface{}, options JSON, optimize bool) (int, error) { - var ( - result []byte - err error - ) - +func handleJSONResponseValue(w io.Writer, v interface{}, options JSON) (bool, int, error) { if m, ok := v.(proto.Message); ok { - result, err = options.Proto.Marshal(m) + result, err := options.Proto.Marshal(m) if err != nil { - return 0, err + return true, 0, err } - return writer.Write(result) + n, err := w.Write(result) + return true, n, err } if easyObject, ok := v.(easyjson.Marshaler); ok { jw := jwriter.Writer{NoEscapeHTML: !options.UnescapeHTML} easyObject.MarshalEasyJSON(&jw) - return jw.DumpTo(writer) + n, err := jw.DumpTo(w) + return true, n, err } - if !optimize && options.Indent == "" { + return false, 0, nil +} + +// WriteJSON marshals the given interface object and writes the JSON response to the 'writer'. +// Ignores StatusCode and StreamingJSON options. +func WriteJSON(writer io.Writer, v interface{}, options JSON, shouldOptimize bool) (int, error) { + if handled, n, err := handleJSONResponseValue(writer, v, options); handled { + return n, err + } + + var ( + result []byte + err error + ) + + if !shouldOptimize && options.Indent == "" { options.Indent = " " } if indent := options.Indent; indent != "" { - marshalIndent := json.MarshalIndent - if optimize { - marshalIndent = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent + if shouldOptimize { + // result,err = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent + result, err = gojson.MarshalIndent(v, "", indent) + } else { + result, err = json.MarshalIndent(v, "", indent) } - result, err = marshalIndent(v, "", indent) result = append(result, newLineB...) } else { - marshal := json.Marshal - if optimize { - marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal + if shouldOptimize { + // result, err = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal + if options.Context != nil { + result, err = gojson.MarshalContext(options.Context, v) + } else { + result, err = gojson.Marshal(v) + } + } else { + result, err = json.Marshal(v) } - - result, err = marshal(v) } if err != nil { @@ -3668,22 +3706,43 @@ var DefaultJSONOptions = JSON{} // If the value is a compatible `proto.Message` one // then it only uses the options.Proto settings to marshal. func (ctx *Context) JSON(v interface{}, opts ...JSON) (n int, err error) { - options := DefaultJSONOptions + ctx.ContentType(ContentJSONHeaderValue) + shouldOptimize := ctx.shouldOptimize() - if len(opts) > 0 { + optsLength := len(opts) + + if shouldOptimize && optsLength == 0 { // if no options given and optimizations are enabled. + // try handle proto or easyjson. + if handled, n, err := handleJSONResponseValue(ctx, v, DefaultJSONOptions); handled { + return n, err + } + + // as soon as possible, use the fast json marshaler with the http request context. + result, err := gojson.MarshalContext(ctx.request.Context(), v) + if err != nil { + return 0, err + } + + return ctx.Write(result) + } + + options := DefaultJSONOptions + if optsLength > 0 { options = opts[0] } - ctx.ContentType(ContentJSONHeaderValue) - if options.StreamingJSON { - if ctx.shouldOptimize() { - jsoniterConfig := jsoniter.Config{ - EscapeHTML: !options.UnescapeHTML, - IndentionStep: 4, - }.Froze() - enc := jsoniterConfig.NewEncoder(ctx.writer) - err = enc.Encode(v) + if shouldOptimize { + // jsoniterConfig := jsoniter.Config{ + // EscapeHTML: !options.UnescapeHTML, + // IndentionStep: 4, + // }.Froze() + // enc := jsoniterConfig.NewEncoder(ctx.writer) + // err = enc.Encode(v) + enc := gojson.NewEncoder(ctx.writer) + enc.SetEscapeHTML(!options.UnescapeHTML) + enc.SetIndent(options.Prefix, options.Indent) + err = enc.EncodeContext(options.GetContext(ctx), v) } else { enc := json.NewEncoder(ctx.writer) enc.SetEscapeHTML(!options.UnescapeHTML) @@ -3699,7 +3758,7 @@ func (ctx *Context) JSON(v interface{}, opts ...JSON) (n int, err error) { return ctx.writer.Written(), err } - n, err = WriteJSON(ctx.writer, v, options, ctx.shouldOptimize()) + n, err = WriteJSON(ctx.writer, v, options, shouldOptimize) if err != nil { ctx.app.Logger().Debugf("JSON: %v", err) ctx.StatusCode(http.StatusInternalServerError) @@ -3728,7 +3787,8 @@ func WriteJSONP(writer io.Writer, v interface{}, options JSONP, optimize bool) ( if indent := options.Indent; indent != "" { marshalIndent := json.MarshalIndent if optimize { - marshalIndent = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent + // marshalIndent = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent + marshalIndent = gojson.MarshalIndent } result, err := marshalIndent(v, "", indent) @@ -3741,7 +3801,8 @@ func WriteJSONP(writer io.Writer, v interface{}, options JSONP, optimize bool) ( marshal := json.Marshal if optimize { - marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal + // marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal + marshal = gojson.Marshal } result, err := marshal(v) @@ -5177,21 +5238,30 @@ func (ctx *Context) SetCookieKV(name, value string, options ...CookieOption) { // returns empty string if nothing was found. // // If you want more than the value then: -// cookie, err := ctx.Request().Cookie("name") +// cookie, err := ctx.GetRequestCookie("name") // // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic func (ctx *Context) GetCookie(name string, options ...CookieOption) string { - c, err := ctx.request.Cookie(name) + c, err := ctx.GetRequestCookie(name, options...) if err != nil { return "" } - ctx.applyCookieOptions(c, OpCookieGet, options) - value, _ := url.QueryUnescape(c.Value) return value } +// GetRequestCookie returns the request cookie including any context's cookie options (stored or given by this method). +func (ctx *Context) GetRequestCookie(name string, options ...CookieOption) (*http.Cookie, error) { + c, err := ctx.request.Cookie(name) + if err != nil { + return nil, err + } + + ctx.applyCookieOptions(c, OpCookieGet, options) + return c, nil +} + var ( // CookieExpireDelete may be set on Cookie.Expire for expiring the given cookie. CookieExpireDelete = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) diff --git a/go.mod b/go.mod index f9f168b5..393243c1 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 // retract v12.1.8 // please update to @master require ( - github.com/BurntSushi/toml v0.4.1 + github.com/BurntSushi/toml v1.0.0 github.com/CloudyKit/jet/v6 v6.1.0 github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f github.com/andybalholm/brotli v1.0.4 @@ -16,6 +16,7 @@ require ( github.com/fatih/structs v1.1.0 github.com/flosch/pongo2/v4 v4.0.2 github.com/go-redis/redis/v8 v8.11.4 + github.com/goccy/go-json v0.9.4 github.com/golang/snappy v0.0.4 github.com/google/uuid v1.3.0 github.com/iris-contrib/httpexpect/v2 v2.0.5 @@ -29,22 +30,23 @@ require ( github.com/kataras/pio v0.0.10 github.com/kataras/sitemap v0.0.5 github.com/kataras/tunnel v0.0.3 - github.com/klauspost/compress v1.13.6 + github.com/klauspost/compress v1.14.3 github.com/mailru/easyjson v0.7.7 - github.com/microcosm-cc/bluemonday v1.0.16 + github.com/microcosm-cc/bluemonday v1.0.18 github.com/russross/blackfriday/v2 v2.1.0 github.com/schollz/closestmatch v2.1.0+incompatible - github.com/tdewolff/minify/v2 v2.9.22 + github.com/shirou/gopsutil/v3 v3.22.1 + github.com/tdewolff/minify/v2 v2.10.0 github.com/vmihailenco/msgpack/v5 v5.3.5 github.com/yosssi/ace v0.0.5 go.etcd.io/bbolt v1.3.6 - golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 - golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c - golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 + golang.org/x/crypto v0.0.0-20220214200702-86341886e292 + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd + golang.org/x/sys v0.0.0-20220209214540-3681064d5158 golang.org/x/text v0.3.7 - golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 google.golang.org/protobuf v1.27.1 - gopkg.in/ini.v1 v1.66.2 + gopkg.in/ini.v1 v1.66.4 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) @@ -56,44 +58,50 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/ristretto v0.1.0 // indirect - github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de // indirect + github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/gobwas/httphead v0.1.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.1.0 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/gobwas/ws v1.0.4 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-querystring v1.1.0 // indirect + github.com/google/go-querystring v1.0.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/imkira/go-interpol v1.1.0 // indirect github.com/iris-contrib/go.uuid v2.0.0+incompatible // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mediocregopher/radix/v3 v3.8.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/mediocregopher/radix/v3 v3.6.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/nats-io/nats-server/v2 v2.6.5 // indirect - github.com/nats-io/nats.go v1.13.1-0.20211122170419-d7c1d78a50fc // indirect + github.com/nats-io/nats-server/v2 v2.7.2 // indirect + github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d // indirect github.com/nats-io/nkeys v0.3.0 // indirect github.com/nats-io/nuid v1.0.1 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/errors v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/shiyanhui/hero v0.0.2 // indirect github.com/smartystreets/goconvey v1.7.2 // indirect github.com/stretchr/testify v1.7.0 // indirect - github.com/tdewolff/parse/v2 v2.5.22 // indirect + github.com/tdewolff/parse/v2 v2.5.27 // indirect + github.com/tklauser/go-sysconf v0.3.9 // indirect + github.com/tklauser/numcpus v0.3.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect github.com/yudai/gojsondiff v1.0.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect github.com/yudai/pp v2.0.1+incompatible // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect moul.io/http2curl v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index 6da3e804..9a1c222c 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v6 v6.1.0 h1:hvO96X345XagdH1fAoBjpBYG4a1ghhL/QzalkduPuXk= @@ -24,7 +24,6 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= @@ -37,12 +36,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= -github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= @@ -58,19 +55,19 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg= github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 h1:YyrUZvJaU8Q0QsoVo+xLFBgWDTam29PKea6GYmwvSiQ= github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs= github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= -github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/goccy/go-json v0.9.4 h1:L8MLKG2mvVXiQu07qB6hmfqeSYQdOnqPot2GhsIwIaI= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -88,13 +85,12 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -139,12 +135,14 @@ github.com/kataras/tunnel v0.0.3 h1:+8eHXujPD3wLnqTbYtPGa/3/Jc+Eq+bsPwEGTeFBB00= github.com/kataras/tunnel v0.0.3/go.mod h1:VOlCoaUE5zN1buE+yAjWCkjfQ9hxGuhomKLsjei/5Zs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.3 h1:DQv1WP+iS4srNjibdnHtqu8JNWCDMluj5NzPnFJsnvk= +github.com/klauspost/compress v1.14.3/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= @@ -153,30 +151,27 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mediocregopher/radix/v3 v3.6.0 h1:L18rTxOP19e/S1d+8VW13OEKiVLwUjvskfq7BhJCjCU= github.com/mediocregopher/radix/v3 v3.6.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/mediocregopher/radix/v3 v3.8.0 h1:HI8EgkaM7WzsrFpYAkOXIgUKbjNonb2Ne7K6Le61Pmg= -github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc= -github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= +github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= +github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt/v2 v2.1.0 h1:1UbfD5g1xTdWmSeRV8bh/7u+utTiBsRtWhLl1PixZp4= -github.com/nats-io/jwt/v2 v2.1.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= -github.com/nats-io/nats-server/v2 v2.6.5 h1:VTG8gdSw4bEqMwKudOHkBLqGwNpNaJOwruj3+rquQlQ= -github.com/nats-io/nats-server/v2 v2.6.5/go.mod h1:LlMieumxNUnCloOTVFv7Wog0YnasScxARUMXVXv9/+M= +github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296 h1:vU9tpM3apjYlLLeY23zRWJ9Zktr5jp+mloR942LEOpY= +github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats-server/v2 v2.7.2 h1:+LEN8m0+jdCkiGc884WnDuxR+qj80/5arj+szKuRpRI= +github.com/nats-io/nats-server/v2 v2.7.2/go.mod h1:tckmrt0M6bVaDT3kmh9UrIq/CBOBBse+TpXQi5ldaa8= github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= -github.com/nats-io/nats.go v1.13.1-0.20211018182449-f2416a8b1483/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nats.go v1.13.1-0.20211122170419-d7c1d78a50fc h1:SHr4MUUZJ/fAC0uSm2OzWOJYsHpapmR86mpw7q1qPXU= -github.com/nats-io/nats.go v1.13.1-0.20211122170419-d7c1d78a50fc/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d h1:GRSmEJutHkdoxKsRypP575IIdoXe7Bm6yHQF6GcDBnA= +github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= @@ -197,11 +192,12 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -209,9 +205,12 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil/v3 v3.22.1 h1:33y31Q8J32+KstqPfscvFwBlNJ6xLaBy4xqBXzlYV5w= +github.com/shirou/gopsutil/v3 v3.22.1/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY= +github.com/shiyanhui/hero v0.0.2 h1:RF8fwiIeWbVsdki8LCS905pxLjCQbOz/NcKE0g1ZOJc= +github.com/shiyanhui/hero v0.0.2/go.mod h1:aBlyf5bmklQfvOmVQm5i04lIGFY7t2QYIJdqEMNGJZM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= @@ -235,13 +234,16 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tdewolff/minify/v2 v2.9.22 h1:PlmaAakaJHdMMdTTwjjsuSwIxKqWPTlvjTj6a/g/ILU= -github.com/tdewolff/minify/v2 v2.9.22/go.mod h1:dNlaFdXaIxgSXh3UFASqjTY0/xjpDkkCsYHA1NCGnmQ= -github.com/tdewolff/parse/v2 v2.5.21/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= -github.com/tdewolff/parse/v2 v2.5.22 h1:KXMHTyx4VTL6Zu9a94SULQalDMvtP5FQq10mnSfaoGs= -github.com/tdewolff/parse/v2 v2.5.22/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= +github.com/tdewolff/minify/v2 v2.10.0 h1:ovVAHUcjfGrBDf1EIvsodRUVJiZK/28mMose08B7k14= +github.com/tdewolff/minify/v2 v2.10.0/go.mod h1:6XAjcHM46pFcRE0eztigFPm0Q+Cxsw8YhEWT+rDkcZM= +github.com/tdewolff/parse/v2 v2.5.27 h1:PL3LzzXaOpmdrknnOlIeO2muIBHAwiKp6TxN1RbU5gI= +github.com/tdewolff/parse/v2 v2.5.27/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -249,9 +251,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= @@ -268,6 +269,8 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -277,9 +280,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -293,8 +296,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c h1:WtYZ93XtWSO5KlOMgPZu7hXY9WhMZpprvlm5VwvAl8c= -golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= @@ -306,29 +309,34 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -353,8 +361,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= +gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/middleware/accesslog/template.go b/middleware/accesslog/template.go index b4b9632b..ec058447 100644 --- a/middleware/accesslog/template.go +++ b/middleware/accesslog/template.go @@ -51,6 +51,32 @@ func (f *Template) SetOutput(dest io.Writer) { const defaultTmplText = "{{.Now.Format .TimeFormat}}|{{.Latency}}|{{.Code}}|{{.Method}}|{{.Path}}|{{.IP}}|{{.RequestValuesLine}}|{{.BytesReceivedLine}}|{{.BytesSentLine}}|{{.Request}}|{{.Response}}|\n" +func (f *Template) LogText(log *Log) (string, error) { + var err error + + // A template may be executed safely in parallel, although if parallel + // executions share a Writer the output may be interleaved. + // We solve that using a buffer pool, no locks when template is executing (x2 performance boost). + temp := f.ac.bufPool.Get().(*bytes.Buffer) + + if f.TmplName != "" { + err = f.Tmpl.ExecuteTemplate(temp, f.TmplName, log) + } else { + err = f.Tmpl.Execute(temp, log) + } + + if err != nil { + f.ac.bufPool.Put(temp) + return "", err + } + + text := temp.String() + temp.Reset() + f.ac.bufPool.Put(temp) + + return text, nil +} + // Format prints the logs in text/template format. func (f *Template) Format(log *Log) (bool, error) { var err error diff --git a/middleware/monitor/expvar_uint64.go b/middleware/monitor/expvar_uint64.go new file mode 100644 index 00000000..3f1a5f9f --- /dev/null +++ b/middleware/monitor/expvar_uint64.go @@ -0,0 +1,33 @@ +package monitor + +import ( + "expvar" + "strconv" + "sync/atomic" +) + +// Uint64 completes the expvar metric interface, holds an uint64 value. +type Uint64 struct { + value uint64 +} + +// Set sets v to value. +func (v *Uint64) Set(value uint64) { + atomic.StoreUint64(&v.value, value) +} + +// Value returns the underline uint64 value. +func (v *Uint64) Value() uint64 { + return atomic.LoadUint64(&v.value) +} + +// String returns the text representation of the underline uint64 value. +func (v *Uint64) String() string { + return strconv.FormatUint(atomic.LoadUint64(&v.value), 10) +} + +func newUint64(name string) *Uint64 { + v := new(Uint64) + expvar.Publish(name, v) + return v +} diff --git a/middleware/monitor/monitor.go b/middleware/monitor/monitor.go new file mode 100644 index 00000000..1527fd6b --- /dev/null +++ b/middleware/monitor/monitor.go @@ -0,0 +1,105 @@ +package monitor + +import ( + "bytes" + "fmt" + "os" + "time" + + "github.com/kataras/iris/v12/context" + + "github.com/shirou/gopsutil/v3/process" +) + +func init() { + context.SetHandlerName("iris/middleware/monitor.*", "iris.monitor") +} + +// Options holds the optional fields for the Monitor structure. +type Options struct { + // Optional process id, defaults to the current one. + PID int32 `json:"pid" yaml:"PID"` + + RefreshInterval time.Duration `json:"refresh_interval" yaml:"RefreshInterval"` + ViewRefreshInterval time.Duration `json:"view_refresh_interval" yaml:"ViewRefreshInterval"` + // If more than zero enables line animation. Defaults to zero. + ViewAnimationInterval time.Duration `json:"view_animation_interval" yaml:"ViewAnimationInterval"` + // The title of the monitor HTML document. + ViewTitle string `json:"view_title" yaml:"ViewTitle"` +} + +// Monitor tracks and renders the server's process and operating system statistics. +// +// Look its `Stats` and `View` methods. +// Initialize with the `New` package-level function. +type Monitor struct { + opts Options + Holder *StatsHolder + + viewBody []byte +} + +// New returns a new Monitor. +// Metrics stored through expvar standard package: +// - pid_cpu +// - pid_ram +// - pid_conns +// - os_cpu +// - os_ram +// - os_total_ram +// - os_load_avg +// - os_conns +// +// Check https://github.com/iris-contrib/middleware/tree/master/expmetric +// which can be integrated with datadog or other platforms. +func New(opts Options) *Monitor { + if opts.PID == 0 { + opts.PID = int32(os.Getpid()) + } + + if opts.RefreshInterval <= 0 { + opts.RefreshInterval = 2 * opts.RefreshInterval + } + + if opts.ViewRefreshInterval <= 0 { + opts.ViewRefreshInterval = opts.RefreshInterval + } + + viewRefreshIntervalBytes := []byte(fmt.Sprintf("%d", opts.ViewRefreshInterval.Milliseconds())) + viewBody := bytes.Replace(defaultViewBody, viewRefreshIntervalTmplVar, viewRefreshIntervalBytes, 1) + viewAnimationIntervalBytes := []byte(fmt.Sprintf("%d", opts.ViewAnimationInterval.Milliseconds())) + viewBody = bytes.Replace(viewBody, viewAnimationIntervalTmplVar, viewAnimationIntervalBytes, 2) + viewTitleBytes := []byte(opts.ViewTitle) + viewBody = bytes.Replace(viewBody, viewTitleTmplVar, viewTitleBytes, 2) + proc, err := process.NewProcess(opts.PID) + if err != nil { + panic(err) + } + + sh := startNewStatsHolder(proc, opts.RefreshInterval) + m := &Monitor{ + opts: opts, + Holder: sh, + viewBody: viewBody, + } + + return m +} + +// Stop terminates the retrieve stats loop for +// the process and the operating system statistics. +// No other monitor instance should be initialized after the first Stop call. +func (m *Monitor) Stop() { + m.Holder.Stop() +} + +// Stats sends the stats as json. +func (m *Monitor) Stats(ctx *context.Context) { + ctx.JSON(m.Holder.GetStats()) +} + +// View renders a default view for the stats. +func (m *Monitor) View(ctx *context.Context) { + ctx.ContentType("text/html") + ctx.Write(m.viewBody) +} diff --git a/middleware/monitor/stats.go b/middleware/monitor/stats.go new file mode 100644 index 00000000..f826dca3 --- /dev/null +++ b/middleware/monitor/stats.go @@ -0,0 +1,205 @@ +package monitor + +import ( + "expvar" + "sync" + "time" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/load" + "github.com/shirou/gopsutil/v3/mem" + "github.com/shirou/gopsutil/v3/net" + "github.com/shirou/gopsutil/v3/process" +) + +// Stats holds the process and operating system statistics values. +// +// Note that each statistic has its own expvar metric that you can use +// to render e.g. through statsd. Available values: +// * pid_cpu +// * pid_ram +// * pid_conns +// * os_cpu +// * os_ram +// * os_total_ram +// * os_load_avg +// * os_conns +type Stats struct { + PIDCPU float64 `json:"pid_cpu" yaml:"PIDCPU"` + PIDRAM uint64 `json:"pid_ram" yaml:"PIDRAM"` + PIDConns int64 `json:"pid_conns" yaml:"PIDConns"` + + OSCPU float64 `json:"os_cpu" yaml:"OSCPU"` + OSRAM uint64 `json:"os_ram" yaml:"OSRAM"` + OSTotalRAM uint64 `json:"os_total_ram" yaml:"OSTotalRAM"` + OSLoadAvg float64 `json:"os_load_avg" yaml:"OSLoadAvg"` + OSConns int64 `json:"os_conns" yaml:"OSConns"` +} + +// StatsHolder holds and refreshes the statistics. +type StatsHolder struct { + proc *process.Process + + stats *Stats + mu sync.RWMutex + + started bool + closeCh chan struct{} + errCh chan error +} + +func startNewStatsHolder(proc *process.Process, refreshInterval time.Duration) *StatsHolder { + sh := newStatsHolder(proc) + sh.start(refreshInterval) + return sh +} + +func newStatsHolder(proc *process.Process) *StatsHolder { + sh := &StatsHolder{ + proc: proc, + stats: new(Stats), + closeCh: make(chan struct{}), + errCh: make(chan error, 1), + } + + return sh +} + +// Err returns a read-only channel which may be filled with errors +// came from the refresh stats operation. +func (sh *StatsHolder) Err() <-chan error { + return sh.errCh +} + +// Stop terminates the routine retrieves the stats. +// Note that no other monitor can be initialized after Stop. +func (sh *StatsHolder) Stop() { + if !sh.started { + return + } + + sh.closeCh <- struct{}{} + sh.started = false +} + +func (sh *StatsHolder) start(refreshInterval time.Duration) { + if sh.started { + return + } + sh.started = true + + once.Do(func() { + go func() { + ticker := time.NewTicker(refreshInterval) + defer ticker.Stop() + + for { + select { + case <-sh.closeCh: + // close(sh.errCh) + return + case <-ticker.C: + err := refresh(sh.proc) + if err != nil { + // push the error to the channel and continue the execution, + // the only way to stop it is through its "Stop" method. + sh.errCh <- err + } + } + } + }() + }) +} + +var ( + once = new(sync.Once) + + metricPidCPU = expvar.NewFloat("pid_cpu") + metricPidRAM = newUint64("pid_ram") + metricPidConns = expvar.NewInt("pid_conns") + + metricOsCPU = expvar.NewFloat("os_cpu") + metricOsRAM = newUint64("os_ram") + metricOsTotalRAM = newUint64("os_total_ram") + metricOsLoadAvg = expvar.NewFloat("os_load_avg") + metricOsConns = expvar.NewInt("os_conns") +) + +// refresh updates the process and operating system statistics. +func refresh(proc *process.Process) error { + // Collect the stats. + // + // Process. + pidCPU, err := proc.CPUPercent() + if err != nil { + return err + } + + pidRAM, err := proc.MemoryInfo() + if err != nil { + return err + } + + pidConns, err := net.ConnectionsPid("tcp", proc.Pid) + if err != nil { + return err + } + + // Operating System. + osCPU, err := cpu.Percent(0, false) + if err != nil { + return err + } + + osRAM, err := mem.VirtualMemory() + if err != nil { + return err + } + + osLoadAvg, err := load.Avg() + if err != nil { + return err + } + + osConns, err := net.Connections("tcp") + if err != nil { + return err + } + + // Update the fields. + // + // Process. + metricPidCPU.Set(pidCPU / 10) + metricPidRAM.Set(pidRAM.RSS) + metricPidConns.Set(int64(len(pidConns))) + + // Operating System. + if len(osCPU) > 0 { + metricOsCPU.Set(osCPU[0]) + } + metricOsRAM.Set(osRAM.Used) + metricOsTotalRAM.Set(osRAM.Total) + metricOsLoadAvg.Set(osLoadAvg.Load1) + metricOsConns.Set(int64(len(osConns))) + + return nil +} + +// GetStats returns a copy of the latest stats available. +func (sh *StatsHolder) GetStats() Stats { + sh.mu.Lock() + statsCopy := Stats{ + PIDCPU: metricPidCPU.Value(), + PIDRAM: metricPidRAM.Value(), + PIDConns: metricPidConns.Value(), + + OSCPU: metricOsCPU.Value(), + OSRAM: metricOsRAM.Value(), + OSTotalRAM: metricOsTotalRAM.Value(), + OSLoadAvg: metricOsLoadAvg.Value(), + OSConns: metricOsConns.Value(), + } + sh.mu.Unlock() + + return statsCopy +} diff --git a/middleware/monitor/view.go b/middleware/monitor/view.go new file mode 100644 index 00000000..ad540e2d --- /dev/null +++ b/middleware/monitor/view.go @@ -0,0 +1,15 @@ +package monitor + +var ( + viewRefreshIntervalTmplVar = []byte(`{{.ViewRefreshInterval}}`) + viewAnimationIntervalTmplVar = []byte(`{{.ViewAnimationInterval}}`) + viewTitleTmplVar = []byte(`{{.ViewTitle}}`) + defaultViewBody = []byte(`