├── .gitpod.yml ├── internal └── tools │ └── tools_test.go ├── .pre-commit-config.yaml ├── AUTHORS ├── Makefile ├── example_test.go ├── SECURITY.md ├── go.mod ├── .dockerignore ├── COPYRIGHT ├── package.json ├── .all-contributorsrc ├── LICENSE-MIT ├── .editorconfig ├── .golangci.yml ├── doc.go ├── chizap.go ├── chizap_test.go ├── README.md ├── go.sum ├── depaware.txt ├── rules.mk └── LICENSE-APACHE /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: go get && go build ./... && go test ./... 3 | command: go run 4 | -------------------------------------------------------------------------------- /internal/tools/tools_test.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/tailscale/depaware" // required by rules.mk 7 | ) 8 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | - repo: git://github.com/dnephin/pre-commit-golang 2 | rev: master 3 | hooks: 4 | - id: go-fmt 5 | - id: go-vet 6 | - id: go-lint 7 | - id: go-imports 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This file lists all individuals having contributed content to the repository. 2 | # For how it is generated, see 'https://github.com/moul/rules.mk' 3 | 4 | Manfred Touron <94029+moul@users.noreply.github.com> 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOPKG ?= moul.io/chizap 2 | 3 | include rules.mk 4 | 5 | generate: 6 | GO111MODULE=off go get github.com/campoy/embedmd 7 | mkdir -p .tmp 8 | go doc -all > .tmp/godoc.txt 9 | embedmd -w README.md 10 | rm -rf .tmp 11 | .PHONY: generate 12 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package chizap_test 2 | 3 | import ( 4 | "github.com/go-chi/chi" 5 | "go.uber.org/zap" 6 | "moul.io/chizap" 7 | ) 8 | 9 | func Example() { 10 | logger := zap.NewExample() 11 | r := chi.NewRouter() 12 | r.Use(chizap.New(logger, &chizap.Opts{ 13 | WithReferer: true, 14 | WithUserAgent: true, 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting security issues 2 | 3 | I take security seriously. If you discover a security issue, 4 | please bring it to my attention right away! 5 | 6 | ## Reporting a Vulnerability 7 | 8 | Please **DO NOT** file a public issue, 9 | instead send your report privately to security@moul.io. 10 | 11 | Security reports are greatly appreciated and I will publicly thank you for it, 12 | although I keep your name confidential if you request it. 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module moul.io/chizap 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-chi/chi v1.5.4 7 | github.com/kr/text v0.2.0 // indirect 8 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e // indirect 9 | github.com/stretchr/testify v1.8.0 10 | github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 11 | go.uber.org/atomic v1.8.0 // indirect 12 | go.uber.org/multierr v1.7.0 // indirect 13 | go.uber.org/zap v1.19.1 14 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | ## 2 | ## Specific to .dockerignore 3 | ## 4 | 5 | .git/ 6 | Dockerfile 7 | contrib/ 8 | 9 | ## 10 | ## Common with .gitignore 11 | ## 12 | 13 | # Temporary files 14 | *~ 15 | *# 16 | .#* 17 | 18 | # Vendors 19 | node_modules/ 20 | vendor/ 21 | 22 | # Binaries for programs and plugins 23 | dist/ 24 | gin-bin 25 | *.exe 26 | *.exe~ 27 | *.dll 28 | *.so 29 | *.dylib 30 | 31 | # Test binary, build with `go test -c` 32 | *.test 33 | 34 | # Output of the go coverage tool, specifically when used with LiteIDE 35 | *.out 36 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright 2021 Manfred Touron and other chizap Developers. 2 | 3 | Intellectual Property Notice 4 | ---------------------------- 5 | 6 | chizap is licensed under the Apache License, Version 2.0 7 | (see LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or 8 | the MIT license (see LICENSE-MIT or http://opensource.org/licenses/MIT), 9 | at your option. 10 | 11 | Copyrights and patents in the chizaps project are retained 12 | by contributors. 13 | No copyright assignment is required to contribute to chizap. 14 | 15 | SPDX-License-Identifier: (Apache-2.0 OR MIT) 16 | 17 | SPDX usage 18 | ---------- 19 | 20 | Individual files may contain SPDX tags instead of the full license text. 21 | This enables machine processing of license information based on the SPDX 22 | License Identifiers that are available here: https://spdx.org/licenses/ 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "this project is not a node.js one, package.json is just used to define some metadata", 3 | "name": "@moul.io/chizap", 4 | "version": "0.0.1", 5 | "author": "Manfred Touron (https://manfred.life)", 6 | "contributors": [ 7 | "Manfred Touron (https://manfred.life)" 8 | ], 9 | "license": "(Apache-2.0 OR MIT)", 10 | "scripts": { 11 | "start": "chizap", 12 | "install": "make install", 13 | "test": "make test" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/moul/chizap.git" 18 | }, 19 | "bugs": "https://github.com/moul/chizap/issues", 20 | "homepage": "https://moul.io/chizap", 21 | "release": { 22 | "branches": ["master", "main"], 23 | "plugins": [ 24 | "@semantic-release/commit-analyzer", 25 | "@semantic-release/release-notes-generator", 26 | "@semantic-release/github" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "badgeTemplate": "[![All Contributors](https://img.shields.io/badge/all_contributors-<%= contributors.length %>-orange.svg)](#contributors)", 8 | "contributors": [ 9 | { 10 | "login": "moul", 11 | "name": "Manfred Touron", 12 | "avatar_url": "https://avatars1.githubusercontent.com/u/94029?v=4", 13 | "profile": "http://manfred.life", 14 | "contributions": [ 15 | "maintenance", 16 | "doc", 17 | "test", 18 | "code" 19 | ] 20 | }, 21 | { 22 | "login": "moul-bot", 23 | "name": "moul-bot", 24 | "avatar_url": "https://avatars1.githubusercontent.com/u/41326314?v=4", 25 | "profile": "https://manfred.life/moul-bot", 26 | "contributions": [ 27 | "maintenance" 28 | ] 29 | } 30 | ], 31 | "contributorsPerLine": 7, 32 | "projectName": "chizap", 33 | "projectOwner": "moul", 34 | "repoType": "github", 35 | "repoHost": "https://github.com", 36 | "skipCi": true 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Manfred Touron (manfred.life) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | indent_style = space 11 | indent_size = 4 12 | 13 | [*.mod] 14 | indent_style = tab 15 | 16 | [{Makefile,**.mk}] 17 | indent_style = tab 18 | 19 | [*.go] 20 | indent_style = tab 21 | 22 | [*.css] 23 | indent_size = 2 24 | 25 | [*.proto] 26 | indent_size = 2 27 | 28 | [*.ftl] 29 | indent_size = 2 30 | 31 | [*.toml] 32 | indent_size = 2 33 | 34 | [*.swift] 35 | indent_size = 4 36 | 37 | [*.tmpl] 38 | indent_size = 2 39 | 40 | [*.js] 41 | indent_size = 2 42 | block_comment_start = /* 43 | block_comment_end = */ 44 | 45 | [*.{html,htm}] 46 | indent_size = 2 47 | 48 | [*.bat] 49 | end_of_line = crlf 50 | 51 | [*.{yml,yaml}] 52 | indent_size = 2 53 | 54 | [*.json] 55 | indent_size = 2 56 | 57 | [.{babelrc,eslintrc,prettierrc}] 58 | indent_size = 2 59 | 60 | [{Fastfile,.buckconfig,BUCK}] 61 | indent_size = 2 62 | 63 | [*.diff] 64 | indent_size = 1 65 | 66 | [*.m] 67 | indent_size = 1 68 | indent_style = space 69 | block_comment_start = /** 70 | block_comment = * 71 | block_comment_end = */ 72 | 73 | [*.java] 74 | indent_size = 4 75 | indent_style = space 76 | block_comment_start = /** 77 | block_comment = * 78 | block_comment_end = */ 79 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | deadline: 1m 3 | tests: false 4 | skip-files: 5 | - "testing.go" 6 | - ".*\\.pb\\.go" 7 | - ".*\\.gen\\.go" 8 | 9 | linters-settings: 10 | golint: 11 | min-confidence: 0 12 | maligned: 13 | suggest-new: true 14 | goconst: 15 | min-len: 5 16 | min-occurrences: 4 17 | misspell: 18 | locale: US 19 | 20 | linters: 21 | disable-all: true 22 | enable: 23 | - asciicheck 24 | - bodyclose 25 | - deadcode 26 | - depguard 27 | - dogsled 28 | - dupl 29 | - errcheck 30 | - exhaustive 31 | - exportloopref 32 | - gci 33 | - gochecknoinits 34 | - gocognit 35 | - goconst 36 | - gocritic 37 | - gocyclo 38 | - godot 39 | - goerr113 40 | - gofmt 41 | - gofumpt 42 | - goimports 43 | - golint 44 | - gomnd 45 | - gomodguard 46 | - gosec 47 | - gosimple 48 | - govet 49 | - ineffassign 50 | - interfacer 51 | - maligned 52 | - misspell 53 | - nakedret 54 | - nestif 55 | - noctx 56 | - nolintlint 57 | - prealloc 58 | - scopelint 59 | - sqlclosecheck 60 | - staticcheck 61 | - structcheck 62 | - stylecheck 63 | - typecheck 64 | - unconvert 65 | - unparam 66 | - unused 67 | - varcheck 68 | - whitespace 69 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 Manfred Touron 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | // message from the author: 5 | // +--------------------------------------------------------------+ 6 | // | * * * ░░░░░░░░░░░░░░░░░░░░ Hello ░░░░░░░░░░░░░░░░░░░░░░░░░░| 7 | // +--------------------------------------------------------------+ 8 | // | | 9 | // | ++ ______________________________________ | 10 | // | ++++ / \ | 11 | // | ++++ | | | 12 | // | ++++++++++ | Feel free to contribute to this | | 13 | // | +++ | | project or contact me on | | 14 | // | ++ | | manfred.life if you like this | | 15 | // | + -== ==| | project! | | 16 | // | ( <*> <*> | | | 17 | // | | | /| :) | | 18 | // | | _) / | | | 19 | // | | +++ / \______________________________________/ | 20 | // | \ =+ / | 21 | // | \ + | 22 | // | |\++++++ | 23 | // | | ++++ ||// | 24 | // | ___| |___ _||/__ __| 25 | // | / --- \ \| ||| __ _ ___ __ __/ /| 26 | // |/ | | \ \ / / ' \/ _ \/ // / / | 27 | // || | | | | | /_/_/_/\___/\_,_/_/ | 28 | // +--------------------------------------------------------------+ 29 | package chizap 30 | -------------------------------------------------------------------------------- /chizap.go: -------------------------------------------------------------------------------- 1 | package chizap 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/go-chi/chi/middleware" 8 | "go.uber.org/zap" 9 | ) 10 | 11 | // Opts contains the middleware configuration. 12 | type Opts struct { 13 | // WithReferer enables logging the "Referer" HTTP header value. 14 | WithReferer bool 15 | 16 | // WithUserAgent enables logging the "User-Agent" HTTP header value. 17 | WithUserAgent bool 18 | } 19 | 20 | // New returns a logger middleware for chi, that implements the http.Handler interface. 21 | func New(logger *zap.Logger, opts *Opts) func(next http.Handler) http.Handler { 22 | if logger == nil { 23 | return func(next http.Handler) http.Handler { return next } 24 | } 25 | if opts == nil { 26 | opts = &Opts{} 27 | } 28 | return func(next http.Handler) http.Handler { 29 | fn := func(w http.ResponseWriter, r *http.Request) { 30 | ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) 31 | t1 := time.Now() 32 | defer func() { 33 | reqLogger := logger.With( 34 | zap.String("proto", r.Proto), 35 | zap.String("path", r.URL.Path), 36 | zap.String("reqId", middleware.GetReqID(r.Context())), 37 | zap.Duration("lat", time.Since(t1)), 38 | zap.Int("status", ww.Status()), 39 | zap.Int("size", ww.BytesWritten()), 40 | ) 41 | if opts.WithReferer { 42 | ref := ww.Header().Get("Referer") 43 | if ref == "" { 44 | ref = r.Header.Get("Referer") 45 | } 46 | if ref != "" { 47 | reqLogger = reqLogger.With(zap.String("ref", ref)) 48 | } 49 | } 50 | if opts.WithUserAgent { 51 | ua := ww.Header().Get("User-Agent") 52 | if ua == "" { 53 | ua = r.Header.Get("User-Agent") 54 | } 55 | if ua != "" { 56 | reqLogger = reqLogger.With(zap.String("ua", ua)) 57 | } 58 | } 59 | reqLogger.Info("Served") 60 | }() 61 | next.ServeHTTP(ww, r) 62 | } 63 | return http.HandlerFunc(fn) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /chizap_test.go: -------------------------------------------------------------------------------- 1 | package chizap_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/go-chi/chi" 10 | "github.com/stretchr/testify/require" 11 | "go.uber.org/zap" 12 | "go.uber.org/zap/zapcore" 13 | "go.uber.org/zap/zaptest/observer" 14 | "moul.io/chizap" 15 | ) 16 | 17 | func TestBasic(t *testing.T) { 18 | r := chi.NewRouter() 19 | core, logs := observer.New(zapcore.DebugLevel) 20 | logger := zap.New(core) 21 | middleware := chizap.New(logger, &chizap.Opts{}) 22 | r.Use(middleware) 23 | r.Get("/foo", func(w http.ResponseWriter, r *http.Request) { 24 | w.Header().Set("Content-Type", "text/html") 25 | w.Write([]byte("foobar")) 26 | }) 27 | ts := httptest.NewServer(r) 28 | defer ts.Close() 29 | 30 | { 31 | req, err := http.NewRequest("GET", ts.URL+"/foo", nil) 32 | require.NoError(t, err) 33 | resp, err := http.DefaultClient.Do(req) 34 | require.NoError(t, err) 35 | respBody, err := ioutil.ReadAll(resp.Body) 36 | require.NoError(t, err) 37 | resp.Body.Close() 38 | require.Equal(t, string(respBody), "foobar") 39 | allLogs := logs.All() 40 | require.Len(t, allLogs, 1) 41 | log := allLogs[0] 42 | require.Equal(t, log.Message, "Served") 43 | require.Equal(t, log.Level, zap.InfoLevel) 44 | require.NotEmpty(t, log.Time) 45 | require.Empty(t, log.LoggerName) 46 | require.False(t, log.Caller.Defined) 47 | fields := log.Context 48 | require.Len(t, fields, 6) 49 | require.Equal(t, fieldByKey(fields, "proto"), &zapcore.Field{Key: "proto", String: "HTTP/1.1", Type: zapcore.StringType}) 50 | require.Equal(t, fieldByKey(fields, "path"), &zapcore.Field{Key: "path", String: "/foo", Type: zapcore.StringType}) 51 | require.Equal(t, fieldByKey(fields, "reqId"), &zapcore.Field{Key: "reqId", String: "", Type: zapcore.StringType}) 52 | require.Equal(t, fieldByKey(fields, "status"), &zapcore.Field{Key: "status", Integer: 200, Type: zapcore.Int64Type}) 53 | require.Equal(t, fieldByKey(fields, "size"), &zapcore.Field{Key: "size", Integer: 6, Type: zapcore.Int64Type}) 54 | duration := fieldByKey(fields, "lat") 55 | require.NotZero(t, duration.Integer) 56 | duration.Integer = 0 57 | require.Equal(t, duration, &zapcore.Field{Key: "lat", Type: zapcore.DurationType}) 58 | } 59 | } 60 | 61 | func TestWithFeatures(t *testing.T) { 62 | r := chi.NewRouter() 63 | core, logs := observer.New(zapcore.DebugLevel) 64 | logger := zap.New(core) 65 | middleware := chizap.New(logger, &chizap.Opts{ 66 | WithReferer: true, 67 | WithUserAgent: true, 68 | }) 69 | r.Use(middleware) 70 | r.Get("/foo", func(w http.ResponseWriter, r *http.Request) { 71 | w.Header().Set("Content-Type", "text/html") 72 | w.Header().Set("Referer", "https://manfred.life/") 73 | w.Header().Set("User-Agent", "TestMan") 74 | w.Write([]byte("foobar")) 75 | }) 76 | ts := httptest.NewServer(r) 77 | defer ts.Close() 78 | 79 | { 80 | req, err := http.NewRequest("GET", ts.URL+"/foo", nil) 81 | require.NoError(t, err) 82 | resp, err := http.DefaultClient.Do(req) 83 | require.NoError(t, err) 84 | respBody, err := ioutil.ReadAll(resp.Body) 85 | require.NoError(t, err) 86 | resp.Body.Close() 87 | require.Equal(t, string(respBody), "foobar") 88 | allLogs := logs.All() 89 | require.Len(t, allLogs, 1) 90 | log := allLogs[0] 91 | require.Equal(t, log.Message, "Served") 92 | require.Equal(t, log.Level, zap.InfoLevel) 93 | require.NotEmpty(t, log.Time) 94 | require.Empty(t, log.LoggerName) 95 | require.False(t, log.Caller.Defined) 96 | fields := log.Context 97 | require.Len(t, fields, 8) 98 | require.Equal(t, fieldByKey(fields, "proto"), &zapcore.Field{Key: "proto", String: "HTTP/1.1", Type: zapcore.StringType}) 99 | require.Equal(t, fieldByKey(fields, "path"), &zapcore.Field{Key: "path", String: "/foo", Type: zapcore.StringType}) 100 | require.Equal(t, fieldByKey(fields, "reqId"), &zapcore.Field{Key: "reqId", String: "", Type: zapcore.StringType}) 101 | require.Equal(t, fieldByKey(fields, "status"), &zapcore.Field{Key: "status", Integer: 200, Type: zapcore.Int64Type}) 102 | require.Equal(t, fieldByKey(fields, "size"), &zapcore.Field{Key: "size", Integer: 6, Type: zapcore.Int64Type}) 103 | require.Equal(t, fieldByKey(fields, "ref"), &zapcore.Field{Key: "ref", String: "https://manfred.life/", Type: zapcore.StringType}) 104 | require.Equal(t, fieldByKey(fields, "ua"), &zapcore.Field{Key: "ua", String: "TestMan", Type: zapcore.StringType}) 105 | duration := fieldByKey(fields, "lat") 106 | require.NotZero(t, duration.Integer) 107 | duration.Integer = 0 108 | require.Equal(t, duration, &zapcore.Field{Key: "lat", Type: zapcore.DurationType}) 109 | } 110 | } 111 | 112 | func fieldByKey(fields []zapcore.Field, key string) *zapcore.Field { 113 | for _, field := range fields { 114 | if field.Key == key { 115 | return &field 116 | } 117 | } 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chizap 2 | 3 | :smile: chizap 4 | 5 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/moul.io/chizap) 6 | [![License](https://img.shields.io/badge/license-Apache--2.0%20%2F%20MIT-%2397ca00.svg)](https://github.com/moul/chizap/blob/main/COPYRIGHT) 7 | [![GitHub release](https://img.shields.io/github/release/moul/chizap.svg)](https://github.com/moul/chizap/releases) 8 | [![Docker Metrics](https://images.microbadger.com/badges/image/moul/chizap.svg)](https://microbadger.com/images/moul/chizap) 9 | [![Made by Manfred Touron](https://img.shields.io/badge/made%20by-Manfred%20Touron-blue.svg?style=flat)](https://manfred.life/) 10 | 11 | [![Go](https://github.com/moul/chizap/workflows/Go/badge.svg)](https://github.com/moul/chizap/actions?query=workflow%3AGo) 12 | [![Release](https://github.com/moul/chizap/workflows/Release/badge.svg)](https://github.com/moul/chizap/actions?query=workflow%3ARelease) 13 | [![PR](https://github.com/moul/chizap/workflows/PR/badge.svg)](https://github.com/moul/chizap/actions?query=workflow%3APR) 14 | [![GolangCI](https://golangci.com/badges/github.com/moul/chizap.svg)](https://golangci.com/r/github.com/moul/chizap) 15 | [![codecov](https://codecov.io/gh/moul/chizap/branch/main/graph/badge.svg)](https://codecov.io/gh/moul/chizap) 16 | [![Go Report Card](https://goreportcard.com/badge/moul.io/chizap)](https://goreportcard.com/report/moul.io/chizap) 17 | [![CodeFactor](https://www.codefactor.io/repository/github/moul/chizap/badge)](https://www.codefactor.io/repository/github/moul/chizap) 18 | 19 | [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/moul/chizap) 20 | 21 | ## Example 22 | 23 | [embedmd]:# (example_test.go /import\ / $) 24 | ```go 25 | import ( 26 | "github.com/go-chi/chi" 27 | "go.uber.org/zap" 28 | "moul.io/chizap" 29 | ) 30 | 31 | func Example() { 32 | logger := zap.NewExample() 33 | r := chi.NewRouter() 34 | r.Use(chizap.New(logger, &chizap.Opts{ 35 | WithReferer: true, 36 | WithUserAgent: true, 37 | })) 38 | } 39 | ``` 40 | 41 | ## Usage 42 | 43 | [embedmd]:# (.tmp/godoc.txt txt /FUNCTIONS/ $) 44 | ```txt 45 | FUNCTIONS 46 | 47 | func New(logger *zap.Logger, opts *Opts) func(next http.Handler) http.Handler 48 | New returns a logger middleware for chi, that implements the http.Handler 49 | interface. 50 | 51 | 52 | TYPES 53 | 54 | type Opts struct { 55 | // WithReferer enables logging the "Referer" HTTP header value. 56 | WithReferer bool 57 | 58 | // WithUserAgent enables logging the "User-Agent" HTTP header value. 59 | WithUserAgent bool 60 | } 61 | Opts contains the middleware configuration. 62 | 63 | ``` 64 | 65 | ## Install 66 | 67 | ### Using go 68 | 69 | ```sh 70 | go get moul.io/chizap 71 | ``` 72 | 73 | ### Releases 74 | 75 | See https://github.com/moul/chizap/releases 76 | 77 | ## Contribute 78 | 79 | ![Contribute <3](https://raw.githubusercontent.com/moul/moul/main/contribute.gif) 80 | 81 | I really welcome contributions. 82 | Your input is the most precious material. 83 | I'm well aware of that and I thank you in advance. 84 | Everyone is encouraged to look at what they can do on their own scale; 85 | no effort is too small. 86 | 87 | Everything on contribution is sum up here: [CONTRIBUTING.md](./.github/CONTRIBUTING.md) 88 | 89 | ### Dev helpers 90 | 91 | Pre-commit script for install: https://pre-commit.com 92 | 93 | ### Contributors ✨ 94 | 95 | 96 | [![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg)](#contributors) 97 | 98 | 99 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |

Manfred Touron

🚧 📖 ⚠️ 💻

moul-bot

🚧
110 | 111 | 112 | 113 | 114 | 115 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) 116 | specification. Contributions of any kind welcome! 117 | 118 | ### Stargazers over time 119 | 120 | [![Stargazers over time](https://starchart.cc/moul/chizap.svg)](https://starchart.cc/moul/chizap) 121 | 122 | ## License 123 | 124 | © 2021 [Manfred Touron](https://manfred.life) 125 | 126 | Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) 127 | ([`LICENSE-APACHE`](LICENSE-APACHE)) or the [MIT license](https://opensource.org/licenses/MIT) 128 | ([`LICENSE-MIT`](LICENSE-MIT)), at your option. 129 | See the [`COPYRIGHT`](COPYRIGHT) file for more details. 130 | 131 | `SPDX-License-Identifier: (Apache-2.0 OR MIT)` 132 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 2 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs= 8 | github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= 9 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 10 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 11 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 12 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 13 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 14 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 15 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 16 | github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= 17 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= 18 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 19 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 20 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 24 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 25 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 26 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 27 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 28 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 29 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 30 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 31 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 32 | github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 h1:34icjjmqJ2HPjrSuJYEkdZ+0ItmGQAQ75cRHIiftIyE= 33 | github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= 34 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 35 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 36 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 37 | go.uber.org/atomic v1.8.0 h1:CUhrE4N1rqSE6FM9ecihEjRkLQu8cDfgDyoOs83mEY4= 38 | go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 39 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= 40 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 41 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 42 | go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= 43 | go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= 44 | go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= 45 | go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= 46 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 47 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 48 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 49 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 50 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 51 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 52 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 53 | golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= 54 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 55 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 56 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 57 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 58 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 59 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 60 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 61 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 62 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 63 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 64 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 65 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 66 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= 69 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 70 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 71 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 72 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 73 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 74 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 75 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 76 | golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 77 | golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= 78 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 79 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 80 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 81 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 82 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 83 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 84 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 85 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 86 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 87 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 88 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 89 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 90 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 91 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 92 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 93 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 94 | -------------------------------------------------------------------------------- /depaware.txt: -------------------------------------------------------------------------------- 1 | moul.io/chizap dependencies: (generated by github.com/tailscale/depaware) 2 | 3 | go.uber.org/atomic from go.uber.org/multierr+ 4 | go.uber.org/multierr from go.uber.org/zap+ 5 | go.uber.org/zap from moul.io/zapconfig 6 | go.uber.org/zap/buffer from go.uber.org/zap/internal/bufferpool+ 7 | go.uber.org/zap/internal/bufferpool from go.uber.org/zap+ 8 | go.uber.org/zap/internal/color from go.uber.org/zap/zapcore 9 | go.uber.org/zap/internal/exit from go.uber.org/zap/zapcore 10 | go.uber.org/zap/zapcore from go.uber.org/zap+ 11 | moul.io/banner from moul.io/motd 12 | moul.io/motd from moul.io/chizap 13 | moul.io/srand from moul.io/chizap 14 | moul.io/u from moul.io/chizap 15 | moul.io/zapconfig from moul.io/chizap 16 | golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305 17 | golang.org/x/crypto/chacha20poly1305 from crypto/tls 18 | golang.org/x/crypto/cryptobyte from crypto/ecdsa+ 19 | golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+ 20 | golang.org/x/crypto/curve25519 from crypto/tls 21 | golang.org/x/crypto/hkdf from crypto/tls 22 | golang.org/x/crypto/poly1305 from golang.org/x/crypto/chacha20poly1305 23 | golang.org/x/net/dns/dnsmessage from net 24 | golang.org/x/net/http/httpguts from net/http 25 | golang.org/x/net/http/httpproxy from net/http 26 | golang.org/x/net/http2/hpack from net/http 27 | golang.org/x/net/idna from golang.org/x/net/http/httpguts+ 28 | D golang.org/x/net/route from net 29 | golang.org/x/sys/cpu from golang.org/x/crypto/chacha20poly1305 30 | golang.org/x/text/secure/bidirule from golang.org/x/net/idna 31 | golang.org/x/text/transform from golang.org/x/text/secure/bidirule+ 32 | golang.org/x/text/unicode/bidi from golang.org/x/net/idna+ 33 | golang.org/x/text/unicode/norm from golang.org/x/net/idna 34 | archive/zip from moul.io/u 35 | bufio from archive/zip+ 36 | bytes from bufio+ 37 | compress/flate from archive/zip+ 38 | compress/gzip from net/http 39 | container/list from crypto/tls+ 40 | context from crypto/tls+ 41 | crypto from crypto/ecdsa+ 42 | crypto/aes from crypto/ecdsa+ 43 | crypto/cipher from crypto/aes+ 44 | crypto/des from crypto/tls+ 45 | crypto/dsa from crypto/x509 46 | crypto/ecdsa from crypto/tls+ 47 | crypto/ed25519 from crypto/tls+ 48 | crypto/elliptic from crypto/ecdsa+ 49 | crypto/hmac from crypto/tls+ 50 | crypto/md5 from crypto/tls+ 51 | crypto/rand from crypto/ed25519+ 52 | crypto/rc4 from crypto/tls 53 | crypto/rsa from crypto/tls+ 54 | crypto/sha1 from crypto/tls+ 55 | crypto/sha256 from crypto/tls+ 56 | crypto/sha512 from crypto/ecdsa+ 57 | crypto/subtle from crypto/aes+ 58 | crypto/tls from net/http+ 59 | crypto/x509 from crypto/tls 60 | crypto/x509/pkix from crypto/x509 61 | encoding from encoding/json 62 | encoding/asn1 from crypto/x509+ 63 | encoding/base64 from encoding/json+ 64 | encoding/binary from archive/zip+ 65 | encoding/hex from crypto/x509+ 66 | encoding/json from go.uber.org/atomic+ 67 | encoding/pem from crypto/tls+ 68 | errors from archive/zip+ 69 | flag from go.uber.org/zap 70 | fmt from compress/flate+ 71 | hash from archive/zip+ 72 | hash/crc32 from archive/zip+ 73 | io from archive/zip+ 74 | io/fs from archive/zip+ 75 | io/ioutil from crypto/x509+ 76 | log from go.uber.org/zap+ 77 | math from compress/flate+ 78 | math/big from crypto/dsa+ 79 | math/bits from compress/flate+ 80 | math/rand from math/big+ 81 | mime from mime/multipart+ 82 | mime/multipart from net/http 83 | mime/quotedprintable from mime/multipart 84 | net from crypto/tls+ 85 | net/http from go.uber.org/zap 86 | net/http/httptrace from net/http 87 | net/http/internal from net/http 88 | net/textproto from golang.org/x/net/http/httpguts+ 89 | net/url from crypto/x509+ 90 | os from archive/zip+ 91 | os/exec from moul.io/u 92 | os/signal from moul.io/u 93 | os/user from moul.io/u 94 | path from archive/zip+ 95 | path/filepath from crypto/x509+ 96 | reflect from crypto/x509+ 97 | sort from compress/flate+ 98 | strconv from compress/flate+ 99 | strings from archive/zip+ 100 | sync from archive/zip+ 101 | sync/atomic from context+ 102 | syscall from crypto/rand+ 103 | time from archive/zip+ 104 | unicode from bytes+ 105 | unicode/utf16 from encoding/asn1+ 106 | unicode/utf8 from archive/zip+ 107 | -------------------------------------------------------------------------------- /rules.mk: -------------------------------------------------------------------------------- 1 | # +--------------------------------------------------------------+ 2 | # | * * * moul.io/rules.mk | 3 | # +--------------------------------------------------------------+ 4 | # | | 5 | # | ++ ______________________________________ | 6 | # | ++++ / \ | 7 | # | ++++ | | | 8 | # | ++++++++++ | https://moul.io/rules.mk is a set | | 9 | # | +++ | | of common Makefile rules that can | | 10 | # | ++ | | be configured from the Makefile | | 11 | # | + -== ==| | or with environment variables. | | 12 | # | ( <*> <*> | | | 13 | # | | | /| Manfred Touron | | 14 | # | | _) / | manfred.life | | 15 | # | | +++ / \______________________________________/ | 16 | # | \ =+ / | 17 | # | \ + | 18 | # | |\++++++ | 19 | # | | ++++ ||// | 20 | # | ___| |___ _||/__ __| 21 | # | / --- \ \| ||| __ _ ___ __ __/ /| 22 | # |/ | | \ \ / / ' \/ _ \/ // / / | 23 | # || | | | | | /_/_/_/\___/\_,_/_/ | 24 | # +--------------------------------------------------------------+ 25 | 26 | .PHONY: _default_entrypoint 27 | _default_entrypoint: help 28 | 29 | ## 30 | ## Common helpers 31 | ## 32 | 33 | rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) 34 | check-program = $(foreach exec,$(1),$(if $(shell PATH="$(PATH)" which $(exec)),,$(error "No $(exec) in PATH"))) 35 | my-filter-out = $(foreach v,$(2),$(if $(findstring $(1),$(v)),,$(v))) 36 | novendor = $(call my-filter-out,vendor/,$(1)) 37 | 38 | ## 39 | ## rules.mk 40 | ## 41 | ifneq ($(wildcard rules.mk),) 42 | .PHONY: rulesmk.bumpdeps 43 | rulesmk.bumpdeps: 44 | wget -O rules.mk https://raw.githubusercontent.com/moul/rules.mk/master/rules.mk 45 | BUMPDEPS_STEPS += rulesmk.bumpdeps 46 | endif 47 | 48 | ## 49 | ## Maintainer 50 | ## 51 | 52 | ifneq ($(wildcard .git/HEAD),) 53 | .PHONY: generate.authors 54 | generate.authors: AUTHORS 55 | AUTHORS: .git/ 56 | echo "# This file lists all individuals having contributed content to the repository." > AUTHORS 57 | echo "# For how it is generated, see 'https://github.com/moul/rules.mk'" >> AUTHORS 58 | echo >> AUTHORS 59 | git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf >> AUTHORS 60 | GENERATE_STEPS += generate.authors 61 | endif 62 | 63 | ## 64 | ## Golang 65 | ## 66 | 67 | ifndef GOPKG 68 | ifneq ($(wildcard go.mod),) 69 | GOPKG = $(shell sed '/module/!d;s/^omdule\ //' go.mod) 70 | endif 71 | endif 72 | ifdef GOPKG 73 | GO ?= go 74 | GOPATH ?= $(HOME)/go 75 | GO_INSTALL_OPTS ?= 76 | GO_TEST_OPTS ?= -test.timeout=30s 77 | GOMOD_DIRS ?= $(sort $(call novendor,$(dir $(call rwildcard,*,*/go.mod go.mod)))) 78 | GOCOVERAGE_FILE ?= ./coverage.txt 79 | GOTESTJSON_FILE ?= ./go-test.json 80 | GOBUILDLOG_FILE ?= ./go-build.log 81 | GOINSTALLLOG_FILE ?= ./go-install.log 82 | 83 | ifdef GOBINS 84 | .PHONY: go.install 85 | go.install: 86 | ifeq ($(CI),true) 87 | @rm -f /tmp/goinstall.log 88 | @set -e; for dir in $(GOBINS); do ( set -xe; \ 89 | cd $$dir; \ 90 | $(GO) install -v $(GO_INSTALL_OPTS) .; \ 91 | ); done 2>&1 | tee $(GOINSTALLLOG_FILE) 92 | 93 | else 94 | @set -e; for dir in $(GOBINS); do ( set -xe; \ 95 | cd $$dir; \ 96 | $(GO) install $(GO_INSTALL_OPTS) .; \ 97 | ); done 98 | endif 99 | INSTALL_STEPS += go.install 100 | 101 | .PHONY: go.release 102 | go.release: 103 | $(call check-program, goreleaser) 104 | goreleaser --snapshot --skip-publish --rm-dist 105 | @echo -n "Do you want to release? [y/N] " && read ans && \ 106 | if [ $${ans:-N} = y ]; then set -xe; goreleaser --rm-dist; fi 107 | RELEASE_STEPS += go.release 108 | endif 109 | 110 | .PHONY: go.unittest 111 | go.unittest: 112 | ifeq ($(CI),true) 113 | @echo "mode: atomic" > /tmp/gocoverage 114 | @rm -f $(GOTESTJSON_FILE) 115 | @set -e; for dir in $(GOMOD_DIRS); do (set -e; (set -euf pipefail; \ 116 | cd $$dir; \ 117 | (($(GO) test ./... $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race -json && touch $@.ok) | tee -a $(GOTESTJSON_FILE) 3>&1 1>&2 2>&3 | tee -a $(GOBUILDLOG_FILE); \ 118 | ); \ 119 | rm $@.ok 2>/dev/null || exit 1; \ 120 | if [ -f /tmp/profile.out ]; then \ 121 | cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \ 122 | rm -f /tmp/profile.out; \ 123 | fi)); done 124 | @mv /tmp/gocoverage $(GOCOVERAGE_FILE) 125 | else 126 | @echo "mode: atomic" > /tmp/gocoverage 127 | @set -e; for dir in $(GOMOD_DIRS); do (set -e; (set -xe; \ 128 | cd $$dir; \ 129 | $(GO) test ./... $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race); \ 130 | if [ -f /tmp/profile.out ]; then \ 131 | cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \ 132 | rm -f /tmp/profile.out; \ 133 | fi); done 134 | @mv /tmp/gocoverage $(GOCOVERAGE_FILE) 135 | endif 136 | 137 | .PHONY: go.checkdoc 138 | go.checkdoc: 139 | go doc $(first $(GOMOD_DIRS)) 140 | 141 | .PHONY: go.coverfunc 142 | go.coverfunc: go.unittest 143 | go tool cover -func=$(GOCOVERAGE_FILE) | grep -v .pb.go: | grep -v .pb.gw.go: 144 | 145 | .PHONY: go.lint 146 | go.lint: 147 | @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \ 148 | cd $$dir; \ 149 | golangci-lint run --verbose ./...; \ 150 | ); done 151 | 152 | .PHONY: go.tidy 153 | go.tidy: 154 | @# tidy dirs with go.mod files 155 | @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \ 156 | cd $$dir; \ 157 | $(GO) mod tidy; \ 158 | ); done 159 | 160 | .PHONY: go.depaware-update 161 | go.depaware-update: go.tidy 162 | @# gen depaware for bins 163 | @set -e; for dir in $(GOBINS); do ( set -xe; \ 164 | cd $$dir; \ 165 | $(GO) run github.com/tailscale/depaware --update .; \ 166 | ); done 167 | @# tidy unused depaware deps if not in a tools_test.go file 168 | @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \ 169 | cd $$dir; \ 170 | $(GO) mod tidy; \ 171 | ); done 172 | 173 | .PHONY: go.depaware-check 174 | go.depaware-check: go.tidy 175 | @# gen depaware for bins 176 | @set -e; for dir in $(GOBINS); do ( set -xe; \ 177 | cd $$dir; \ 178 | $(GO) run github.com/tailscale/depaware --check .; \ 179 | ); done 180 | 181 | 182 | .PHONY: go.build 183 | go.build: 184 | @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \ 185 | cd $$dir; \ 186 | $(GO) build ./...; \ 187 | ); done 188 | 189 | .PHONY: go.bump-deps 190 | go.bumpdeps: 191 | @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \ 192 | cd $$dir; \ 193 | $(GO) get -u ./...; \ 194 | ); done 195 | 196 | .PHONY: go.fmt 197 | go.fmt: 198 | @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \ 199 | cd $$dir; \ 200 | $(GO) run golang.org/x/tools/cmd/goimports -w `go list -f '{{.Dir}}' ./...` \ 201 | ); done 202 | 203 | VERIFY_STEPS += go.depaware-check 204 | BUILD_STEPS += go.build 205 | BUMPDEPS_STEPS += go.bumpdeps go.depaware-update 206 | TIDY_STEPS += go.tidy 207 | LINT_STEPS += go.lint 208 | UNITTEST_STEPS += go.unittest 209 | FMT_STEPS += go.fmt 210 | 211 | # FIXME: disabled, because currently slow 212 | # new rule that is manually run sometimes, i.e. `make pre-release` or `make maintenance`. 213 | # alternative: run it each time the go.mod is changed 214 | #GENERATE_STEPS += go.depaware-update 215 | endif 216 | 217 | ## 218 | ## Tool 219 | ## 220 | 221 | ifneq ($(wildcard ./tool/lint/Makefile),) 222 | .PHONY: tool.lint 223 | tool.lint: 224 | cd tool/lint; make 225 | LINT_STEPS += tool.lint 226 | endif 227 | 228 | ## 229 | ## Gitattributes 230 | ## 231 | 232 | ifneq ($(wildcard .gitattributes),) 233 | .PHONY: _linguist-ignored 234 | _linguist-kept: 235 | @git check-attr linguist-vendored $(shell git check-attr linguist-generated $(shell find . -type f | grep -v .git/) | grep unspecified | cut -d: -f1) | grep unspecified | cut -d: -f1 | sort 236 | 237 | .PHONY: _linguist-kept 238 | _linguist-ignored: 239 | @git check-attr linguist-vendored linguist-ignored `find . -not -path './.git/*' -type f` | grep '\ set$$' | cut -d: -f1 | sort -u 240 | endif 241 | 242 | ## 243 | ## Node 244 | ## 245 | 246 | ifndef NPM_PACKAGES 247 | ifneq ($(wildcard package.json),) 248 | NPM_PACKAGES = . 249 | endif 250 | endif 251 | ifdef NPM_PACKAGES 252 | .PHONY: npm.publish 253 | npm.publish: 254 | @echo -n "Do you want to npm publish? [y/N] " && read ans && \ 255 | @if [ $${ans:-N} = y ]; then \ 256 | set -e; for dir in $(NPM_PACKAGES); do ( set -xe; \ 257 | cd $$dir; \ 258 | npm publish --access=public; \ 259 | ); done; \ 260 | fi 261 | RELEASE_STEPS += npm.publish 262 | endif 263 | 264 | ## 265 | ## Docker 266 | ## 267 | 268 | docker_build = docker build \ 269 | --build-arg VCS_REF=`git rev-parse --short HEAD` \ 270 | --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ 271 | --build-arg VERSION=`git describe --tags --always` \ 272 | -t "$2" -f "$1" "$(dir $1)" 273 | 274 | ifndef DOCKERFILE_PATH 275 | DOCKERFILE_PATH = ./Dockerfile 276 | endif 277 | ifndef DOCKER_IMAGE 278 | ifneq ($(wildcard Dockerfile),) 279 | DOCKER_IMAGE = $(notdir $(PWD)) 280 | endif 281 | endif 282 | ifdef DOCKER_IMAGE 283 | ifneq ($(DOCKER_IMAGE),none) 284 | .PHONY: docker.build 285 | docker.build: 286 | $(call check-program, docker) 287 | $(call docker_build,$(DOCKERFILE_PATH),$(DOCKER_IMAGE)) 288 | 289 | BUILD_STEPS += docker.build 290 | endif 291 | endif 292 | 293 | ## 294 | ## Common 295 | ## 296 | 297 | TEST_STEPS += $(UNITTEST_STEPS) 298 | TEST_STEPS += $(LINT_STEPS) 299 | TEST_STEPS += $(TIDY_STEPS) 300 | 301 | ifneq ($(strip $(TEST_STEPS)),) 302 | .PHONY: test 303 | test: $(PRE_TEST_STEPS) $(TEST_STEPS) 304 | endif 305 | 306 | ifdef INSTALL_STEPS 307 | .PHONY: install 308 | install: $(PRE_INSTALL_STEPS) $(INSTALL_STEPS) 309 | endif 310 | 311 | ifdef UNITTEST_STEPS 312 | .PHONY: unittest 313 | unittest: $(PRE_UNITTEST_STEPS) $(UNITTEST_STEPS) 314 | endif 315 | 316 | ifdef LINT_STEPS 317 | .PHONY: lint 318 | lint: $(PRE_LINT_STEPS) $(FMT_STEPS) $(LINT_STEPS) 319 | endif 320 | 321 | ifdef TIDY_STEPS 322 | .PHONY: tidy 323 | tidy: $(PRE_TIDY_STEPS) $(TIDY_STEPS) 324 | endif 325 | 326 | ifdef BUILD_STEPS 327 | .PHONY: build 328 | build: $(PRE_BUILD_STEPS) $(BUILD_STEPS) 329 | endif 330 | 331 | ifdef VERIFY_STEPS 332 | .PHONY: verify 333 | verify: $(PRE_VERIFY_STEPS) $(VERIFY_STEPS) 334 | endif 335 | 336 | ifdef RELEASE_STEPS 337 | .PHONY: release 338 | release: $(PRE_RELEASE_STEPS) $(RELEASE_STEPS) 339 | endif 340 | 341 | ifdef BUMPDEPS_STEPS 342 | .PHONY: bumpdeps 343 | bumpdeps: $(PRE_BUMDEPS_STEPS) $(BUMPDEPS_STEPS) 344 | endif 345 | 346 | ifdef FMT_STEPS 347 | .PHONY: fmt 348 | fmt: $(PRE_FMT_STEPS) $(FMT_STEPS) 349 | endif 350 | 351 | ifdef GENERATE_STEPS 352 | .PHONY: generate 353 | generate: $(PRE_GENERATE_STEPS) $(GENERATE_STEPS) 354 | endif 355 | 356 | .PHONY: help 357 | help:: 358 | @echo "General commands:" 359 | @[ "$(BUILD_STEPS)" != "" ] && echo " build" || true 360 | @[ "$(BUMPDEPS_STEPS)" != "" ] && echo " bumpdeps" || true 361 | @[ "$(FMT_STEPS)" != "" ] && echo " fmt" || true 362 | @[ "$(GENERATE_STEPS)" != "" ] && echo " generate" || true 363 | @[ "$(INSTALL_STEPS)" != "" ] && echo " install" || true 364 | @[ "$(LINT_STEPS)" != "" ] && echo " lint" || true 365 | @[ "$(RELEASE_STEPS)" != "" ] && echo " release" || true 366 | @[ "$(TEST_STEPS)" != "" ] && echo " test" || true 367 | @[ "$(TIDY_STEPS)" != "" ] && echo " tidy" || true 368 | @[ "$(UNITTEST_STEPS)" != "" ] && echo " unittest" || true 369 | @[ "$(VERIFY_STEPS)" != "" ] && echo " verify" || true 370 | @# FIXME: list other commands 371 | 372 | print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true 373 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 Manfred Touron (manfred.life) 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------